Work log: March 30, 2026
What shipped today
Today was a focused data-safety sprint, closing five issues that all traced back to PRODUCT.md principle #4: “Safe by default. Never destroy data.”
The highest-impact fix was atomic writes in save_post. Previously, a crash or power loss mid-write could corrupt a user’s markdown file — the content directory is the source of truth and there’s no backup. Now save_post writes to a temp file, fsyncs, then does a POSIX rename, which is atomic on all our target filesystems. create_post intentionally skips this (no pre-existing data to lose).
The second major fix was content dirty tracking. The smart quotes feature (Q key) modifies post.content in memory, but the dirty detection only checked frontmatter changes. This meant content-only changes weren’t saved by Ctrl+S, didn’t trigger the quit warning, and couldn’t be reverted with u. Added original_content to the Post struct and unified all dirty checks through is_dirty().
The third data-safety fix was single-post editor reload. Opening a post in $EDITOR previously triggered a full scan_posts reload on return, silently replacing all in-memory posts and discarding unsaved edits on other posts. Now only the edited post is reloaded via read_post, preserving all other in-progress work.
On the infrastructure side, eliminated flaky CLI tests caused by unsafe { std::env::set_var("HOME") } — which is undefined behavior in Rust 1.78+ when tests run in parallel. Replaced with Config::load_from(config_dir) injection. Tests went from 3-5 random failures per run to 62/62 passing consistently across 10 consecutive runs.
Also improved the browser-open error message to include the actual error and URL, so users can troubleshoot or copy-paste the URL manually.
Completed
- #121 — Include error details in browser-open failure status message
- #120 — Use atomic writes in save_post to prevent data loss on crash
- #119 — Fix flaky CLI tests caused by shared HOME env var mutation
- #123 — Content changes (smart quotes) not tracked as dirty
- #124 — Editor reload silently discards unsaved changes to other posts
Release progress
- v1.0.3: 1 open / 1 closed (inline content editing via external editor is the remaining feature)
- First external user: 0 open / 0 closed (empty milestone)
Carry-over
- #125 (ready-for-prep): Manual refresh (
rkey) silently discards unsaved changes — same class of bug as #124 but for the explicit refresh path. Needs a warning-before-discard pattern. - #122 (ready-for-prep): Add test coverage for TUI keyboard handlers and save operations. Needs decomposition into specific test scenarios.
Risks
- The
rkey (manual refresh) still does a fullscan_poststhat discards unsaved changes. Now tracked as #125.
Flags and watch-outs
is_dirty()changed from&selfmethod to associated functionApp::is_dirty(post)to work in both&selfand iterator contexts. Any new code calling it needs to use the associated function syntax.
Next session
- Prep and implement #125 — add dirty-check warning before
rkey refresh, following the quit confirmation pattern. - Prep #122 — decompose TUI test coverage into specific, implementable sub-issues.
- Run /scout — today’s fixes introduced new patterns (single-post reload,
original_content) worth scanning for consistency.
Why customer tools are organized wrong
This article reveals a fundamental flaw in how customer support tools are designed—organizing by interaction type instead of by customer—and explains why this fragmentation wastes time and obscures the full picture you need to help users effectively.
Infrastructure shapes thought
The tools you build determine what kinds of thinking become possible. On infrastructure, friction, and building deliberately for thought rather than just throughput.
Server-side dashboard architecture: Why moving data fetching off the browser changes everything
How choosing server-side rendering solved security, CORS, and credential management problems I didn't know I had.
The work of being available now
A book on AI, judgment, and staying human at work.
The practice of work in progress
Practical essays on how work actually gets done.
Your design philosophy is already written
Builders who work across multiple projects leave fingerprints everywhere. The same mind solves the same problem differently in every domain — and usually doesn't notice. You need someone to read it back to you.
The day nothing satisfying happened
The most productive day in an organization's life usually looks like nothing happened. No launches, no features, no announcements. Just people quietly making the existing work more honest.
Your AI agent is probably not an agent
The word 'agent' has become meaningless. Everyone from chatbot vendors to autonomous system builders uses it. We've been here before — with self-driving cars — and it didn't end well.