Wiki State Machines

Every pipeline in the wiki is one machine: capture, judge, write, publish. The mechanical steps have executors and run. The judgment step has none, so it stalls, and the health checks stayed green because they timed the mechanical steps instead of counting the queue.

The Machine

Each lifecycle below is an instance of this machine. Only the nouns change.

From Event To Executor
(none) sync captured launchd com.kevinliu.wiki-intake, 06:40 daily
captured index indexed launchd, same run
indexed harvest and absorb judged the kevin-wiki-weekly-harvest scheduled session, Mondays 09:00 (since 2026-11-03; before, a person had to start it)
indexed bulk review stamp listed a one-off batch run
listed real absorb judged none
judged edit owner page written the same session
written push committed upstream Kevin approves each push
committed upstream build and deploy published Vercel; prebuild runs the privacy tests

If a transition is not in the table, it does not exist. Until 2026-11-03 two rows had no executor, and both queues grew without limit. The bulk stamp is gone; the judgment step now runs weekly.

Why The Checks Were Green

  • npm run status reported each step's last-run time. A sync can run every day while nothing is absorbed.
  • The bookmark coverage audit counted a generated ledger page and a July transfer snapshot as coverage. That made 2,103 of 2,243 bookmarks look covered.
  • Automation files without a lifecycle field were parsed as an off-schema legacy state, and status skipped that state entirely.
  • brain/loops.json declares each loop ready by hand. Nothing measures it, and nothing has written the file since 2026-08-10.

One fix covers every case: a check must count the queue between states from raw records. A timestamp, a generated listing or a hand-written status field does not count. Since 2026-11-03, scripts/check-freshness.ts counts the bookmark and conversation queues and names automations that declare no lifecycle.

Lifecycles

X bookmarks

ft sync writes raw/x-bookmarks/bookmarks.jsonl. Enrichment adds links, threads and media. Absorption means a topic page cites the post. Source: npm run audit:x-bookmarks, 2026-11-03

State Count, 2026-11-03
Synced (last sync 2026-10-26) 2,243
Absorbed into a topic page 1,378
Listed only in the reference ledger 726
Missing from every page 139

Absorption by sync month: July 897 of 1,763, August 32 of 204, September 9 of 186, October 1 of 90. Sync kept running after July; absorption almost stopped. The reference ledger marks 701 rows reviewed with one stock sentence, which recorded a state without doing the work. The audit now counts the ledger as a report, not coverage.

Agent conversations

The daily intake indexes Codex, Cursor and Claude transcripts in place. It copies no bytes. The weekly cross-project-learning harvest is the step that turns conversations into owner-page edits. Source: .brain/sessions/index.json; automations/cross-project-learning.md

State Count, 2026-11-03
Traces indexed (conversations, subagents, workflows) 30,466
Conversations 685
Changed since the last harvest (2026-10-05) 680

The harvest declared a cron trigger but runner: manual, so its executor was a person remembering; since 2026-11-03 a scheduled session runs it every Monday. npm run brain -- replay sessions is a side path. It is deterministic and private, but it emits about one candidate signal per message (11,897 from 10 conversations), and nothing reads them. Its pending count also subtracts from all traces, not from conversations.

Automations

The V2 contract allows proposed, active, paused and retired. A definition without a lifecycle field predates the contract and is parsed as legacy, a fifth state nothing schedules or watches. Source: automations/_schema.md; scripts/lib/automation-contract.ts

State Definitions Recent runs
active 2 wiki-intake daily; cross-project-learning last 2026-10-05
paused 7 source-compile paused 2026-10-26
retired 2026-11-03 14 none in 90 days; five never ran; moved to ~/repos/_retired/automations-legacy-20261103/
legacy, on demand 2 self-heal last 2026-08-12

state.json also records runs for three automations with no definition file: mac-app-harvest, fmhy-catalog-harvest and repository-evidence-capture. npm run status still names any definition that declares no lifecycle and stops running.

Privacy

Every surface asks one gate, ui/src/lib/privacy-gate.mjs: runtime pages, listings, search, the sitemap, llms.txt, the build bundle and the OKF export. First match wins:

  1. frontmatter private: true makes a page private.
  2. A sensitive-subject tag (investor, fundraising, recruiting, job search, interview) makes it private.
  3. A reviewed public brain object, a listed public slug, or the README of a public category makes it public.
  4. A proprietary tag, a private slug token, a listed private slug or a private category makes it private.
  5. Anything else is public.

Before 2026-11-03 the rule lived in three copies, and step 2 did not exist. A page in a public folder with investor tags was public. ui/scripts/test-privacy-gating.ts pins both sides: sensitive pages stay private, and look-alike tags such as career on an essay stay public.

Gating a page was not enough, because other pages name it. A link prints its target's title, and for people and investor pages the slug is the name. On 2026-11-03, 445 public pages linked private ones, and the generated _index listed every page by title. The second rule, ui/src/lib/private-links.mjs, masks every reference to a page the viewer cannot open. The reference renders as "private page", with no link. Kevin and 23 public figures (the policy's publicNames) keep their names as plain text. It covers article HTML and markdown, metadata, related pages and backlinks, the /api/wiki JSON, the Wikibot's context, the search index and the OKF export.

Access itself has two parts:

  • Unlock cookie. It is an HMAC-signed, expiring token. Before 2026-11-03 it was the constant 1, so anyone could set it.
  • Private app routes. /me, /brief, /system/sessions and /audit are gated in ui/src/proxy.ts, before any page renders. A layout-only gate still let Next serialize the page.

The public brain graph shows only public objects, and it names nothing else by id.

Deploy

Commit, Kevin's push approval, the Vercel build, then live. Builds fail closed: prebuild runs the privacy tests before bundling. Site deploys failed from 2026-10-30 to 2026-11-02 because .vercelignore dropped files the build needed, and nothing reported the failures until a session looked. Source: .state/RESUME-overhaul.md

Pages

A page carries updated:, but nothing re-reads it when its sources change. Kevin's user-model pages kept July facts until a session rewrote them on 2026-10-30. Page freshness has no executor except the session that happens to open the page.

Bookmarks, after the lane

The 2026-11-03 bookmarks lane synced 61 new posts and absorbed the backlog. Coverage rose from 1,378 of 2,243 to 2,285 of 2,304, with 0 missing. 19 were left out on privacy or funding grounds. The reference ledger shrank from 732 rows to 38 parked sources. Source: npm run audit:x-bookmarks, 2026-11-03

Rules For Any New Pipeline

  1. Write the transition table first. A transition that is not in the table does not exist.
  2. Name an executor for every transition. "Manual" is a stall unless a schedule starts the session.
  3. Have status count each queue from raw records. Never infer progress from a timestamp, a generated listing or a declared field.
  4. Make a missing state field an error. Never give it a default state.

Open Fixes

  • Done 2026-11-03: the judgment step has a weekly scheduled session, and the 14 dead automations are retired.
  • Derive loop status in brain/loops.json from measured queues, or delete the hand-written field.

Timeline

  • 2026-11-03 | Kevin approved the open fixes: a weekly scheduled harvest session now runs the judgment step, the 14 dead automations are retired, and the two funding pages left the wiki. Source: Kevin, 2026-11-03
  • 2026-11-03 | Added the link mask, signed unlock cookies, the proxy gate and the scrubbed brain graph to Privacy; recorded the bookmarks lane's result. Source: commits de5560217, 2d264edf3, 73dbb8261, c7b655d19
  • 2026-11-03 | Mapped the machine and its six lifecycles from raw counts. Status now counts the bookmark and conversation queues and names undeclared automations; the bookmark audit stopped counting the reference ledger and the transfer snapshot; the privacy rule became one shared gate. Source: npm run status, 2026-11-03