Commit Graph
9 Commits
Author SHA1 Message Date
Connor JohnstoneandClaude Opus 5 1eef5e5ae3 Add the WebDAV-syncing laptop client
Fixes the three defects PLAN.md section 6 lists. The previous-note glob is
constrained to the date pattern, so a .sync-conflict- sibling can no longer
be selected as the file to carry forward, which is the likeliest origin of
how those conflicts spread. The retitle is line 1 only, so a body line like
"meeting moved to 2026-09-08" is no longer silently advanced a day. The dead
`&& cd -` is gone.

Pull before the edit, push after. Error handling is shaped by the CrowdSec
rules in section 5 rather than by generic retry logic: a 401 stops dead with
a message, a 404 means the timer has not run yet so it creates once and
never polls, and anything else degrades to editing the local copy.

The offline fallback shells out to the same prune.py the server rollover
uses, installed to ~/.local/lib/todo, so the two cannot drift.

Since SFTPGo ignores If-Match, the precondition is enforced client side by
comparing the ETag captured at pull against a HEAD before the PUT. A remote
that moved mid-edit produces a .sync-conflict- sibling and a non-zero exit
rather than an overwrite. Real merging is phase 5; this just makes phase 2
incapable of losing an edit.

The credential comes from rbw and reaches curl over stdin, never argv, where
any other user's ps could read it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
2026-09-09 18:59:16 -04:00
Connor JohnstoneandClaude Opus 5 45f0fca444 Untrack a .pyc that predated the __pycache__ ignore rule
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
2026-09-09 18:35:30 -04:00
Connor JohnstoneandClaude Opus 5 caa367feb2 Fix review.sh inventing 12 removals that the prune never makes
It round-tripped the pruned text through a shell variable, and command
substitution strips every trailing newline while printf added exactly one
back, so any file whose pruned output ended in more than one blank line
diffed against a reshaped copy of itself. Comparing files directly instead
brings review.sh to 270 removals across 118 files, matching validate.py,
which derives the same number from the drop set rather than from a diff.

The counting was wrong too: a removed `- [x] foo` is `-- [x] foo` in a
unified diff, so the `^-[^-]` pattern skipped every checkbox it was meant
to be counting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
2026-09-09 18:35:25 -04:00
Connor JohnstoneandClaude Opus 5 1053310d32 Add review.sh to regenerate the whole-corpus prune diff on demand
The plan gates the timer on reviewing this diff, and the same gate should
be re-runnable after any change to the rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
2026-09-09 18:34:20 -04:00
Connor JohnstoneandClaude Opus 5 213e285c37 Add the server rollover, its systemd units, and an installer
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
2026-09-09 18:33:54 -04:00
Connor JohnstoneandClaude Opus 5 185b45dbfc Add the prune rule, its tests, and a corpus validator
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
2026-09-09 18:30:29 -04:00
Connor JohnstoneandClaude Opus 5 727813b5e9 Record that the NAS crystal is sound, the clock was just never set
chrony measures the hardware at 2.6 ppm, so the 124s was about eighteen
months of accumulation on a machine with no time daemon, not drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
2026-09-09 18:20:53 -04:00
Connor JohnstoneandClaude Opus 5 6033f9c38d Reconcile notes, verify WebDAV, fix NAS clock drift
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
2026-09-09 18:20:11 -04:00
Connor JohnstoneandClaude Opus 5 275ecd78ac Add plan of record for cross-device todo notes
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
2026-09-09 17:53:32 -04:00