Never modifies an existing note. It only creates today's file when that
file is absent, so the blast radius is one new file and re-running is a
no-op.
Two bugs that only a real run on the server could surface. tempfile
creates 0600 and os.replace preserves it, so notes would have landed
private in a tree where everything is 0644 and both sftpgo and syncthing
read; the previous note's mode is now carried over rather than hardcoded.
And --dry-run fell into a degenerate branch once today's file existed,
emitting a bare heading exactly when a preview is most wanted, so it now
always previews from the newest note that is not today.
The unit is sandboxed, and that was verified against the NFS mount rather
than assumed: writes to the notes directory succeed, and writes to its
parent are refused.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
The rule PLAN.md describes and the rationale it gives for that rule
disagree on one shape that turns out to be common. 235 checkboxes in the
corpus sit under a plain bullet used as a group label, and by the letter
of the rule those have no checkbox ancestor, so a finished one would be
dropped on its own. But section 4 already argues the opposite case for
checkbox parents: a finished item under an unfinished group is context,
not cruft, and a label like `- Trellis "Ergonomics" Fixes` is a group in
every sense that matters.
Resolved in favour of the rationale, which collapses both cases into one
rule. A top-level bullet is any bullet with no bullet ancestor in its
section. It is dropped, with its subtree, exactly when that subtree holds
at least one checkbox and all of them are [x]. Prose bullets hold none and
so are never eligible, which is the property section 4 wanted.
Validated across all 228 notes with zero invariant failures: no heading,
no unfinished item and no checkbox-free subtree is ever removed, and no
group label is left with its children gone. 270 lines would be dropped
across 118 files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
Phase 0. Resolve the four Syncthing conflict files and push the laptop's
228 notes to mainframe, both sides now hashing identically.
Settle the If-Match question PLAN.md deferred to phase 1: SFTPGo's WebDAV
returns ETags but ignores the precondition, so a PUT carrying a bogus
If-Match still writes. The client takes the HEAD-compare-then-PUT fallback.
Chasing a PUT/HEAD ETag mismatch turned up a NAS whose clock was 124
seconds fast with no time daemon installed, which NFS was stamping onto
every file mtime under /docs. Installed and enabled chrony there. The
mismatch survives the fix at ~1ms, so it is a write-path race in SFTPGo
that the broken clock had been amplifying, not a protocol defect.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
Extends ~/.local/bin/todo to Android without turning it into an app. Notes stay
plain markdown in ~/docs/notes/todo; this repo holds the tooling.
Key finding: files.rcjohnstone.com already serves /home/connor/docs over
authenticated WebDAV, and sftpgo and syncthing mount the same host directory, so
today's note is already reachable at a public TLS URL. No new service, hostname,
Caddy block, or credential store is needed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NctNN59e573MxYdNWzNHUw