Lossless Changelog
product

The bucket has a corpus in it now

Fourteen days after R2 was decided, the prefix still held zero objects. 886 files went up in 28 seconds and came back byte-identical.

Michael StatonMusic Editoraugmented with Claude Code on Claude Opus 5 (1M context)

The bucket has a corpus in it now

Why Care?

The R2-native substrate decision was made on 2026-08-08. An audit on 2026-08-22 found the configured prefix — r2://reach-edu/corpora/ — holding zero objects, while the reach-edu corpus grew through an entirely separate path in git. Not a plan being executed slowly; a plan the work was routing around.

This is the smallest move that changes that, and it took under a minute of transfer time.

What happened

886 files, 153.7 MiB, uploaded in 28 seconds. Then pulled back down into an empty directory and compared file-by-file:

counts:  source 886   recovered 886
source manifest lines:    886
recovered manifest lines: 886
RESULT: IDENTICAL — every file's sha256 matches, byte-for-byte round trip

The file count is a useful cross-check on its own: 886 is exactly what git ls-files corpus reports for the augment-reach-edu submodule, so what is in the bucket is precisely what is tracked in git — no more, no less. The six .DS_Store files in the working tree were excluded and account for the whole difference from the 892 files on disk.

The property this was actually testing

live/ is not machinery. It is plain markdown written at plain keys, and its whole value is that someone with rclone and no corpora-builder can reconstitute a working corpus. That was a design intention until today. It is now measured:

CheckResult
Recovered file count886 / 886
Per-file sha256all identical
Markdown files845
Files opening with --- frontmatter840 (the five without are README.md, AGENTS.md, and kin — correct)
Binaries (PDFs and friends) survived41
grep -rl "rural" strategies/ on the recovered tree46 hits

That last row is the one that matters. A corpus you can grep with no tooling installed is the constraint the whole substrate argument rests on, and it holds.

scripts/mirror_corpus.sh

Shipped so this is repeatable rather than a one-time act.

BASH
./scripts/mirror_corpus.sh <source-dir> [--verify] [--dry-run]

Three decisions worth stating, because each could reasonably have gone the other way:

  • rclone copy, never sync. sync deletes at the destination to make it match the source. The destination is a client-owned bucket. A one-way mirror should not be able to remove anything there, and if pruning is ever wanted it should be its own explicit decision rather than a flag on this script.

  • Credentials via RCLONE_CONFIG_R2_* environment variables, read from .env at run time and never echoed. No remote is persisted to ~/.config/rclone/rclone.conf, so running this leaves no new copy of an R2 secret on disk. (A persistent remote was created during the first manual run and has been removed.)

  • .DS_Store excluded, matching what git tracks.

Idempotent: a second run transfers nothing.

What this does not do

  • It does not make R2 authoritative. The laptop and git remain the source of truth. This is a mirror, one direction, laptop → bucket.

  • It is not scheduled. Run deliberately. Automating it is a separate decision with its own failure modes.

  • It does not give corpora-builder ownership of the corpus. That is a handover, and it is still open — see the plan.

  • ai-labs/context-v/plans/Sync-Corpora-to-R2-and-Show-Clients-What-Changed.md — Phase 1, which this completes

  • context-v/specs/Storage-Seam.md — the seam that made the destination interchangeable

  • lossless-monorepo/context-v/reminders/Check-The-Substrate-Before-Reasoning-On-Top-Of-It.md — the audit that prompted this