Release notes for offplan.online's operating system (the os/ repo + everything it deploys).
Each release groups a meaningful chunk of work into user-facing features — what it is, why it matters, how to use it, and a concrete example. Releases are dated; the newest is at the top. Internal-only churn (refactors, test scaffolding) doesn't get its own entry — only changes an operator or stakeholder would care about.
This file is the single source of truth. It renders to preview.offplan.online/changelog and the last few entries surface as a "What's New" widget on the preview homepage.
Status: dogfood. The release-notes system (capture-at-/plan, refine-at-/handoff, advisory commit hook, /audit verify) is being ratified — see
plans/releases-and-understanding-hooks.md. This file's first entry (2026-05-12) was written by hand as the proof-of-concept.
2026-05-12 — Notion-sync Worker live · preview timeline · daemon fixes · hook resilience
A heavy session (15 commits, 22 files, +4.6k/−1.4k) — the headline ships are the Notion sync Worker going live, a chronological timeline view on the preview site, and the idle-reflection daemon finally working end-to-end after two bugs were caught and fixed.
🚀 The offplan-notion-sync Cloudflare Worker is live
What it is. A Cloudflare Worker that pulls the current state of main from GitHub on an hourly cron and mirrors changed files (sessions, plans, workstreams, ADRs, learnings) into the OFFPLAN ONLINE Notion workspace. TypeScript port of the old scripts/sync_to_notion_oneshot.py. Writes an audit log to R2 and a per-file manifest to KV.
Why it matters. Notion stays in sync with the canonical repo automatically — no more "did someone remember to run the sync script?" The repo is canonical; Notion is a downstream mirror that's now always fresh (within an hour).
How to use it.
- It runs itself — hourly cron, apply mode. Nothing to do.
- Manual trigger:
curl -H "Authorization: Bearer $SYNC_AUTH_TOKEN" https://offplan-notion-sync.roman-b16.workers.dev/sync— dry-run by default; add?dry=0to actually write. Optional?kind=sessionand?limit=Nto scope it. - Health check:
curl https://offplan-notion-sync.roman-b16.workers.dev/healthz→{"ok": true} - Deploy a new version:
bash scripts/deploy-worker.sh --apply(the runbook walks every step; seeworkers/notion-sync/README.md).
Example.
$ curl -s -H "Authorization: Bearer $SYNC_AUTH_TOKEN" \
"https://offplan-notion-sync.roman-b16.workers.dev/sync?limit=40"
{
"mode": "dry-run",
"fileCount": 40,
"counts": { "new": 1, "updated": 17, "skipped": 22, "error": 0 },
...
}
Known limitation. Free-plan Workers cap at 50 subrequests per invocation; a full sync of all ~126 files exceeds that (≈56 files error with "Too many subrequests"). Workaround for now: the ?limit=N param keeps a manual run under the cap. Permanent fix is pending a decision — Workers Paid plan ($5/mo, cap → 1000) vs in-code chunking. Deferred until it bites during regular use.
📅 Preview site: Timeline view
What it is. A new View: [Grid] [Timeline] toggle on preview.offplan.online. Grid is the existing card layout. Timeline arranges the same artefacts on a vertical rail, newest at top, grouped by day with HH:MM timestamps and status-tinted dots — so you can visually scan what was done when.
Why it matters. The grid answers "what kinds of docs exist"; the timeline answers "what's the recent activity / what's stalled". Different mental model, same data, one click apart.
How to use it. Open preview.offplan.online, click Timeline in the filter bar. The choice persists in the URL (#view=timeline) and composes with the existing Kind/Status filters — e.g. #kind=plan&status=approved&view=timeline deep-links to "approved plans, on a timeline".
Example. Share https://preview.offplan.online/#view=timeline with Sergei → he lands directly on the chronological view.
🔧 Idle-reflection daemon: two bugs fixed, now working end-to-end
What it is. The launchd idle-reflection daemon (shipped RT-260511-06) had two bugs that meant it never actually produced a reflection: (1) the day-budget counter saturated on the first run because no-op skips (claude_missing, kill-switch) were counted as successful reflections, and (2) the claude CLI invocation passed a --system flag that the current CLI rejects (error: unknown option).
Why it matters. The daemon is supposed to write per-session reflections to memory + session files automatically. Both bugs are now fixed — soft-skips return rc=2 (no counter increment, no backoff sentinel), and the flag is --system-prompt. 799 stale backoff sentinels were purged so the next cron tick fires cleanly.
How to use it. Nothing — it's automatic. To watch it: tail -f ~/.claude/reflection_log/dispatcher.log after a 10-min tick. Kill-switch if it gets noisy: set "enabled": false in ~/.claude/reflection_config.json.
Test coverage. 341/341 green (was 334) — +7 regression tests across the two fixes, including a forward-compat guard that cross-checks every claude CLI flag in _run_llm against claude --help output so the next API shift fails loudly in CI rather than silently in production.
🛡️ Hook resilience: worktree-drift defense + auto-push self-heal
What it is. Two long-standing footguns hardened: (1) parallel worktree-isolated agents could silently leave the main worktree's HEAD on a feature branch — there's now a post-checkout hook that warns loudly, plus scripts/check_main_branch.sh to detect + auto-recover, wired into /resume Step 0. (2) core.hooksPath periodically reset itself, silently disabling the auto-push hook — there's now scripts/install_hooks.sh that re-normalises it + dual-installs to .git/hooks/, also run at every /resume.
Why it matters. Two classes of "why didn't my commit push / why is HEAD on a weird branch" mystery are now self-healing. The drift one had been worked around defensively for weeks; the root cause is closed.
How to use it. Automatic — /resume runs both checks at session start. Manual recovery if drift bites mid-session: bash scripts/check_main_branch.sh --recover (safely merges any orphan commits into main, refuses on a dirty tree).
🔒 Security: wrangler 3 → 4 (closes 4 npm vulns)
What it is. Bumped wrangler 3.114.17 → 4.90.0 in workers/notion-sync/, which closes 4 npm-audit vulnerabilities (3 moderate in esbuild, 1 high in undici — all transitive deps with no fix available in the 3.x line).
Why it matters. npm audit is now clean. Zero config or TypeScript changes were needed — wrangler 4 was a clean lift for this Worker.
How to use it. Already in source. The deployed Worker still runs the version from before the bump; redeploy whenever convenient (bash scripts/deploy-worker.sh --apply) to land it on production — dry-run is clean, all secrets remain bound.
✅ Preview site: 2026-05-11 fix cluster fully closed
What it is. The preview-site-fixes workstream (PF1–PF5) is now done: Cloudflare email-obfuscation toggled off (verified — no more mangled mailto links), empty <meta name="summary"> populated, "How it evolved" section gets a graceful per-type empty state, sibling-page links switched to absolute paths, and PF5 documented (CF Pages 308-strips .html — expected, not a bug).
Why it matters. The crawl-surfaced issues from the 2026-05-11 audit are resolved; the preview site renders cleanly.
Operator notes (not user-facing — context for future-you)
SYNC_AUTH_TOKENwas re-pushed mid-session: the initial deploycat-piped the whole.sync-auth-token.localfile (5 comment lines + value) as the secret, so/sync401-ed until fixed. README + runbook now document the value-only extraction pattern..env.examplegained a documentedGITHUB_TOKENentry (the fine-grained PAT the Worker uses to read the repo).- Memory: added the
act-don't-askfeedback entry + a second worktree-drift failure mode (Bash tool CWD shifting into an agent worktree after completion).