REFERENCEObservify docs
Every route this file serves, and how to read the think-like-fable kit atlas it hosts under /reports/. Generated from the live route table — not hand-maintained prose.
Overview
Observify is one Python stdlib file (observify.py, no dependencies) serving an
always-on dashboard over every live agentic session on this machine, plus the read-only infra panes (wolfplex Pi, Plex,
Holt), a static game-build showcase, the think-like-fable kit-report atlas, and — as of this build — the
~/.agent_sessions export store and this reference.
run python3 observify.py [--port 8787] [--window 30] [--exclude <session-id>]
gate python3 observify.py --selftest — the build-failing invariant gate; exit 0 = green.
compile check python3 -m py_compile observify.py
Sessions render SERVER-SIDE on first load (the HTML already contains real data), then the browser polls the matching
/api/... endpoint every few seconds and re-renders in place — the same card markup either way.
Cost figures carry a ⚠ until Wolf verifies the rate table; unverified numbers are never presented as fact.
?raw=1 (or true/yes) on any HTML route suppresses Observify's injected
auto-refresh badge, so a raw/byte-for-byte fetch of a page is never rewritten.
Live dashboard
The landing page. One card per live session across every runtime (Claude Code, Codex, Grok, Antigravity), plus a pinned Pi/Holt observation bar and the game-build card. Server-rendered, then polled.
GET/
The SSR live-sessions dashboard.
| param | kind | meaning |
|---|
minutes | int, optional | override the active-session window in minutes (default is the server’s --window, currently 30); sessions idle longer than this drop off the grid |
reach it the ◈ Observify brand link in every masthead
GET/api/sessions
JSON: {sessions, totals, window_min, pi, processes} — powers the poll loop behind /. A session whose transcript went quiet past the window but whose Claude Code process is still open stays in the list, carrying open: true and idle_seconds.
| param | kind | meaning |
|---|
minutes | int, optional | same window override as / |
reach it fetched automatically by / every few seconds; not linked directly
GET/healthz
Liveness probe: {"ok": true}.
Session detail
One session, drilled into: a paginated event stream ("book"), metrics & trends, a subagent/shell flow map, and a complete-session Kit Journey displayed reverse-chronological/newest first, with linked Atlas descriptions and transcript evidence; its subagent/shell tree remains collapsed below. Internal scoring still follows causal oldest-to-newest order. Codex sessions get two more tabs: Instructions and Implementation. All sub-tabs are click-only — none of them read a URL hash.
GET/session/<id>
The full detail page: Stream / Metrics & trends / Flow / Kit tabs (+ Instructions / Implementation for Codex).
reach it click any card on /
GET/api/session/<id>
Incremental event JSON that feeds the live Stream tab.
| param | kind | meaning |
|---|
since | int, optional | cursor — return only events after this index (0 = from the start) |
window | string, optional | "main" (default) or a subagent window id from this response’s windows field |
implementation_since | int, optional | cursor for the Codex Implementation tab’s incremental events |
GET/api/session/<id>/analytics
Chart data (cost/token/turn trends) for the Metrics & trends tab.
GET/api/session/<id>/kit
Normalized complete-session Kit Journey events: tools, files, skills, lessons, apparatus, delegation, verified Atlas links, state, and bounded transcript evidence. The human view is reverse-chronological/newest first; scoring consumes causal oldest-to-newest order. Pass the returned revision as ?revision=… to receive a tiny unchanged marker when no grounded dependency moved.
GET/api/session/<id>/orchestration
The subagent + background-shell tree retained beneath the Kit Journey.
GET/api/session/<id>/atlas
One Codex instruction/atlas entry’s content, for the Instructions tab.
| param | kind | meaning |
|---|
layer | string | which instruction layer to read |
path | string | the entry’s path within that layer |
GET/api/session/<id>/shell/<shell_id>
Cached output for one background shell.
GET/api/session/<id>/process/<pid>
One authorized background OS process’s detail + output tail.
POST/api/session/<id>/process/<pid>/stop
Stop that process — TOCTOU-guarded: refuses if the PID’s argv identity changed since it was authorized.
GET/api/session/<id>/inbox
Pending and delivered interjection messages for this session.
POST/api/session/<id>/inbox/dismiss
Dismiss one delivered inbox message.
| param | kind | meaning |
|---|
for_ts | string, JSON body | the message’s timestamp being dismissed |
POST/api/session/<id>/interject
Queue a text message into a live session’s inbox (file-write only; never touches the process).
| param | kind | meaning |
|---|
text | string, JSON body | the interjection text, capped at 4000 chars |
Infra · Pi / Plex / Holt
Read-only observation of wolfplex (the Raspberry Pi 5), its Plex server, and the Holt graduation/orchestration run — every value arrives over one cached, read-only SSH batch per tier; Observify never opens a live SSH session from inside an HTTP handler.
GET/pi
wolfplex Pi detail console. Tabs: orchestration (default) / software / services / hardware / plex / hailo.
| param | kind | meaning |
|---|
#panel | hash anchor, optional | e.g. /pi#hardware, /pi#software — read once via location.hash on load to pick the initial tab |
reach it the pinned Pi card in the obs-bar on /; cross-links from /holt and /reports/
GET/api/pi/detail
JSON {html, pending, updated} fragment, refreshed every 15s behind /pi.
GET/api/pi/software
Cache-only JSON inventory behind the Software tab: exhaustive installed packages, applications, services, models, containers/images when Docker access permits, curated operational stack, Holt security toolkit, source freshness, and explicit collection gaps. The HTTP handler never waits for SSH.
| param | kind | meaning |
|---|
page | int, default 1 | 1-indexed result page, clamped to the filtered page count |
per_page | int, default 50, max 100 | rows per API page; the Software UI always requests exactly 50 |
q | string, optional | case-insensitive substring search over normalized software metadata |
category | allowlisted string, default all | filter by the categories returned in the payload |
ecosystem | allowlisted string, default all | filter by the ecosystems returned in the payload |
state | allowlisted string, default all | filter by literal active / failed / loaded / idle / unknown and related states |
sort | name|version|category|ecosystem|state, default name | deterministic whole-result sort |
dir | asc|desc, default asc | sort direction |
GET/pi/plex
Exhaustive Plex server detail. Nested tabs: libraries / activity / pass / users / server — click-only, no hash support.
reach it the Open deep Plex route link on /pi
GET/api/pi/plex
JSON fragment behind /pi/plex.
GET/holt
Holt graduation/orchestration dashboard (wolfplex :8899). Tabs: education / terminus / corpus / kit atlas / commands / questions.
| param | kind | meaning |
|---|
#panel | hash anchor, optional | e.g. /holt#commands, /holt#questions — read once via location.hash on load to pick the initial tab, matching /pi's pattern |
reach it the Holt dashboard link on /pi and the obs-bar
GET/api/holt/detail
JSON {html, pending, updated} fragment behind /holt, refreshed every 15s.
| param | kind | meaning |
|---|
fresh | 1|true|yes, optional | synchronous cache-bust — re-reads live+holt over SSH right now instead of trusting the ≤180s passive TTL; used by the refresh badge's manual button and this page's own first load |
GET/api/holt/questions
Server-paginated JSON over every taught question in qa_bank.jsonl since the start of Holt's education. Never loads the 98MB source whole; embeddings are stripped remotely and never appear in the response.
| param | kind | meaning |
|---|
page | int, default 1 | 1-indexed page number, clamped to the actual page count |
per | int, default 50, max 200 | rows per page |
phase | all|undergrad|grad|unlabeled, default all | undergrad/grad come from matching against curriculum.json/grad_curriculum.json; unmatched questions are honestly 'unlabeled', never guessed |
q | string, optional | case-insensitive substring search over the question text |
sort | line|ts|phase|course|q, default line | sorts the WHOLE filtered set server-side, not just the current page |
dir | asc|desc, default asc | sort direction |
Game build
A static showcase for the "I Drink Your Milkshake" Godot build — scene tree, pipeline, school path, render gallery. No live polling.
GET/game
The game-build page.
reach it the 🥤 Game Build card on /
GET/game-assets/<name>
One static asset (image) referenced by that page.
Kit reports
The think-like-fable kit atlas, served two ways: the hand-authored static cover at /reports/, and a dynamic, Observify-rendered index of every file on disk at /reports/browse.
GET/reports/
Serves reports/index.html verbatim — the hand-authored think-like-fable cover page.
reach it the ◈ Kit Report nav link
GET/reports/browse
Observify’s own kit-health index: mirror-sync / scar-ledger / build-health / entabeni-state stat tiles, then a card grid of every file directly under reports/ (HTML, JSON, Markdown, text — and anything else on disk there, including build scripts and hidden state files).
reach it the open viewer flow, or link to it directly
GET/reports/<name>
Open one report file.
| param | kind | meaning |
|---|
raw | 1 / true / yes, optional | .json/.md/.markdown/.txt normally render inside Observify’s own viewer chrome (JSON gets syntax highlighting, Markdown gets prose rendering) — ?raw=1 forces the raw bytes instead. .html files (and any other extension) always serve raw, with a best-guess content type. |
reach it cards on /reports/browse
Agent sessions store
The view added in this build. Reads /Users/wolf/.agent_sessions/*.json directly — one record per finished agentic run, kept current by session_export.py --watch. Fully server-rendered like / and /reports/browse; there is no separate JSON API.
GET/sessions
Every session record, newest capture_time first: executor, model, session_name, token totals (+ prompt/completion breakdown), duration, human capture time, and content (snippet, expands to full past 240 chars). Grouped stat tiles by executor, plus a client-side executor filter (no query params, no reload). The 3 seed test fixtures (claude_test_01, codex_test_02, grok_test_03) render de-emphasized with a TEST FIXTURE pill — shown, never hidden or crashed on.
reach it the ◈ Agent Sessions nav link
Capture log
CAPTURE DATA, not another work session: the observer/router’s own log of what it captured and how it routed it, read live from ~/.observer-watch/journal.jsonl + state.json + heartbeat. This is deliberately distinct from Agent sessions store above — that view is WORK sessions; this one is the capture ENGINE watching them. See the Watch apparatus section below for what actually produces this data.
GET/capture
The Capture Log page: a CAPTURE SUMMARY (router liveness, captured vs prefiltered, synthetic vs real, by-verdict, by-brain with p50/p95 latency, cost-of-observing, last-capture-age, RECONCILE against state.json) plus the paginated, sortable per-event ledger.
| param | kind | meaning |
|---|
page | int, default 1 | 1-indexed page number, clamped to the actual page count |
per | int, default 50, max 200 | rows per page |
sort | ts|source|verdict|brain|latency|cost|synthetic, default ts | sort column — ts+desc (newest-first) is the cheap offset-indexed path; any other combination fully parses the event set once |
dir | asc|desc, default desc | sort direction |
reach it the ◈ Capture nav link
GET/api/capture
JSON {summary, log} — the exact same numbers the HTML page renders, for programmatic/verification access.
| param | kind | meaning |
|---|
page | int, default 1 | same as /capture |
per | int, default 50, max 200 | same as /capture |
sort | string, default ts | same as /capture |
dir | asc|desc, default desc | same as /capture |
Memory pipeline
The machine’s RAM as a kinetic mission console: four strata (GPU-MAPPED / ACTIVE RAM / COMPRESSED+SWAP / EVICTED), one glowing contact per live session tree, fed by the observer fleet’s 5-minute memory ledger. Ceiling lines show the ARMED advisory values mirrored from ~/.observer-watch/ceiling-checker.py (alert-only — nothing on this page can kill or throttle anything). A timeline scrubber replays every recorded cycle; LIVE mode re-polls the JSON every 30s.
GET/memory
The console page. Server-renders the newest cycle as a static strata board, then the canvas engine animates cycle-to-cycle interpolation, births/evictions, WARN pulses, PANIC strobes + swap-descent trails, the swap-pressure gauge, and the aggregate rail. Hover/tap a contact for its full RSS/footprint history sparkline. Honors prefers-reduced-motion.
reach it the ◈ Observability → Memory nav link
GET/api/memory.json
JSON {available, generated_at, machine, ceilings, cycles, panics} — bounded parse of ~/.observer-watch/memory-ledger.tsv (newest 400 cycles; the system row’s overloaded columns are unpacked server-side) plus memory-panics.tsv (absent file = honestly zero alerts). Memoized on file signatures, so the 30s LIVE poll is nearly free.
Watch apparatus — the observer’s senses and brain
The always-on observation layer that FEEDS this dashboard and wakes a human (or a Claude
session) only when warranted. Everything lives in ~/.observer-watch/, runs detached (survives every window
close), and costs zero tokens at idle: the brain is the local gpt-oss:20b via ollama.
Senses — one watcher per machine × runtime, each tailing where that runtime’s activity lands, emitting
events to w-<name>.out feed files:
• claude (this Mac) — every Claude Code transcript under ~/.claude/projects/: human steers, outcomes, errors
• codex (this Mac) — codex rollouts under ~/.codex/sessions/
• grok (this Mac) — the ~/.grok session store (sqlite, read-only polling)
• agy (this Mac) — ~/.gemini/antigravity-cli/history.jsonl
• inbox — this dashboard’s interjection channel to the observer
• pi-claude — Claude transcripts on the Pi (quiet by design: Holt runs natively there)
Prefilter: a feed line becomes an EVENT only if it matches l123-pos.txt (one ERE per line,
case-insensitive: caps, gate failures, interjections, human-steer + anger register) and clears l123-neg.txt
(benign-context exclusions). The filter files are the canonical copies — edit them, never inline regexes.
Brain: observer-router.py routes each event STATELESSLY through local gpt-oss:20b
(free, ~5k tokens/event) → verdict IGNORE / LOG / PUSH / WAKE. Confidence < 0.6 escalates to ONE
haiku call; total failure PUSHES the raw event — a dead brain must never look like a quiet night. Repeat-defect
counters upgrade any 3rd occurrence to PUSH. Native workloads no transcript watcher can see (Holt/grad on the Pi,
the Mac B relay build, kit drift) are polled every 300s as synthetic events through the same pipe.
Outputs: journal.jsonl (every event + verdict), state.json (counters),
PUSHES.log + a macOS notification per PUSH, wake-<ts>.md per WAKE (the prepared
briefing a fresh Claude session picks up), heartbeat (liveness).
Re-entry: /continue observer VERIFIES this apparatus is alive and reads the journal since last
seen — it does not rebuild the watch inside an expensive long-lived window (L149).
This page
Generated from the live route table in make_handler().do_GET/do_POST every time it is requested — not hand-maintained prose that can drift from the code.
GET/docs
This reference.
reach it the ◈ Docs nav link
Kit atlas guide — think-like-fable
/reports/ opens on the think-like-fable cover: "one method, four kits"
— frozen July 18, 2026, 148 scars at that freeze. It is the production orchestration kit that runs Observify’s
own build fleet, reviewed here rather than hand-narrated.
⬢ CANONICALThe God Kit
The master, at ~/Projects/Kit/think-like-fable — 96 files, 148 L-scars, 12 principles, 16 commands, the narrative canon, NotebookLM sources, two sub-kits. Single-writer: the harvester allocates L-ids, Wolf greenlights, ship-kit.sh mirrors it everywhere (L34-guarded). Truth accumulates here first.
open god-kit.html →⬡ EDITION · v2 ZIPThe Orchestration Kit
The deployable subset — regenerated from canonical by build-kit-v2.sh on every ship, never hand-edited, so it cannot drift. Kernel + skills + tools + templates (78 files): what a fresh engagement drops in to boot a fleet with /begin.
open orchestration-kit.html →⬠ FIRST DERIVED INSTANCEThe Entabeni Kit
On Mac B at ~/Desktop/EntabeniRepos/think-like-entabeni — derived for the Entabeni engagement. Own id-space (25 EL-scars, zero L-ids; EL→L promotion is deliberate), a 4-command subset, 6 engagement profiles grounding each repo, small-window mode pinned. Proof the method generalizes.
open entabeni-kit.html →⬔ SECOND DERIVED INSTANCEThe Terminus Kit
Local at ~/Desktop/Completely Sorted/Snorkel/terminus/think-like-terminus — derived for the Snorkel review workspace. Own id-space (12 TL-scars, zero L-ids), the 4-command subset (/begin, /continue, /dogfood, /dogfood-it), no fleet apparatus — one unit at a time, human-gated. De-mirrored from /ship; watched via /drift.
open terminus-kit.html →
The other atlas pages (each one click from the cover’s nav):
GET/reports/kit-atlas.html
Kit Atlas — the file-by-file map of every kit.
GET/reports/scar-codex.html
Scar Codex — the full L-scar lesson ledger (148 at freeze; check /reports/scars.json for the live count).
GET/reports/kit-drift.html
Kit Drift — mirror and derived-kit drift status; what /drift adjudicates.
GET/reports/terminus-findings.html
Terminus Review — Findings from the Terminus engagement review.
How to read it: start at the cover (/reports/)
for the four-kit map and the scar-ledger tick strip; open Kit Atlas for the file-by-file breakdown of any one
kit; use /reports/browse when you want the raw/live state Observify renders itself — drift.json,
scars.json, files.json — instead of the hand-authored narrative; and check Kit
Drift whenever a mirror looks behind, since that page (and /drift) is the adjudicator, not this one.