| 1 |
# Overnight backlog 2 (2026-09-28 to 2026-09-30) |
| 2 |
|
| 3 |
Andy's brief: usability and features on Oxygenhub, as many features as |
| 4 |
possible in the VS Code extensions, and a stable, mature `oxy` client. |
| 5 |
Work top to bottom, but interleave the three tracks: after each hub or |
| 6 |
extension item, take the next oxy stability item if one is open. Each |
| 7 |
item is done when its tests pass, the gate is clean, it is committed with |
| 8 |
`oxy` as Andy and pushed to Earth. Log each iteration at the bottom. |
| 9 |
|
| 10 |
## Hub (oxygenhub): usability and features |
| 11 |
|
| 12 |
- [x] H1 Dashboard for signed-in users: my repositories, proposals needing me, issues assigned to me, recent activity across repositories I can see (pushes, proposals, issues, releases). |
| 13 |
- [x] H2 Empty-repository quickstart: exact commands for this repository (remote add, token, CA download, push) and for starting from nothing. |
| 14 |
- [x] H3 Theme switch (dark, light, auto) in the header and under Settings, remembered in the cookie; check every page in light mode. |
| 15 |
- [x] H4 Profile and organisation pages: generated identicon avatar, display name, bio, linked keys, repositories, recent activity, organisation members and teams. |
| 16 |
- [x] H5 Diff view: split (side by side) toggle remembered per user, collapse per file, hide whitespace changes, intra-line word highlighting. |
| 17 |
- [x] H6 Forks: fork button, fork creation (store copy, forked_from), fork list, "propose to upstream" from a fork. |
| 18 |
- [x] H7 Stars and watch counts, explore sorting (activity, stars, name) and filtering, repository topics. |
| 19 |
- [x] H8 Proposal and issue lists: filters (author, label, target, assignee), sort orders, pagination, counts in tabs. |
| 20 |
- [x] H9 File view: file tree sidebar, `y` for a permalink to the state, line range copy, blame age shading and "blame the state before this change". |
| 21 |
- [x] H10 Compare page with bookmark and tag pickers and a "propose this" button. |
| 22 |
- [x] H11 Help pages: getting started (install oxy, clone, push, tokens, CA, delegating to the hub), keyboard shortcuts, glossary for people coming from GitHub. |
| 23 |
- [x] H12 Password reset and email verification (email when SMTP is set; admin-issued reset link otherwise). |
| 24 |
- [x] H13b Uploaded profile pictures and organisation logos (Andy, oxygenhub#11): upload, validate by content, re-encode and crop, identicon fallback, remove. |
| 25 |
- [x] H13 Notifications: per-thread mark read, filter by kind and repository, "participating" versus "watching". |
| 26 |
- [x] H14 Accessibility and mobile pass: focus styles, skip link, landmarks, responsive tables and tabs, contrast in both themes. |
| 27 |
- [x] H15 Organisation webhooks page; admin storage and repository maintenance page (sizes, oxy gc). |
| 28 |
- [x] H16 Performance: cache rendered Markdown and highlighted files by object hash; measure the heaviest pages on Earth before and after. |
| 29 |
- [x] H17 Issue API: labels, assignees and milestone on create/edit; create labels (oxygenhub#3). |
| 30 |
- [x] H18 `refs #N` in change messages appears on the issue's timeline (oxygenhub#4). |
| 31 |
- [x] H19 Projects: boards of issues and proposals across an owner's repositories (oxygenhub#5). |
| 32 |
- [x] H21 Projects API: list, create, board, cards and columns over /api/v1 (oxygenhub#15). |
| 33 |
- [x] H20 Issue Markdown resolves against the default bookmark, not `main` (oxygenhub#6). |
| 34 |
|
| 35 |
## VS Code (oxygen-vscode, oxygenhub-vscode) |
| 36 |
|
| 37 |
- [x] V1 oxygenhub-vscode: proposal files in native diff editors (target state against projected state through a content provider), with review threads in them. |
| 38 |
- [x] V2 oxygenhub-vscode: notifications count in the status bar and an inbox quick pick that opens the item. |
| 39 |
- [x] V3 oxygenhub-vscode: completion of `#N` and `@user` in the Oxygen commit box and in comments; hover on `#N` shows the title. |
| 40 |
- [x] V4 oxygenhub-vscode: create an issue from the editor selection or a TODO line, with a permalink. |
| 41 |
- [x] V5 oxygenhub-vscode: releases view; draft a release from a tag. |
| 42 |
- [x] V6 oxygen-vscode: history graph actions for split, squash and rebase (oxy history commands), with confirmation and undo hint. |
| 43 |
- [x] V7 oxygen-vscode: commit box polish: amend toggle, message history, signed/unsigned indicator, warning on conflict markers. |
| 44 |
- [x] V8 oxygen-vscode: Open on Remote in editor gutter context (line permalinks), copy permalink. |
| 45 |
- [x] V9 Both: README screenshots-free walkthroughs, settings documentation, CHANGELOG per release. |
| 46 |
|
| 47 |
## oxy: stability and maturity |
| 48 |
|
| 49 |
- [x] O1 Property tests (proptest) that applying the same changes in any order gives the same state, over random edit sequences on several files. |
| 50 |
- [x] O2 `oxy fsck`: verify every object's hash, every change's references and signatures, the op log chain, and report precisely. |
| 51 |
- [x] O3 Crash safety audit: every write goes through write_atomic or the store's batch; a test that interrupts commit and push at each step and proves the repository still opens and fscks. |
| 52 |
- [x] O4 Benchmarks with thresholds: 10 000 files, 5 000 changes; status, commit, log, blame, push, clone; record numbers in docs. |
| 53 |
- [x] O10a Heads at scale (first part of O10): `State::maximal` compared every pair of heads (quadratic; about 1.5 s per call at 4 500 heads, called three times per commit); now one walk. Commit reuses the snapshot's base state instead of loading it again. |
| 54 |
- [x] O10b Parallel snapshot work: rendering the base state's files, building file graphs from a checkpoint, and reading working-copy files now run on every core (rayon). |
| 55 |
- [x] O12 `log_for_path_in` and `blame_in` rebuild and re-render the whole state on every call, and walk every change: take an already loaded state, stop at the first change touching the path when only the newest is wanted, and benchmark it (found in H16). |
| 56 |
- [x] O10c Snapshot cost in the in-memory state: faster graph hashing, no unused predecessor map, inline successor sets, lines moved not copied; `OXY_TIMINGS=1` phase timings. |
| 57 |
- [x] O10d Skip reading text files whose stat matches a manifest that recorded them equal to the base (racy entries, those modified at or after the manifest was written, are still read); test that no edit is ever missed. |
| 58 |
- [x] F1a **Priority (Andy, 2026-09-28): dogfood OxygenFlow.** Every push to oxygen, oxygenhub, oxygenflow and both extensions lints, tests and builds through OxygenFlow on Earth, with results as checks on OxygenHub. F1a: ephemeral scratch mounts in jobs (`ephemeral://tmpfs`, `ephemeral://disk`) so builds have room; `make deploy-runner`. |
| 59 |
- [x] F1b A build image for the runner: a rootfs with the Rust toolchain (stable, rustfmt, clippy), Node, e2fsprogs-free build basics, and every crate of the oxygen, oxygenhub and oxygenflow lock files vendored with cargo pointed at them (jobs have no network); a script that rebuilds it when lock files change; imported on Earth and measured on a real cargo test of oxygen in a microVM. |
| 60 |
- [x] F2 oxygen pipeline (.flow/pipeline.kdl): fmt, clippy -D warnings, tests, release build; green on a real push. |
| 61 |
- [x] F3 oxygenhub pipeline: needs oxygen alongside (path dependencies), so the job checks out oxygen at a pinned change; fmt, clippy, tests, build. |
| 62 |
- [x] F4a oxygenflow's pipeline gains fmt, clippy and tests, with oxygen as its pinned subrepo; oxygenhub builds against the oxygen that oxygenflow pins (one oxy-core), and claims follow subrepos of subrepos. |
| 63 |
- [x] F4b Extension pipelines (oxygen-vscode, oxygenhub-vscode): typecheck, bundle, tests and package, offline. Needs the npm packages in the build image, and `oxy` for oxygen-vscode's tests (built in the job from a pinned oxygen). The hub client tests skip without a hub binary. |
| 64 |
- [ ] F6 Faster runs: the runner takes one job at a time (oxygenhub is about 14.5 min to green, 8.5 of them the release build), so run independent jobs in parallel within the runner's CPU and memory, and reuse a build tree between jobs of one run (clippy, test and build compile the same crates). |
| 65 |
- [x] F5 Required checks: each repository's default bookmark requires its pipeline's checks to land, and the work loop pushes and waits for green instead of gating only locally. |
| 66 |
- [x] O10e Checkpoint payload version 2: large file graphs as shared blobs named from an index, read and decoded in parallel; readers accept both versions (spec §11). Agreed by Andy on 2026-09-28. |
| 67 |
- [x] M1 (metalgear feedback 1, reported by Andy) Bookmarks with `/` in their names could not be pushed: attestation and checkpoint files are now flat, escaped names (oxygen#12). The hub is pinned to the fix and deployed. |
| 68 |
- [x] M2 Flow on/off per repository in the hub (Andy's request; feedback 10): a switch in repository settings for site admins, with hub.toml's `[flow] repos` as the default; no restart. |
| 69 |
- [x] M11 (feedback 12, Andy: urgent) Flow runs numbered per repository, not by the hub-wide id; existing runs renumbered and their check links rewritten by migration 0019 (oxygenhub#23). |
| 70 |
- [x] M12 (Andy; feedback 14a) Delete a bookmark on a remote: `oxy push REMOTE --delete NAME` (a signed, attested deletion the server checks like any update; never the default bookmark), and a delete button on the hub's bookmarks page for writers. |
| 71 |
- [ ] M13 (feedback 3a) `oxy bookmark switch --carry`: take uncommitted work to the other bookmark when it applies cleanly there (and `bookmark create --switch` moving the working copy onto a new bookmark at its base, changes and all). |
| 72 |
- [ ] M14 (feedback 7a) `oxy show`'s summary lists every path the change touches with its kind (added, modified, removed, renamed, mode), not only additions. |
| 73 |
- [ ] M3 Start a run without moving a bookmark (feedback 9): "Run pipeline" on a state and "Re-run" on a finished run, in the web and the API. |
| 74 |
- [ ] M4 Guest `/dev/fd`, `/dev/stdin|stdout|stderr` links in flow-init (feedback 15), and job variables `FLOW_STATE`, `FLOW_HEADS`, `FLOW_BOOKMARK`, `FLOW_RUN`, `FLOW_REPO` (feedback 17). |
| 75 |
- [ ] M5 Sandbox-level `env` in pipeline.kdl, and the image's environment kept through `flow rootfs import` (feedback 16). |
| 76 |
- [ ] M6 Flow API: `GET /repos/{o}/{n}/flow/runs`, `/flow/runs/{n}` with jobs, a job's log; checks by head hash as well as state (feedback 11). JSON 404s under `/api/v1` (feedback 13). Per-repository run numbers, or a note (feedback 12). |
| 77 |
- [ ] M7 Server push errors name the operation and bookmark and log the error chain (feedback 2). |
| 78 |
- [ ] M8 `merge` updates a working copy that is clean against the pre-merge base; `checkout` tells "removed by you" from "not yet written"; `bookmark switch --help` says what happens to uncommitted work (feedback 3). |
| 79 |
- [ ] M9 Small oxy items: `oxy describe` (tag, else bookmark + change, `+dirty`) and tags accepted where a change is (feedback 4); `commit -F` (6); `submodule status` shows added-but-unpinned and has `--json` (5, 7); doctor before the first commit (8). |
| 80 |
- [ ] M10 Hub: say "signed by an unlinked key" on changes and in `/api/v1/user`, pointing at Settings (feedback 14). |
| 81 |
- [ ] P1 **Conflict resolution on OxygenHub** (Andy's list, item 1). A resolver page for a conflicted state: |
| 82 |
- Per conflict region: pick a side, several sides, or none; edit freely; tree conflicts too (keep a candidate). |
| 83 |
- Commit the result as a `kind = resolution` change on the bookmark, through the web-edit path. Signing: sealed when the author next pushes, or "resolve in VS Code" hands off to oxygen-vscode. |
| 84 |
- Shareable link to a conflict. |
| 85 |
- First single-user, then P1b. |
| 86 |
- [ ] P1b Real-time collaboration on the resolver: presence, live edits merged between viewers (SSE or WebSocket), and whoever is viewing sees the resolution land. |
| 87 |
- [ ] P2 **Function history** (item 5; needs no semantic-deps): |
| 88 |
- oxy-core: a history query over a set of atoms (the changes that inserted or deleted any of them, with deleted atoms included). |
| 89 |
- Hub: function boundaries by tree-sitter for the languages we host (Rust, TypeScript, Python, shell). |
| 90 |
- Clicking a function on a file page shows every ChangeId that touched its lines, as a list and a small graph; exact because atoms keep identity across edits and moves. |
| 91 |
- The cross-file semantic graph stays out; noted as future work. |
| 92 |
- [ ] P3 **Swimlanes** (item 3): |
| 93 |
- Hub: a bookmark's changes drawn as lanes of independent changes (oxy-core `lanes`), with dependencies as edges and no merge nodes. |
| 94 |
- P3b: drag and drop on proposal bookmarks only. Dropping a change onto another proposes a squash, dragging a dependent chain proposes a rebase. The hub produces a plan and the author's oxy or oxygen-vscode applies and signs it (V6 has split, squash and rebase). Reordering independent changes means nothing and is not offered. |
| 95 |
- [ ] P4 **Signed Flow receipts**: |
| 96 |
- A `receipt` object type in the Oxygen spec. |
| 97 |
- A runner identity whose key (composite Ed25519 + ML-DSA-65) signs each receipt. |
| 98 |
- Receipts stored in the repository and verified by the hub. |
| 99 |
- Flow `output` and artifacts, so a release asset links to the receipt that built it. |
| 100 |
- [ ] P4b **Trust dashboard** (item 4): per release and asset, a graph of authors' identities and signatures, the runner and its pinned environment, inputs (state, scope hash), and receipts, with a one-click audit view and export. |
| 101 |
- [ ] P5 **Web workspaces** (item 2), in parts: |
| 102 |
- P5a: flow-casfs, a read-only FUSE mount of a state from CAS chunks, measured. |
| 103 |
- P5b: "Open in Workspace" on a change. openvscode-server (MIT, unmodified) with oxygen-vscode inside a Firecracker microVM per workspace, the state mounted read-only through casfs with an ephemeral overlay for edits, and commits back through oxy. Boot time measured, aiming under 100 ms for the mount. |
| 104 |
- A fork ("oxycode") only if an extension cannot do what we need; decided with Andy. |
| 105 |
- [ ] P5c Import microsoft/vscode's full Git history with `oxy git import` into a local scratch repository (not published): about 130 000 commits as a stress test of import, log, blame and status at scale, recorded in docs/benchmarks.md. |
| 106 |
- [x] O10f Load file graphs lazily from a version 2 checkpoint: a state keeps unread graphs by reference and reads one when first needed. |
| 107 |
- [x] O10g Render only what a snapshot needs, first part: the manifest records, per file, the key of the base graph it matched, so an unchanged file whose graph is untouched is neither read nor rendered; used by status, log, show, tag and bookmark. |
| 108 |
- [ ] O10h The same for commit, diff, amend and the other full snapshots: a lazy base that builds only the graphs the change touches; cache materialized states. |
| 109 |
- [x] O5 Concurrency: two processes committing, snapshotting and pushing on one repository; locks hold, nothing is lost. |
| 110 |
- [x] O6 Error messages audit: every user-facing error says what to do next; `oxy doctor` checks identity, config, remotes, CA files, hub reachability. |
| 111 |
- [x] O7 Shell completions (`oxy completions bash|zsh|fish`) and man pages generated from clap. |
| 112 |
- [x] O8 Git round trip on real history: import a scratch mirror of tetriblox (never the live repo), export it back, compare trees at every commit. |
| 113 |
- [x] O11 Line placement found by O8: in 5 of 539 tetriblox commits (all non-merge; e.g. 43de61fac554 src/banked/code_bank_07.c, 021ad78791a2 code_bank_03.c, 2721670b49af code_bank_02.c, 435fbe1d5fac scripts/x16_unchanged.py, d17c19b22748 test/test_memory_guards.py) one line lands elsewhere than in Git with no conflict reported, so an insertion is anchored between atoms that are not adjacent in the base. Reproduce with `oxy git verify` on a scratch mirror; find which edit of the importer's diff (build_from_entries/text_edits) anchors non-adjacently, add a debug assertion for anchor adjacency, fix, re-verify. |
| 114 |
- [x] O9 Portability review for macOS and Windows: path separators, executable bits, case-insensitive file systems, CRLF; tests where it can be exercised on Linux. |
| 115 |
|
| 116 |
## Log |
| 117 |
|
| 118 |
- 2026-09-28 H1 done: signed-in dashboard at `/` (review queue, own proposals, assigned issues, repositories, activity feed filtered to visible repositories); test `dashboard_shows_what_needs_the_viewer`; deployed to Earth. |
| 119 |
- 2026-09-28 O1 done: `crates/oxy-core/tests/commutativity.rs` (proptest, 32 cases): diverging clones with random edits, rebuilt in shuffled and random dependency orders, checkpoint round trip, and push in random order to a fresh clone; all give identical checkpoints, heads and trees. About 80% of generated cases conflict. No defects found. Workspace tests all green (oxy-core 129 + new). |
| 120 |
- 2026-09-28 H2 done: empty repositories show a quickstart (remote named after the host, e.g. `earth`; CA download and `remote.X.ca` when self-signed; token placeholder; push; start from nothing; `oxy git import`); readers get a plain notice. Tests: `empty_repository_shows_a_quickstart` and a remote-naming unit test; hub spec §6 row updated; deployed. |
| 121 |
- 2026-09-28 O2 done: `oxy fsck [--json]` (objects, op log through all parents, references and change closure with identities/blobs/chunks/signatures, bookmark states, checkpoints re-derived, ref and tag attestations verified); spec Draft 0.8h §21.1, guide updated. Test `fsck_passes_a_sound_repository_and_names_each_fault` (corrupt object, missing change, missing blob, broken op head). Clean on oxygen, oxygenhub, oxygen-vscode, oxygenhub-vscode, hellooxy. Release binary rebuilt. |
| 122 |
- 2026-09-28 V1 done: hub API `GET /proposals/{n}/files` and `/proposals/{n}/content/{base|head}/{path}`, and anchored comments now carry their current `line` (deployed). oxygenhub-vscode 0.2.0: proposals expand to files, native diff editor per file, View Changes multi-diff, review threads on the projected side; installed in Default and Rust profiles (Developer: Reload Window to pick it up). Tests: hub api test extended; extension pass 6 fail 0 against a real hub. |
| 123 |
- 2026-09-28 O3 done: new `oxy_core::durable` (unique temp, fsync, rename, directory fsync) for every durable write; **real bug fixed**: inside a write batch the op head moved before the batch (including the new op object) was on disk, so a crash there left an unopenable repository; `Store::flush` now runs before the head moves. Test stops commit (20 steps) and push (32 steps) at every step: both repos open, fsck clean, command retries. Spec §13.6. Hub rebuilt and redeployed with the fix. Not covered: working-copy file writes during checkout are still plain writes (user files, recoverable by snapshot). |
| 124 |
- 2026-09-28 H3 done: header theme switch (dark/light/auto) and Settings → Appearance, theme cookie; auto follows the system live. Light-mode pass by headless Chrome screenshots of dashboard, overview, file, proposal, issue, settings, changes: found and fixed highlighted code drawing boxes in both themes (syntect scope `comment` colliding with the page `.comment` class; classes now `sy-` prefixed) and the band ADMIN link unreadable in light. Test `theme_is_chosen_in_settings_and_remembered_in_a_cookie`; hub spec §6.1; deployed. The code editor picks its theme at load, not live. |
| 125 |
- 2026-09-28 O4 done: harness `crates/oxy-core/tests/bench.rs` (ignored; run by hand) and `docs/benchmarks.md`. Full run on disk took 5 h 14 min. **Andy: status and commit are slow at scale**: at 10 000 files / 5 000 changes a commit averages 3.8 s, a 10-file commit 9.1 s, a clean status 3.0 s, blame 2.9 s, peak memory 1.4 GiB; push 0.8 s, clone 3.4 s, log 0.4 s are fine. Cause: every snapshot loads and materializes the whole base state. New item O10 (moved ahead of O5) fixes it. |
| 126 |
- 2026-09-28 V2 done: hub API `GET /notifications`, `POST /notifications/{id}/read`, `POST /notifications/read` (scope user:read; deployed). oxygenhub-vscode 0.3.0: status-bar bell with unread count across hubs, new-notification messages, Open Inbox quick pick that marks read and opens proposals/issues in the editor. Installed in both profiles (Reload Window). Tests: hub api test extended; extension pass 7 fail 0. |
| 127 |
- 2026-09-28 O10a done: `State::maximal` was quadratic in heads (4 500 heads in the benchmark); now one walk, with a property test against the old definition. Commit reuses the snapshot's base. At 10 000 files / 5 000 changes: commit 3.8 s to 0.86 s, 10-file commit 9.1 s to 0.86 s, status 3.0 s to 0.80 s, blame 2.9 s to 0.81 s, memory 1.4 to 1.1 GiB; benchmark run 5 h 14 min to 1 h 12 min. Hub redeployed with it. O10b (manifest fast path, lazy base) remains for the per-file cost. |
| 128 |
- 2026-09-28 V3 done: oxygenhub-vscode 0.4.0: `#` and `@` completion in the Oxygen commit box and comment editors, hover on `#N`; repository from the SCM root. Parsing tested in Node (pass 10, fail 0). Installed in both profiles. |
| 129 |
- 2026-09-28 O10b done: rayon for materialize, checkpoint file graphs and working-copy reads. At 10 000 files: clean status about 800 to 460-580 ms, commit 861 to 550-605 ms. Remaining cost is loading the whole base (O10c, needs a checkpoint format change in the spec, so left for Andy to agree). Hub redeployed. |
| 130 |
- 2026-09-28 H4 done: `/{owner}` profile (identicon, display name, bio, location, website, keys, organisations, visible repositories, activity by that person; never the email) and organisation page (description, members and roles, teams with sizes, repositories, activity); `/avatar/{name}` identicons also in the header and feeds; migration 0008 (bio, website, location) and Settings fields (http(s) websites only); `avatar` and `help` reserved. Test `profile_and_organisation_pages`; screenshot checked; deployed (andymccall profile 200 on Earth). |
| 131 |
- 2026-09-28 O5 done. **Real bug fixed**: concurrent commands overwrote `.oxy/ops/head`, so the loser's operation (and its commit on the bookmark) vanished; a two-workspace test lost about half its commits every run. The head now moves by compare-and-swap under a short `.oxy/ops/lock`, and a mover that lost the race records a merge op (two parents, three-way merged view; bookmarks keep both sides' heads), as spec §12.1 described; undo refuses on a moved head. Tests: concurrent two-workspace commits with pushes lose nothing (5/5 runs), deterministic merge of two ops from one parent; two real oxy processes x 25 commits keep all 50 (39 merges). Spec §12.1 updated. Hub redeployed. |
| 132 |
- 2026-09-28 H5 done: word-level marks within paired changed lines, `?w=1` hides whitespace-only changes (change, compare, proposal files), collapsible files (over 400 rows start closed), unified/split toggle remembered in a `diff` cookie, collapse/expand all. Tests: two renderer unit tests and `diffs_mark_words_hide_whitespace_and_collapse`; split view checked by screenshot; hub spec §6.8.1; deployed. |
| 133 |
- 2026-09-28 O6 done: `oxy doctor [--offline] [--json]` (op log and stale locks, identity object/secret/expiry, author defaults, each remote: plain HTTP, token, CA file, reachability and repository id) with a next step per problem; every failed remote request now carries a hint (untrusted certificate, 401, 403, 404, refused, DNS, timeout) and transport errors no longer repeat the URL; clearer messages for different-repository remotes, pull-first pushes (still contains "pull first", which oxygen-vscode matches), unauthorized keys, and no repository. Tests: hint table and doctor scenarios. Clean on oxygen and oxygenhub checkouts. Spec §21.2, guide. Hub redeployed. |
| 134 |
- 2026-09-28 H6 done: fork button and form, `Repos::fork` (new id, hard-linked objects, bookmarks copied, no policy), migration 0009 `forked_from`, forks list, "forked from" and fork count on the overview, "propose to upstream" link. Test `fork_and_propose_upstream` (fork, fsck, clone the fork, push a bookmark, propose upstream, files tab shows it). Hub spec §6.3 row; deployed. |
| 135 |
- 2026-09-28 O7 done: `oxy completions SHELL` (clap_complete) and `oxy man [--dir]` (clap_mangen 0.3, 104 pages); help text written for 112 bare commands and arguments with a test that every one has help; oh-my-bash plugin loads bash completions; guide section. Tests `cli_docs` (every shell, pages, argument help on the page). Andy: `oxy completions bash > ~/.local/share/bash-completion/completions/oxy` to enable. |
| 136 |
- 2026-09-28 O7 note: the O7 commit went in before clippy finished (two lints in the new help test); fixed in the next commit. The gate now runs to completion before every commit. |
| 137 |
- 2026-09-28 H7 done: stars (toggle, counts), watcher counts on the overview, repository topics in settings (validated) shown as chips, Explore filter by text and topic and sort by activity/stars/name with per-row stars and topics; migration 0010. Tests: topic parsing unit test and `stars_topics_and_explore`. Gate fully green before commit; deployed. |
| 138 |
- 2026-09-28 O8 done. `oxy git verify GIT_DIR` checks an import commit by commit (content, exec bits, symlinks, pins; conflicts named with their changes; first differing line). On a scratch mirror of tetriblox (539 commits, 208 merges, 880 gitlinks; the live repo was only read by `git clone --mirror`): import 36 s, tip tree round-trips exactly, and verification found **real core bugs that also affect ordinary commits**, now fixed: (1) insert after a line, then delete that line, left a delete/insert conflict (deletions now depend on the inserters of the lines right after them); (2) a binary or submodule reverted to an earlier blob lost it, and replacements had no dependency (now depend on the change that set their old blob; causal supersession, legacy records keep the old rule); (3) merges of independently replaced binaries/submodules stayed conflicted (importer resolves to the merge's blob). Mismatching commits went from 50+ (verification stopped early) to 5, now O11. Tests: 3 regressions (sequential insert/delete, binary revert, Git binary merge + verify). Spec Draft 0.8i §8.1, §9.3, §23. Hub redeployed. |
| 139 |
- 2026-09-28 H8 done: open counts on the Proposals and Issues tabs (from the repository lookup), issue `author:` filter and sorts (newest/oldest/updated/comments), proposal filter (`author:`, `label:`, `target:`, words) with labels shown and sorts (updated/newest/oldest), 25-per-page pager keeping filter and sort. Test `lists_filter_sort_page_and_count`; spec §6.3 rows; deployed. |
| 140 |
- 2026-09-28 O9 done: `portable::Platform` (detected, overridable in tests); no exec bits: snapshot keeps the recorded bit (it was recording a mode change on every Windows snapshot); no symlinks: a plain file holding the target stays the symlink (spec §7.7 required it, the code converted it); checkout lists case collisions and Windows-invalid names on affected platforms and `oxy doctor` does everywhere; CRLF already handled by the eol attribute; separators verified (components joined with /). Tests simulate Windows and macOS. Follow-up: normalise `\` in paths typed on Windows command lines. Spec §7.7. Hub redeployed. |
| 141 |
- 2026-09-28 V4 done: oxygenhub-vscode 0.5.0 "New Issue from Selection" (context menu): quotes the selection under a permalink (head-pinned when single-headed and saved), TODO/FIXME lines suggest the title and can be annotated `TODO(#N)`, then the issue opens; line-range links. Tests pass 13 fail 0. Installed in both profiles. |
| 142 |
- 2026-09-28 O11 done: root cause in `text_edits`: similar's capture_diff_slices runs a Compact hook that slides deletions along equal lines and can leave an insertion with an earlier old index, anchoring a block one line early (a brace on the wrong side). Now uses the diff with only the Replace hook. Randomized replay test (2 000 repetitive files; fails on the old code). tetriblox scratch import verifies all 539 commits, 95 585 files. Note: that commit also picked up Andy's new docs/xenon_spec.md and fix.py, fix2.py, format_xenon.py from the working copy; left for Andy to decide. |
| 143 |
- 2026-09-28 UI request from Andy (between items): repository page centred with the sidebar, README as wide as the file table, clone command in the top bar; repository, organisation and account settings get a sticky list of sections on the left and equal-width boxes. Screenshot-checked, deployed. |
| 144 |
- 2026-09-28 V5 done: oxygenhub-vscode 0.6.0 Releases view (releases per repository with latest/pre-release, assets with size and downloads, source archives) and Draft Release…: pick an unreleased tag, title from the tag message, notes generated from the changes since the newest full release's tag, pre-release suggested from the tag name. End-to-end test tags with oxy and releases through the client. Tests pass 15 fail 0; extension spec §8.2. Installed in both profiles. |
| 145 |
- 2026-09-28 H9 done: file page tree sidebar (path expanded, 200 per directory then "N more", hidden under 900 px); state addresses `h1+h2` (sorted full hashes, at most 32) so any state has a permalink; `y` on file and tree pages switches to it; a line-range bar with Copy lines and Copy permalink; blame shaded in ten age steps and each run links "blame before this change" (the state of its dependencies). Tests: view unit tests (sidebar order and limit, state addresses) and browse end to end. Hub spec §6.3, §6.6, §6.13. Deployed; health ok. |
| 146 |
- 2026-09-28 Requests from Andy (between items): |
| 147 |
- **OxygenHub naming.** The name is written OxygenHub everywhere people read it (hub, both extensions: oxygenhub-vscode 0.6.1, oxygen-vscode 0.7.1). |
| 148 |
- **OxygenFlow M0, built and running on Earth.** Xenon was renamed to OxygenFlow, in the separate oxygenflow repository. It has: |
| 149 |
- flow-core: KDL pipeline, validation with lines, canonical CBOR, receipts, runner API types |
| 150 |
- flow-init, as the guest's PID 1 |
| 151 |
- the Firecracker microVM tier |
| 152 |
- `flow check/run/rootfs/runner` |
| 153 |
- a microVM conformance test |
| 154 |
- **Hub side.** Pushes start runs, a runner API with repo-scoped read-only job tokens, jobs mirrored as `flow/<job>` checks, and a Flow tab with the SVG job graph and live logs. Hub spec §13.2; migration 0011; tests/flow.rs. |
| 155 |
- **Earth.** Firecracker v1.17.0, kernel 6.1.155 and the Alpine 3.24.2 rootfs, pinned in ~claude/flow. `flow-runner.service` is hardened. `hub.toml` gained `[flow]` limited to `andymccall/*` (backed up as hub.toml.before-flow). |
| 156 |
- **Result.** oxygenflow's own pipeline passes (runs 1 and 2). Andy's spec is committed, renamed to OxygenFlow, with a README. |
| 157 |
- **oxy-core.** `tls_client_config` is public. |
| 158 |
- 2026-09-28 Flow state stamps instead of CI badges (oxygenhub#1, closed by the change): |
| 159 |
- `/flow/stamp/{ref}` is an SVG of the newest run of exactly that state, with receipt hashes in its metadata. For bookmarks and tags it redirects to the stamp of the current state. |
| 160 |
- In READMEs, `[](.flow/run)` resolves against the state being viewed. |
| 161 |
- Fixed oxygenhub#2 on the way: README images pointed at the HTML file page. There is now a `/raw/{ref}/{path}` route. |
| 162 |
- The oxygenflow README shows its stamp (oxygenflow#1). |
| 163 |
- From now on every item gets an issue on Earth and its commits say closes/refs #N. |
| 164 |
- Gaps the workflow exposed became H17 to H20 (oxygenhub#3 to #6). |
| 165 |
- 2026-09-28 H10 done (oxygenhub#7, closed by the change): |
| 166 |
- The compare page has base and head pickers (bookmarks and tags in a datalist; any change or state can be typed), a swap link, `/compare?base=&head=` as the form target, and `/compare` alone comparing the newest other bookmark against the default. |
| 167 |
- **Propose this** shows for writers when both sides are bookmarks and head has changes; it becomes "proposal #N is open" when one exists. |
| 168 |
- Tests in browse and proposals. Hub spec §6.3. Deployed. |
| 169 |
- 2026-09-28 H11 done (oxygenhub#8, closed by the change): |
| 170 |
- `/help` is a sectioned page with getting started, identity and key linking, pushing, cloning, proposals, delegating to the hub, pipelines and state stamps, the keyboard, and a GitHub glossary. |
| 171 |
- Every command carries this hub's address, remote name and certificate step. |
| 172 |
- It is linked from the band, the footer and the `?` sheet. |
| 173 |
- Browse test; hub spec §6.3. Deployed. |
| 174 |
- 2026-09-28 H12 done (oxygenhub#9, closed by the change). Spec §5.1.1 first. |
| 175 |
- **Account links.** 32 random bytes, stored as a BLAKE3 hash, single use and expiring. Migration 0012. |
| 176 |
- **Verification.** Sent on registration and on every address change (48 h). It only verifies the address it was sent to. Settings shows the status and can resend: one every 2 minutes per address, 10 an hour per account. |
| 177 |
- **Reset by email.** By username or verified email (1 h, one every 2 minutes). The answer is identical whether or not anything matched, and mail goes out in the background so timing leaks nothing. |
| 178 |
- **Reset by an administrator.** From /admin (24 h, shown once, audited). |
| 179 |
- **Using a reset link.** It rotates the session salt (every session ends) and voids other links. TOTP stays on; banned accounts are refused. |
| 180 |
- **Mail.** A shared `mail` module with `file://` for development. Notification mail only goes to verified addresses. |
| 181 |
- **Tests.** tests/recovery.rs covers the whole flow through a file mailbox. |
| 182 |
- **Earth.** Earth has no SMTP, so resets there go through the admin link. Existing addresses count as unverified until verified. |
| 183 |
- 2026-09-28 H13 done (oxygenhub#10, closed by the change): |
| 184 |
- Notifications record their thread, repository and reason (participating or watching) and can be marked done. Migration 0013 backfills threads and repositories from the URLs. |
| 185 |
- The inbox shows one row per thread, with updates and new counts. |
| 186 |
- Filters: inbox, unread and done views, reason, kind family and repository, with counts. |
| 187 |
- Per-thread read, unread and done. Done threads return when a new event arrives. |
| 188 |
- Opening a row marks its thread read. |
| 189 |
- `notify::add` takes a struct. |
| 190 |
- Tests extended in tests/issues.rs. Hub spec §10. |
| 191 |
- The API keeps per-item read; per-thread actions in the API are a follow-up for the VS Code inbox. |
| 192 |
- 2026-09-28 Andy asked for uploaded profile pictures and organisation logos: added as H13b (oxygenhub#11), taken next. |
| 193 |
- 2026-09-28 H13b done (oxygenhub#11, closed by the change): |
| 194 |
- Upload a picture under Settings › Profile › Picture, or an organisation logo under its settings (owners only). |
| 195 |
- PNG, JPEG or WebP, recognised by content; at most 5 MiB and 8 000 px a side. |
| 196 |
- Every upload is decoded, centre-cropped, scaled to 400×400 and re-encoded (PNG if it has transparency, JPEG otherwise), so uploaded bytes are never served. |
| 197 |
- `/avatar/{name}` serves the picture with an ETag, falling back to the identicon; Remove restores the identicon. |
| 198 |
- Avatar CSS no longer pixelates photos. |
| 199 |
- Unit tests for processing and refusals; end-to-end test in tests/accounts.rs. Hub spec §6.3. Deployed. |
| 200 |
- This machine's disk filled up (0 bytes free) during the build. I removed the regenerable `target/debug/incremental` caches of oxygenhub and oxygen (33 GB) and build with CARGO_INCREMENTAL=0 from now on. |
| 201 |
- 2026-09-28 H14 done (oxygenhub#12, closed by the change): |
| 202 |
- Keyboard and screen readers: a focus ring on everything focusable; a skip link to `<main id="main">`; named landmarks (Account, Site, Repository and Settings navigation); `aria-current` on the current tab. |
| 203 |
- Contrast: WCAG AA in both themes, enforced by tests/contrast.rs over the CSS variables. |
| 204 |
- Light theme: mute, acc, ok, now and the string, function and comment syntax colours adjusted. |
| 205 |
- Dark theme: comment colour adjusted. |
| 206 |
- Text no longer uses `--dim`. |
| 207 |
- Under 760 px: tab bars scroll on one line, the tree drops its message column, wide content scrolls inside its panel, sectioned pages stack, and the header search hides. |
| 208 |
- Checked at 390 px on Earth. Hub spec §6.13. |
| 209 |
- 2026-09-28 H15 done (oxygenhub#13, closed by the change): |
| 210 |
- **Organisation webhooks.** A Webhooks section in organisation settings: add, ping, enable or disable, delete, deliveries and redeliver. |
| 211 |
- Repository and organisation webhooks now share one module (`web/webhooks.rs`) and one template macro. |
| 212 |
- Every action checks that the hook belongs to the scope it is managed from. |
| 213 |
- **Storage.** `/admin/storage` lists each repository's size, loose objects and packs. Maintenance runs `oxy gc` (grace period kept, write lock held) on one repository or all, and is audited. |
| 214 |
- Tested in tests/accounts.rs. Hub spec §6.3, §12.2. Deployed. |
| 215 |
- Earth's repositories can be maintained from /admin/storage now; I have not run it on Andy's data. |
| 216 |
- 2026-09-28 H16 done (oxygenhub#14, closed by the change). Measured on Earth first (oxygenhub/docs/performance.md). |
| 217 |
- The raw bytes of heavy files took 5 ms, but their pages took 196 to 428 ms: syntax highlighting, Markdown rendering, and above all the file's "last change", found by `log_for_path_in` rebuilding the state and reading every change on each view. |
| 218 |
- Now cached, in memory-bounded LRUs keyed by content or by (state, path): rendered Markdown, highlighted files, last change and blame. Labels are still computed fresh. |
| 219 |
- `?plain=1` takes the lean path. |
| 220 |
- Warm views: spec.md 196 → 8 ms, repo/mod.rs 400 → 18 ms, state.rs 317 → 14 ms, blame 428 → 23 ms. |
| 221 |
- Unit test for the LRU; browse test checks the second view hits the cache. Hub spec §16.3. |
| 222 |
- Follow-up for oxy-core: `log_for_path_in` should reuse a loaded state instead of rebuilding it. |
| 223 |
- 2026-09-28 O12 done (oxygen#1, closed by the change; the first issue in the oxygen repository): |
| 224 |
- oxy-core has `log_of(state)`, `log_for_path_of(state, path, limit)` and `blame_of(state, tree, path)`. They find the path in the tree view alone, with no full render, and the walk stops at `limit`. |
| 225 |
- `log_for_path_in` and `blame_in` wrap them and load once instead of twice. |
| 226 |
- Test `history_and_blame_of_a_loaded_state_match_the_full_walk`; gate green; release binary rebuilt. |
| 227 |
- The hub uses them for last change, blame and tree rows. |
| 228 |
- Cold file pages on Earth: 196/400/317/428 ms (before any caching) → 146/256/121/88 ms; warm 8–24 ms (oxygenhub/docs/performance.md). |
| 229 |
- 2026-09-28 H17 done (oxygenhub#3, closed by the change): |
| 230 |
- `POST /issues` takes labels, assignees and a milestone (triage). Unknown names are refused before anything is created. |
| 231 |
- New `PATCH /issues/{n}`: title and body for the author or triage; the whole label set, assignees and milestone for triage; each change goes on the timeline. |
| 232 |
- `POST /labels` creates or changes a label and `DELETE /labels/{name}` removes it (triage). |
| 233 |
- hub-api types `IssueEdit` and `NewLabel`; TS regenerated (the extension's copy follows with its next change). Test `api_issue_triage_and_labels`; hub spec §12.1. |
| 234 |
- Dogfooding: |
| 235 |
- Each Earth repository now has the labels bug, feature, performance, accessibility, security and gap. |
| 236 |
- Every issue so far is labelled. |
| 237 |
- The work log's issue helper sets labels from now on. |
| 238 |
- 2026-09-28 H18 done (oxygenhub#4, closed by the change): |
| 239 |
- Any bare `#N` in a pushed change's message, on any bookmark, adds "referenced by change <hash> on <bookmark>" to that issue's or proposal's timeline, with the hash linked. |
| 240 |
- `closes #N` still records only "closed by change"; `owner/name#N` from other repositories is left alone. |
| 241 |
- `refs::references` has a unit test; the issues end-to-end test covers issue and proposal timelines. Hub spec §8. |
| 242 |
- From now on, the `refs #N` lines in work commits show on the Earth issues. |
| 243 |
- 2026-09-28 H19 done (oxygenhub#5, closed by the change). Spec §8.2 first. |
| 244 |
- Projects belong to a person or organisation: `/projects/{owner}` and `/projects/{owner}/{n}` ("projects" is now a reserved name). |
| 245 |
- Columns start as Backlog, Doing and Done; they can be added, renamed, moved and removed (their cards go to the first column; the last column stays). |
| 246 |
- Cards are issues or proposals of the owner's repositories (by `repo#N`, or `#N` with a repository picked) or notes. Each can be on a project only once. They move, reorder and are removed without JavaScript. |
| 247 |
- Visibility: public or private (owner, organisation members and admins). Cards from repositories the viewer cannot read are not shown at all. |
| 248 |
- A closed project is read-only; deleting one leaves the issues. |
| 249 |
- Issue and proposal pages list their projects; profiles and organisation pages link to them. |
| 250 |
- Migration 0015. Test `project_boards` plus reference parsing. Screenshot-checked on a scratch hub. |
| 251 |
- The screenshot tool now disables the browser cache (an earlier shot had served a stale style.css). |
| 252 |
- Gap found: no projects API → H21 (oxygenhub#15). |
| 253 |
- 2026-09-28 H20 done (oxygenhub#6, closed by the change): relative links and images in issue, comment and proposal Markdown resolve against the repository's default bookmark; release notes resolve against the release's tag. Test `issue_markdown_follows_the_default_bookmark`. |
| 254 |
- 2026-09-28 Webhooks, from Andy's report (oxygenhub#16 and #17, closed by the change): |
| 255 |
- **Why Discord got nothing.** Every delivery, ping included, was the OxygenHub envelope, which Discord rejects ("Cannot send an empty message"). |
| 256 |
- **Formats.** Hooks now have a format: `oxygenhub`, `discord` (an embed with the event's summary; mentions disabled so user text cannot ping) or `slack`. It is picked from the URL; migration 0016 switched existing Discord and Slack hooks. |
| 257 |
- **Settings.** |
| 258 |
- The URL is masked (long path segments and query cut short, since chat webhooks keep a token there; the audit log records the same masked form) and wraps inside the panel. |
| 259 |
- The table shows the last delivery result. |
| 260 |
- Each hook has an edit form; a blank URL or secret keeps the current one. |
| 261 |
- **Also found.** Issues opened, commented on, closed or reopened in the web interface emitted no webhook events at all; now they do. |
| 262 |
- **Tests.** Unit tests for the formats and masking; end-to-end test `webhook_formats_editing_and_web_issue_events`. Hub spec §12.2. |
| 263 |
- 2026-09-28 H21 done (oxygenhub#15, closed by the change): |
| 264 |
- `/api/v1/projects/{owner}`: list and create. |
| 265 |
- `/api/v1/projects/{owner}/{n}`: the board as the caller may see it, and `PATCH`. |
| 266 |
- `/cards`: add a reference or note; columns by id or name. |
| 267 |
- `/cards/{id}`: move or remove. |
| 268 |
- The pages and the API share `rights_of`, `board_columns` and `add_card`. hub-api types added; TS regenerated. Test `api_projects`. Hub spec §12.1. |
| 269 |
- 2026-09-28 Repository overview, from Andy's review (oxygenhub#18, closed by the change): |
| 270 |
- The heads and conflicts strips sit inside the main column. |
| 271 |
- Clone is a button beside search: a popup with a selectable command, copy, and the bare URL. |
| 272 |
- Contributors box: authors counted from the state's changes, linked to hub accounts through linked keys; unsigned changes with no author show as "Unattributed changes". |
| 273 |
- Languages box: bytes per language (an extension table plus syntect), with Linguist's colours. Prose, lock files and vendored paths are left out; under 1% folds into Other. |
| 274 |
- A public, internal or private badge by the repository name. |
| 275 |
- Tests in browse.rs and a unit test for detection. Screenshot-checked on Earth. |
| 276 |
- Andy's key is not linked to his account yet (Settings › Keys), so his contributor entry is not a link. |
| 277 |
- 2026-09-28 V6 done (oxygen-vscode#1, closed by the change). oxygen-vscode 0.8.0: |
| 278 |
- The History graph's change picker, and a right-click menu on its rows (webview context menus), offer: |
| 279 |
- Split for heads with several files: pick files; they become the first change. |
| 280 |
- Squash into a dependency, picked when there are several. |
| 281 |
- Rebase onto a bookmark, or a change or tag typed in. |
| 282 |
- Each asks first, then shows oxy's summary with an Undo button (`oxy undo`). |
| 283 |
- Client methods `split`, `squash` and `rebase`, tested against the real oxy with undo. check green (9 tests); packaged and installed into the Rust profile. |
| 284 |
- 2026-09-28 V7 done (oxygen-vscode#2, closed by the change). oxygen-vscode 0.9.0: |
| 285 |
- **Amend mode** (pencil or close icon in the Source Control title bar): Ctrl+Enter amends; the box starts from the last change's message. |
| 286 |
- **Previous Commit Messages**: remembered per repository in workspace state, 30 kept. |
| 287 |
- **Signing indicator**: the placeholder says signed, sealed at push, or unsigned, read from `oxy identity show` at most every 30 s. |
| 288 |
- **Conflict marker warning** on commit, commit-selected and amend: files oxy reports with markers left, plus any changed text file with lines opening or closing a conflict (Oxygen's or Git's). The dialog can open the first one. |
| 289 |
- Pure logic in `src/commitbox.ts` with unit tests; `identity()` tested against the real oxy. check green (14 tests); installed into the Rust profile. |
| 290 |
- 2026-09-28 V8 done (oxygen-vscode#3, closed by the change). oxygen-vscode 0.10.0 and oxygenhub-vscode 0.6.2: |
| 291 |
- The line-number gutter (`editor/lineNumber/context`) and the editor context menu offer Open on Remote, Copy Remote Link and Copy Permalink. |
| 292 |
- The link covers the clicked line or the selection (`#L10-L20`). |
| 293 |
- A permalink uses OxygenHub's state address: the bookmark's heads, sorted and joined with `+`. Checked live: an 11-head address for oxygen/README.md opens with 200 on Earth. |
| 294 |
- A note appears when uncommitted edits may shift the lines, or when remote links point past unpushed changes. |
| 295 |
- `stateSpec` and `linkedLines` are unit-tested; the hub extension keeps `+` readable (tested) and its API types are brought up to date (issue editing, labels, projects). Both checks green; both installed into the Rust profile. |
| 296 |
- 2026-09-28 V9 done (oxygen-vscode#4, closed by the change): |
| 297 |
- Both READMEs rewritten as walkthroughs by task, each claim checked against the code: |
| 298 |
- Oxygen: commit, conflicts, sync, history rewriting, bisect, line links, trust. |
| 299 |
- OxygenHub: propose, review, land, issues, releases, notifications. |
| 300 |
- Each README has a Settings table. |
| 301 |
- Every CHANGELOG entry is dated from the history. |
| 302 |
- `test/docs.test.mjs` in both fails if a setting is missing from the README or the newest CHANGELOG entry is not the package version. Both checks green (18 tests each). |
| 303 |
- 2026-09-28 O10c done (oxygen#2, closed by the change), after splitting it. |
| 304 |
- **Measured first.** New `OXY_TIMINGS=1` phase timings, documented in the guide. At 10 000 files and 200 changes, loading the base state was about 790 of 1 230 ms. A checkpoint did not help: decoding it took 680 ms, like replaying the history, since both build the same in-memory graphs. |
| 305 |
- **Fixed without a format change:** |
| 306 |
- A seeded fast hasher for the graph maps (`fasthash.rs`, tested). |
| 307 |
- The predecessor map was written for every line and read nowhere; it is removed. |
| 308 |
- An inline edge set replaces a `BTreeSet` per vertex. |
| 309 |
- Lines are moved in with one lookup each. |
| 310 |
- **Result.** Both builds run alternately on the same load, medians: clean status 0.96 s to 0.52 s, one-file commit 0.99 s to 0.51 s, peak memory 657 MB to 239 MB (docs/benchmarks.md). |
| 311 |
- Gates green for oxygen and oxygenhub; release binary rebuilt; hub redeployed. |
| 312 |
- The rest is split into: |
| 313 |
- O10d: skip reading files that are clean by the manifest; next. |
| 314 |
- O10e: per-file checkpoints, a spec §25 format change. **Andy: this one waits for your agreement.** |
| 315 |
- Also this iteration: V6 to V8 had been installed into the Rust profile only; now into the default profile too, as the loop asks. |
| 316 |
- 2026-09-28 O10d done (oxygen#3, closed by the change): |
| 317 |
- A text file whose size and mtime match the manifest (outside the 2 s racy window), and whose recorded hash equals the base's rendering of it, takes the base's bytes instead of being read. |
| 318 |
- At 10 000 files, a clean status opens none of the files (checked with strace). |
| 319 |
- With a warm cache the walk falls only from about 75 to 56 ms, since traversal and stat remain. The gain is larger on a cold cache or a slow disk. |
| 320 |
- Test `stat_cache_takes_unchanged_text_from_the_base_and_misses_no_edit`, using unreadable files to prove what is and is not read: |
| 321 |
- an older uncommitted edit is always read, never replaced by the base; |
| 322 |
- a same-size edit made just now is caught by the racy window; |
| 323 |
- after a commit the edits become the base. |
| 324 |
- Spec §12.4. Gate green; release binary rebuilt. The hub (bare repositories) is unaffected. |
| 325 |
- Next is O10e, which waits for Andy's agreement to change the checkpoint format. |
| 326 |
- 2026-09-28 O10e done (oxygen#4, closed by the change). Spec §11, Draft 0.8j. |
| 327 |
- **Format.** Payload version 2: a file graph encoded in 2 KiB or more is a blob of its own, named from an index; smaller ones stay inline. Blobs are content addressed, so unchanged files are shared between checkpoints. |
| 328 |
- **Compatibility.** Readers accept both versions; writers produce version 2. gc keeps the graph blobs, fsck checks them against a full load in the payload's own version, and shallow-clone bundles carry them. |
| 329 |
- **Speed.** The first cut was slower than version 1, because it read 10 000 blobs one at a time. So the store gained `get_many`, which reads loose objects in parallel and packed ones in parallel after one index pass, and the graphs are decoded in parallel. |
| 330 |
- **Result at 10 000 files, 216 changes** (the same binary reading both versions, alternated, medians): load through a checkpoint 615 ms (v1) to 342 ms (v2). The index is 2.4 MB against a 62 MB v1 payload. A second checkpoint after a one-file edit took 0.63 s against 1.08 s. |
| 331 |
- **Tests.** Unit tests for the v2 round trip, determinism, missing blobs and unknown versions, and `get_many` over loose, packed and missing objects. A repository test covers a large file's graph blob through gc, fsck, a shared second checkpoint, the shallow bundle, an old v1 checkpoint still loading, and a lost blob failing loudly. |
| 332 |
- Gates green for oxygen and oxygenhub; release binary rebuilt; hub redeployed. |
| 333 |
- **Andy:** oxy older than this cannot read checkpoints written from now on. Rebuild every copy, as ~/.local/bin/oxy already is. |
| 334 |
- **New priority from Andy:** dogfood OxygenFlow on every push (F1–F5, ahead of O10f and O10g). |
| 335 |
- The runner is live on Earth, but only oxygenflow has a pipeline, of light shell jobs in a 256 MiB, one-CPU Alpine microVM. |
| 336 |
- No Rust or Node sandbox exists yet. |
| 337 |
- 2026-09-28 F1a done (oxygenflow#2, closed by the change). Andy said go ahead with the Earth work for F1. |
| 338 |
- **Mounts.** Jobs may now declare up to 4 ephemeral scratch mounts: |
| 339 |
- `ephemeral://tmpfs` uses guest memory; `ephemeral://disk` is a fresh sparse ext4 disk per job, attached as vdc onward. |
| 340 |
- Targets are clean paths strictly under /src or /tmp and may not nest; sizes run from 16 MiB to 64 GiB. Other sources and settings are refused as not in M0. |
| 341 |
- Mounts are part of the canonical job (field 6, only when present, so existing pipeline hashes are unchanged) and of the receipt environment. |
| 342 |
- `flow-init` creates mount points owned by the job's user. |
| 343 |
- **Deployment.** `make deploy-runner` builds `flow` and the static `flow-init`, installs them under /home/claude/flow keeping `.prev` copies, re-pins `flow-init` in flow.toml and restarts flow-runner. |
| 344 |
- **Checked on Earth** with `flow run` in a real microVM (no hub involved): the job (uid 1001) owns the mounts, a 2 GiB disk took a 300 MiB write, a 64 MiB tmpfs refused 100 MiB, and scratch was cleaned. The next real push of oxygenflow passed on the new runner. |
| 345 |
- Tests: 2 new flow-core tests (parsing, refusals, hash stability) and a runner test for guest device numbering. Gates green for oxygenflow and oxygenhub; hub redeployed (it validates pipelines with flow-core). |
| 346 |
- Andy also confirmed that only he and I use oxy, so there is no need for backward compatibility before the release. |
| 347 |
- 2026-09-28 F1b done (oxygenflow#3 and oxygen#6, closed by the changes). |
| 348 |
- **The build image** (`oxygenflow/images/rust`): |
| 349 |
- Rust 1.97.1 with rustfmt and clippy, and Node 24.21.0 (SHA-256 checked). |
| 350 |
- Every crate of the oxygen, oxygenhub and oxygenflow lock files, vendored, with cargo pointed at them via /.cargo/config.toml and `offline = true`. |
| 351 |
- Toolchain binaries are linked into /usr/local/bin, so no rustup is needed at run time. |
| 352 |
- `build.sh` builds it; Docker is only a file-system builder on the workstation (Andy agreed). Imported on Earth as `cas://blob/3ba0779a55b1d66fce10adf5a01585624c9aab089f590d228368c38fd3e65a46` (2.2 GiB). |
| 353 |
- Rebuild it when a Cargo.lock changes. |
| 354 |
- **Running oxygen's gate in it found three real problems:** |
| 355 |
- `flow-init` called glibc's `execvpe`, which searches PID 1's own (empty) PATH rather than the job's, so programs outside /bin and /usr/bin were not found. It now resolves them itself (tested). |
| 356 |
- Tests serving on 127.0.0.1 need a loopback interface. `network "loopback"` (already in the spec) is now in M0: `flow-init` brings up `lo`, with still no device leaving the guest (tested). |
| 357 |
- oxygen's `git_import_reproduces_trees` only passed where git has a user identity, because one merge lacked `-c user.name`. Fixed; it now also asserts the merge started. |
| 358 |
- **Result:** oxygen's full gate (fmt, clippy -D warnings, every test) passes in a microVM on Earth in 135 s cold, with 8 GiB, 4 CPUs and a 24 GiB disk mount. |
| 359 |
- Runner and hub redeployed; Earth scratch cleaned. |
| 360 |
- 2026-09-28 Andy agreed the order for his five ideas: F2–F5 first, then conflict resolution (P1, P1b), function history (P2), swimlanes (P3), signed receipts and the trust dashboard (P4, P4b), then web workspaces (P5, P5c). |
| 361 |
- Decisions from the discussion: |
| 362 |
- Web workspaces start from openvscode-server unmodified, not a VS Code fork. |
| 363 |
- Hub-side history rewrites are plans applied and signed by the author's client, only on proposal bookmarks. |
| 364 |
- Function history uses atom identity rather than the semantic-deps hook. |
| 365 |
- Andy asked the loop to carry straight through all of it. |
| 366 |
- 2026-09-28 F2 done (oxygen#6, closed by the change). |
| 367 |
- **The pipeline** (`.flow/pipeline.kdl`) runs on every push to oxygen: |
| 368 |
- `fmt` in a small sandbox (1 GiB, 1 CPU, isolated); |
| 369 |
- then `clippy -D warnings`, `test --workspace` and a release `build`, each with `--locked` in the build image (8 GiB, 4 CPU, loopback) with a 24 GiB scratch disk for target/. |
| 370 |
- **First run** (#6, passed): fmt 13 s, clippy 46 s, test 1 min 37 s, build 2 min 7 s; 4 min 44 s end to end. The runner runs one job at a time. |
| 371 |
- The jobs show as checks on the change. Required checks come with F5. |
| 372 |
- The run shows "site token" as its pusher: Andy asked why pushes are not attributed to him. They use the site push token from hub.toml, set up before accounts existed. |
| 373 |
- Waiting on Andy for a personal access token (Settings › Tokens, repo:write) to put in every repository's `earth` remote. |
| 374 |
- Then, with his OK, remove the site token from hub.toml. |
| 375 |
- 2026-09-28 Pushes now use a personal access token for Andy's account in every repository's `earth` remote, not the site push token, so the hub records them as andymccall. The site token stays in hub.toml until Andy agrees to remove it. |
| 376 |
- 2026-09-28 F3 done (oxygenhub#19, oxygenflow#4, oxygen#8, closed by the changes). Andy chose pinned subrepos. |
| 377 |
- **Subrepos.** oxygenhub pins oxygen and oxygenflow as subrepos in deps/ and builds against them. |
| 378 |
- `make pins` pulls them to main on Earth; the next commit records the new pins. |
| 379 |
- A Cargo `paths` override that kept local builds on the sibling checkouts warned that it will become a hard error, so there is none: local, CI and deploy all build the pins. |
| 380 |
- **OxygenFlow.** |
| 381 |
- Claims list the state's pinned Oxygen subrepos that the pusher can read, and the job token may read those (and nothing else). |
| 382 |
- The runner fetches and exports each at its path, inside the scope hash. |
| 383 |
- Hub test `claims_carry_pinned_subrepos_and_the_job_token_reads_only_those`. |
| 384 |
- **oxy fix.** `submodule add` probed URLs without OXY_TOKEN and OXY_CA, so a hub with its own CA, or a private repository, fell back to git. It now reports the Oxygen error too. |
| 385 |
- **Formatting.** `cargo fmt --all` in oxygenhub also formats the path dependencies in deps/, with the hub's style, since oxygen has no rustfmt.toml. The pipeline and the loop's hub gate use `cargo fmt -p oxygenhub -p hub-api`. |
| 386 |
- **First run** (#12, pushed as andymccall, passed): fmt 5 s, clippy 1 min 49 s, test 4 min 1 s, release build 8 min 26 s. The runner runs one job at a time → F6. |
| 387 |
- Also this iteration: removed fix.py, fix2.py and format_xenon.py from oxygen (Andy's request, oxygen#7). Answered Andy's VS Code question: the extension is installed in both profiles and only shows in an Oxygen repository. |
| 388 |
- 2026-09-28 F4a done (oxygenflow#5, oxygenhub#19, closed or referenced by the changes). |
| 389 |
- **Pinning.** oxygenflow pins oxygen at deps/oxygen, with `make pins` and a README note. |
| 390 |
- **oxygenflow's pipeline** adds fmt (this workspace only), clippy -D warnings and tests on the build image, before its summary. First run #14: all 8 jobs passed. |
| 391 |
- **The hub's layout had to change.** oxygenflow's flow-core now points at its own deps/oxygen, so the hub keeping a second oxygen pin would give Cargo two different oxy-core packages. The hub now pins only oxygenflow and builds against deps/oxygenflow/deps/oxygen. |
| 392 |
- The chain for newer code: push oxygen, then `make pins` in oxygenflow and push it, then `make pins` in oxygenhub. |
| 393 |
- **Nested subrepos.** Hub claims follow subrepos of subrepos, parents first, joined paths, up to 3 deep, with the same read check. The hub test covers a pin inside a pin. |
| 394 |
- **Result.** oxygenhub run #15 fetched deps/oxygenflow and deps/oxygenflow/deps/oxygen and passed: fmt 2 s, clippy 1 min 37 s, test 3 min 44 s, build 6 min 25 s. |
| 395 |
- **Also fixed** for Andy: `oxy identity export` failed in a new repository, because identity lookups ignored the key directory. It now falls back to it, checked against the key, and the help names the file it writes (oxygen#9). |
| 396 |
- 2026-09-28 F4b done (oxygen-vscode#5, closed by the change; oxygenhub-vscode references it). |
| 397 |
- **Image.** The build image (now `85c744e5…`) also carries an npm cache of both extensions' lock files. `npm ci --offline --cache /opt/npm-cache` installs from it read-only; checked locally before building. |
| 398 |
- oxygen, oxygenhub and oxygenflow still pin the previous image `3ba0779a…`; they move on their next re-pin. |
| 399 |
- Rebuild the image when a package-lock.json or Cargo.lock changes. |
| 400 |
- **oxygen-vscode** pins oxygen as deps/oxygen (kept out of the .vsix). Its `check` job builds oxy from the pin on a scratch disk and runs typecheck, bundle and the 18 tests against it; then `package`. |
| 401 |
- **oxygenhub-vscode:** check and package. Its hub-client tests skip in the VM, since there is no hub binary there. |
| 402 |
- **First runs:** `package` failed in both, because a job starts from a fresh workspace and had no dist/. It now builds first. |
| 403 |
- **Green:** oxygen-vscode #21 (check 1 min 8 s, package 6 s), oxygenhub-vscode #22 (check 7 s, package 5 s). oxygenflow #20 passed all 8 jobs. |
| 404 |
- All five repositories are now linted, tested and built by OxygenFlow on every push. |
| 405 |
- 2026-09-28 F5 done (oxygenhub#20, referenced by the changes). |
| 406 |
- **Required checks over the API.** They could only be set from the settings page, so the hub gained `GET` and `PUT /api/v1/repos/{o}/{n}/checks/required` (admins, the whole list, audited). Tested; TS regenerated; hub deployed. |
| 407 |
- **Required checks set on every repository:** |
| 408 |
- oxygen and oxygenhub: flow/fmt, clippy, test, build; |
| 409 |
- oxygenflow: flow/fmt, clippy, test, summary; |
| 410 |
- both extensions: flow/check, package. |
| 411 |
- Proposals cannot land on main without them. |
| 412 |
- **Waiting for green.** |
| 413 |
- `oxy push --json` now reports the state each bookmark was pushed to (tested). |
| 414 |
- New `flow wait <repo URL> <state>` follows that state's checks on the hub against the required ones: exit 0 passed, 1 failed (a failed check, or a skipped required one), 2 at the time limit. Unit-tested. |
| 415 |
- First real use: it followed oxygenflow's own run for the change that adds it, 8 checks from queued to passed, and exited 0. |
| 416 |
- **From now on the loop pushes and waits** with `flow wait` after the local gate, and a red run is fixed before an item counts as done. Recorded in my working notes; Andy's loop prompt file is unchanged. |
| 417 |
- F1 to F5 (dogfooding) are done. F6 (faster runs) is next in the F group; then P1 and the rest. |
| 418 |
- 2026-09-28 O10f done (oxygen#10, closed by the change; OxygenFlow run green, followed with `flow wait`). |
| 419 |
- **History-only states.** `Repo::load_state_history` keeps changes and tree but builds no file graph. |
| 420 |
- From a version 2 checkpoint, graphs stay as pending blob refs or inline values. |
| 421 |
- By replay, each change's edits wait per file. |
| 422 |
- `load_graphs` builds the graphs asked for, in one blob batch, identical to a full load (tested against full payloads, by replay and from a checkpoint with later changes). |
| 423 |
- An unloaded graph panics with a clear message rather than reading as empty, and `materialize` refuses a history-only state (both tested). |
| 424 |
- **Used by** `log`, `log_state`, `log_for_path_in` and `blame_in` (one graph). |
| 425 |
- **At 10 000 files:** the log's state load 270 → 37 ms, `oxy log` 0.89 → about 0.7–0.8 s. The rest is the working-copy snapshot the log takes first (O10g). CLI `blame` works from the snapshot, so it is unchanged (0.6 s). |
| 426 |
- **Andy asked:** |
| 427 |
- Why his pushes show "site token": his repositories metalgear, rstar-api and snake-oxy use hub.toml's site push token (c085…). Told him to make his own personal token and set it with `oxy config remote.earth.token`. Removing the site token waits for his OK. |
| 428 |
- The oxy equivalent of git-flow: bookmarks and `oxy merge` (a union, no merge change), proposals, tags. Answered. |
| 429 |
- 2026-09-28 F6a done (oxygenflow#6, oxygenhub#21, closed by the changes). |
| 430 |
- **The runner** runs up to `max_jobs` jobs at once within a memory and vCPU budget (flow.toml). |
| 431 |
- It reports its free room when it claims, and the hub hands out only a job whose sandbox fits; a lone job may use the whole budget. |
| 432 |
- Preparing workspaces stays one at a time: shared repository caches, and the first clone's token in the environment. |
| 433 |
- Unit-tested budget; the hub was deployed before the runner. |
| 434 |
- **Measured honestly: parallel Rust jobs are slower on Earth's 4 cores.** Oxygen's run took 345 s with clippy beside test and build (clippy 46 s → 1 min 53 s, build 2 min 7 s → 4 min 49 s), against 284 s one at a time. |
| 435 |
- Earth's budget is therefore max_jobs 3, 24 GiB and **5 vCPUs**: one 4-vCPU Rust job at a time, with 1-vCPU and short jobs beside it. |
| 436 |
- The real speed-up is F6b (prebuilt dependencies, release without debug info). |
| 437 |
- **Found on the way:** OxygenFlow log appends failed with HTTP 500 "database is locked", losing log lines. |
| 438 |
- Cause: the hub's write transactions were deferred and read before writing, which SQLite fails at once whatever the busy timeout. |
| 439 |
- All 9 now begin IMMEDIATE (`db::begin_write`). A test of 16 concurrent appends fails with the old code and passes now. |
| 440 |
- Deployed; the next oxygenhub run passed with no new lock errors in the runner's log. |
| 441 |
- **Andy asked** whether an `oh` command, like GitHub's `gh`, should be its own repository. Recommended a crate in the oxygenhub workspace, since it shares hub-api and can test against a real hub. Asked where in the backlog he wants it; no answer yet. |
| 442 |
- 2026-09-28 O10g, first part, done (oxygen#11, closed by the change; OxygenFlow run green, followed with `flow wait`). Spec §12.4, Draft 0.8k. |
| 443 |
- **Graph keys.** `State::graph_key` hashes a file's node, its checkpoint graph (blob, inline value or none) and the set of later changes that edited it. Equal keys mean equal graphs, and so equal renderings. |
| 444 |
- A test caught my first version, which left the node out: two files edited only by the same change got the same key. |
| 445 |
- **What the manifest records.** Per text file found equal to the base's unconflicted rendering, the key it matched. |
| 446 |
- When a file's stat and key both still match, the snapshot neither reads nor renders it. |
| 447 |
- `.oxyattributes` files are always read. When any of them is added, removed, edited or changed in the base, nothing is skipped. |
| 448 |
- **`Repo::snapshot_status`** gives a partial snapshot: a history-only base with only the other graphs built, and a `Snapshot.unrendered` list. |
| 449 |
- Used by status, log, show, tag, bookmark create/set, the default head, and `pull --json`. |
| 450 |
- `snapshot` (commit, diff, amend…) still builds the whole base. Its graphs now load in parallel. |
| 451 |
- **Tests:** 3 new; 157 core tests pass, clippy is clean. |
| 452 |
- **At 10 000 files:** a clean status takes 0.22 s instead of 0.55 s, and 51 MB instead of 259 MB. Log takes 0.38 s instead of about 0.75 s. Diff and commit are unchanged at about 0.57 s. |
| 453 |
- **Split:** O10h (a lazy base for commit and diff, and cached materialized states) is added to the backlog. |
| 454 |
- 2026-09-28 **Metalgear feedback.** Andy's other project reports what it finds in `~/oxygen_feedback.md`; I watch it and act on it. Its items are M1 to M10, placed ahead of P. |
| 455 |
- **M1 done** (oxygen#12). Pushing a bookmark with `/` in its name failed with "No such file or directory". |
| 456 |
- Cause: reference attestations and checkpoint pointers are files named after the bookmark, and nothing created the subdirectory. |
| 457 |
- They are now one flat file each, with `/` escaped (`%2F`), so `feature` and `feature/x` coexist. |
| 458 |
- A test pushes and checkpoints both. |
| 459 |
- Pinned through oxygenflow into oxygenhub and deployed. All three OxygenFlow runs are green. |
| 460 |
- **M2 done** (oxygenhub#22, Andy's request). Settings → OxygenFlow has on, off or site default (hub.toml's `[flow] repos`), and needs no restart. |
| 461 |
- Site administrators change it; repository admins only see it. |
| 462 |
- Audited as `repo.flow`; migration 0018. |
| 463 |
- Two tests; hub gate green; deployed; run green. |
| 464 |
- **Andy asked urgently** to enable Flow for doomsdayone/metalgear. |
| 465 |
- My edit of hub.toml on Earth was refused by the permission check, so I did not make it. |
| 466 |
- The line `"doomsdayone/metalgear"` is now in `[flow] repos`, added outside this loop, and run #37 there passed. |
| 467 |
- From now on the new setting does this without editing hub.toml. |
| 468 |
- 2026-09-28 **M11 done** (oxygenhub#23; Andy asked for it urgently). Flow runs are numbered per repository. |
| 469 |
- Run list, run page (`/flow/{number}`), live view, stamp and check links all use the number. |
| 470 |
- Migration 0019 numbered the existing runs per repository in creation order and rewrote the links of existing flow checks. |
| 471 |
- I tried it first on a read-only copy of Earth's runs and checks: 160 of 185 links changed, and every link names a run of its own repository. |
| 472 |
- Live: metalgear's run is #1, oxygen's #1 to #19. Hub run green. |
| 473 |
- 2026-09-29 **M12 done** (oxygen#13, oxygenhub#24; Andy asked, and feedback 14a). |
| 474 |
- **`oxy push REMOTE --delete NAME`** sends an update to the zero state. |
| 475 |
- Checked like any update: the bookmark must exist at `old_state`, and a policy needs an authorized key. |
| 476 |
- The deletion's attestation stays the bookmark's latest, so an old one cannot be replayed to bring it back. |
| 477 |
- So a bookmark new to a remote starts its serial at the clock in milliseconds. |
| 478 |
- The local bookmark is kept. |
| 479 |
- **The default bookmark is never deleted.** |
| 480 |
- A non-bare repository protects its checked-out bookmark. |
| 481 |
- For a bare one, the host says which is the default through `Backend::default_bookmark`: `oxy serve` uses the configured bookmark, the hub its setting. |
| 482 |
- **Hub:** a DELETE button (with a confirmation) for writers on every bookmark but the default. It goes through the same checks, is logged on the pushes page and sent to webhooks as a push. |
| 483 |
- **Tests:** core, HTTP and hub, one each. Spec §16.4 (Draft 0.8l) and hub spec updated. Deployed; all three OxygenFlow runs green. |
| 484 |
- metalgear's `flow-first-run` can now be deleted; I left that to its project. |
| 485 |
- **Feedback file:** new items 3a and 7a became M13 (`bookmark switch --carry`) and M14 (`oxy show` summary lists every kind). |