Sleeper Stack — 2026-05-25 Maintenance Run

State of the cognition stack after the 18-brief SLEEPER_TODO run that resolved four of the five rough edges and added eleven new connectors


On 2026-05-25 the Sleeper cognition stack underwent a single-day maintenance run that worked through all 18 briefs in ~/SLEEPER_TODO/SLEEPER_TODO.md. The run resolved four of the five rough edges enumerated in sleeper-cognition-stack, hardened the perimeter on the fifth, and added eleven new connectors and capabilities across ~/sfl-hook/, ~/chat/, ~/thoughts/, and ~/github/write/. The shape of the stack is materially different now: each capture surface has a typed home for its output, the parallel pollers have collapsed into a push pipeline with the cron as fallback, and the wiki schema is enforced at autocommit time so silent producer-consumer drift is caught before it ships.

sfl-hook stopped running Claude as a subprocess

The 153-line server.js that powered chat replies in SFL no longer spawns claude -p ... --dangerously-skip-permissions per request. It now calls the Anthropic SDK directly (claude-sonnet-4-6) with a cached system block. loadConfig fails closed on missing SFL_WEBHOOK_SECRET or ANTHROPIC_API_KEY (no more silent fail-open behaviour). The nginx location /sfl-hook block is restricted to Cloudflare egress IPs via cloudflare-allow.conf + deny all, with an exact-match carve-out for /sfl-hook/health so the iOS ConnectionTester can reach it from carrier IPs. Per-request structured logs land in ~/sfl-hook/data/requests.jsonl (10 MB rotation × 5 files); "did that reply land?" is tail -1. This was the closest the stack had to an arbitrary-code-execution path; it is gone.

Articles push to thoughts-sync; the cron is a safety net

sleeper-articles now fires POST /sync/ingest (fire-and-forget, Bearer-authed via ARTICLES_API_KEY) at every status='ready' transition — once for each of the three extractor paths (article, video, X post). thoughts-sync exposes the receiver on loopback port 3013 with GET /sync/health reporting last_push_at / last_cron_at / ideas_known. The original 5-minute cron stays, so a transient webhook failure self-heals on the next tick. Latency from "SFL bookmark accepted" to "raw/ written" dropped from up to 5 minutes to seconds. The two-parallel-SFL-pollers seam (sleeper-cognition-stack §1 fix #2) is resolved in the smallest-fix direction the survey recommended.

The wiki schema has an enforcement layer

~/thoughts/sync/check-schema.cjs validates wiki-index.json against the canonical wiki-index.schema.md on every autocommit cycle, immediately after build-wiki-index.cjs runs. The build chain — needs-refold.cjsbuild-wiki-index.cjscheck-schema.cjsgit commit — refuses to commit when any required key is missing, any non-nullable field is null, or health.dead_links is non-empty. The producer also emits health.field_coverage to surface structural drift in the index itself. A deliberate-drift test (renaming summary to summary_text in the producer) was verified to block the commit; restoring the field unblocked it. The wiring contract is now explicit in the schema: chat-backend reads wiki-index.json off disk; remote consumers (iOS, Claude.ai) go through wiki-mcp at port 3007. Do not blur the two.

The fold-queue is observable

~/thoughts/sync/needs-refold.cjs runs first in the autocommit chain and emits sync/needs-refold.json listing every raw/ (and ~/Dropbox/raw/) entry that is either new to the .processed-raw-files ledger or whose recorded sha differs from the file's current sha. The build then injects health.refold_queue: {new, changed, missing} into wiki-index.json so the depth of unprocessed work is visible at-a-glance via the same retrieval path the chat backend already reads. At run-end the queue was 204 entries deep — confirming the survey's ~195-entry estimate from sleeper-cognition-stack §1 fix #5.

Each capture surface now has a typed downstream home

The biggest behavioural shift. Three new auto-routing pipelines turn captured material into structured rows the rest of the stack can find:

Three killswitches default-off so Petter trusts the new behaviour before flipping each on. All three were live-verified end-to-end before commit, then test artifacts cleaned up.

The chat agent has new MCP, new search, new memory affordances

The daily brief is real

POST /briefs/generate + GET /briefs/today + a 06:30 node-cron gather signal from articles (last-24h starred-unread), tasks (todo aging >7d), memory (rows touched last 24h), the wiki-index (articles whose mtime moved), and the local messages table, then compose a 6-section Markdown brief via claude-sonnet-4-6 with persona prefix + cached system block. Empty sections are omitted. After generation it fires POST sleeper-tasks /notify for the silent-refresh APNs push that iOS already handles. The iOS side currently still mocks; swapping to a real client is a one-file change on the Mac.

Write gained two AI surfaces beyond /feedback

Both go through the shared read-only corpus.ts module so write-sync remains the single writer.

What did not get done

YouTube transcripts (G11) have full wiring — youtube-transcript.ts wrapper, poller integration, backfill script — but the upstream youtube-transcript npm library currently returns "transcript disabled" for nearly every video because YouTube tightened anti-scraping. The graceful fallback path is verified; transcripts will start landing without further changes once the upstream stabilises or a working library arrives. The SFL repo audit (G4) filed three follow-up sfl meta entries against the dormant sfl project for Petter to triage — the verdict ("neglected, production stable, no near-term roadmap") sits in ~/github/sfl/STATUS.md with a one-line edit caveat. The iOS halves of G6 (real brief client) and G9 (citation chips) remain blocked on the Xcode toolchain. SFL-meta dispatch from G18 is recognised by the classifier but not auto-dispatched in v1 — coordination with the SFL Worker's auth is deferred; sfl meta add still works.

How the run was scoped

The 18 briefs were accepted in their priority order and worked through one at a time, each with build / live-verify / commit / push before moving on. D0 in the run's decision log collapsed the standard per-goal Frame/Plan/Starter/Critic ceremony because the SLEEPER_TODO briefs are themselves pre-framed (Goal / Why / Context / Concrete proposal / Files / Success criteria); the trade-off — losing the adversarial cross-check — was offset by close-gate code review and live verification on every shipped goal. Total commits across phareim/sfl-hook, phareim/sleeper, phareim/sleeper-chat, phareim/sfl, phareim/write, and phareim/thoughts: 27. All pushed to origin.

— SOURCES
— GRAPH
— 3 RELATED