OXYGENHUB
# e1dc6d6aad4f oxygen / docs / backlog-overnight-2.md
37.9 KiB · Markdown · last changed Backlog: O10c done (oxygen#2); O10d and O10e split out 1 month ago renderedsource blame history raw

Overnight backlog 2 (2026-09-28 to 2026-09-30)

Andy's brief: usability and features on Oxygenhub, as many features as possible in the VS Code extensions, and a stable, mature oxy client. Work top to bottom, but interleave the three tracks: after each hub or extension item, take the next oxy stability item if one is open. Each item is done when its tests pass, the gate is clean, it is committed with oxy as Andy and pushed to Earth. Log each iteration at the bottom.

Hub (oxygenhub): usability and features

  • 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).
  • H2 Empty-repository quickstart: exact commands for this repository (remote add, token, CA download, push) and for starting from nothing.
  • H3 Theme switch (dark, light, auto) in the header and under Settings, remembered in the cookie; check every page in light mode.
  • H4 Profile and organisation pages: generated identicon avatar, display name, bio, linked keys, repositories, recent activity, organisation members and teams.
  • H5 Diff view: split (side by side) toggle remembered per user, collapse per file, hide whitespace changes, intra-line word highlighting.
  • H6 Forks: fork button, fork creation (store copy, forked_from), fork list, "propose to upstream" from a fork.
  • H7 Stars and watch counts, explore sorting (activity, stars, name) and filtering, repository topics.
  • H8 Proposal and issue lists: filters (author, label, target, assignee), sort orders, pagination, counts in tabs.
  • 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".
  • H10 Compare page with bookmark and tag pickers and a "propose this" button.
  • H11 Help pages: getting started (install oxy, clone, push, tokens, CA, delegating to the hub), keyboard shortcuts, glossary for people coming from GitHub.
  • H12 Password reset and email verification (email when SMTP is set; admin-issued reset link otherwise).
  • H13b Uploaded profile pictures and organisation logos (Andy, oxygenhub#11): upload, validate by content, re-encode and crop, identicon fallback, remove.
  • H13 Notifications: per-thread mark read, filter by kind and repository, "participating" versus "watching".
  • H14 Accessibility and mobile pass: focus styles, skip link, landmarks, responsive tables and tabs, contrast in both themes.
  • H15 Organisation webhooks page; admin storage and repository maintenance page (sizes, oxy gc).
  • H16 Performance: cache rendered Markdown and highlighted files by object hash; measure the heaviest pages on Earth before and after.
  • H17 Issue API: labels, assignees and milestone on create/edit; create labels (oxygenhub#3).
  • H18 refs #N in change messages appears on the issue's timeline (oxygenhub#4).
  • H19 Projects: boards of issues and proposals across an owner's repositories (oxygenhub#5).
  • H21 Projects API: list, create, board, cards and columns over /api/v1 (oxygenhub#15).
  • H20 Issue Markdown resolves against the default bookmark, not main (oxygenhub#6).

VS Code (oxygen-vscode, oxygenhub-vscode)

  • V1 oxygenhub-vscode: proposal files in native diff editors (target state against projected state through a content provider), with review threads in them.
  • V2 oxygenhub-vscode: notifications count in the status bar and an inbox quick pick that opens the item.
  • V3 oxygenhub-vscode: completion of #N and @user in the Oxygen commit box and in comments; hover on #N shows the title.
  • V4 oxygenhub-vscode: create an issue from the editor selection or a TODO line, with a permalink.
  • V5 oxygenhub-vscode: releases view; draft a release from a tag.
  • V6 oxygen-vscode: history graph actions for split, squash and rebase (oxy history commands), with confirmation and undo hint.
  • V7 oxygen-vscode: commit box polish: amend toggle, message history, signed/unsigned indicator, warning on conflict markers.
  • V8 oxygen-vscode: Open on Remote in editor gutter context (line permalinks), copy permalink.
  • V9 Both: README screenshots-free walkthroughs, settings documentation, CHANGELOG per release.

oxy: stability and maturity

  • O1 Property tests (proptest) that applying the same changes in any order gives the same state, over random edit sequences on several files.
  • O2 oxy fsck: verify every object's hash, every change's references and signatures, the op log chain, and report precisely.
  • 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.
  • O4 Benchmarks with thresholds: 10 000 files, 5 000 changes; status, commit, log, blame, push, clone; record numbers in docs.
  • 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.
  • 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).
  • 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).
  • 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.
  • 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.
  • O10e Checkpoints readable per file so a snapshot loads only the file graphs it needs; cache materialized states. Changes the checkpoint format (spec §25): needs Andy's agreement before starting.
  • O5 Concurrency: two processes committing, snapshotting and pushing on one repository; locks hold, nothing is lost.
  • O6 Error messages audit: every user-facing error says what to do next; oxy doctor checks identity, config, remotes, CA files, hub reachability.
  • O7 Shell completions (oxy completions bash|zsh|fish) and man pages generated from clap.
  • 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.
  • 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.
  • O9 Portability review for macOS and Windows: path separators, executable bits, case-insensitive file systems, CRLF; tests where it can be exercised on Linux.

Log

  • 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.
  • 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).
  • 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.
  • 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.
  • 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.
  • 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).
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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).
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 2026-09-28 Requests from Andy (between items):
    • OxygenHub naming. The name is written OxygenHub everywhere people read it (hub, both extensions: oxygenhub-vscode 0.6.1, oxygen-vscode 0.7.1).
    • OxygenFlow M0, built and running on Earth. Xenon was renamed to OxygenFlow, in the separate oxygenflow repository. It has:
      • flow-core: KDL pipeline, validation with lines, canonical CBOR, receipts, runner API types
      • flow-init, as the guest's PID 1
      • the Firecracker microVM tier
      • flow check/run/rootfs/runner
      • a microVM conformance test
    • 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.
    • 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).
    • Result. oxygenflow's own pipeline passes (runs 1 and 2). Andy's spec is committed, renamed to OxygenFlow, with a README.
    • oxy-core. tls_client_config is public.
  • 2026-09-28 Flow state stamps instead of CI badges (oxygenhub#1, closed by the change):
    • /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.
    • In READMEs, [![flow](.flow/stamp)](.flow/run) resolves against the state being viewed.
    • Fixed oxygenhub#2 on the way: README images pointed at the HTML file page. There is now a /raw/{ref}/{path} route.
    • The oxygenflow README shows its stamp (oxygenflow#1).
    • From now on every item gets an issue on Earth and its commits say closes/refs #N.
    • Gaps the workflow exposed became H17 to H20 (oxygenhub#3 to #6).
  • 2026-09-28 H10 done (oxygenhub#7, closed by the change):
    • 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.
    • Propose this shows for writers when both sides are bookmarks and head has changes; it becomes "proposal #N is open" when one exists.
    • Tests in browse and proposals. Hub spec §6.3. Deployed.
  • 2026-09-28 H11 done (oxygenhub#8, closed by the change):
    • /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.
    • Every command carries this hub's address, remote name and certificate step.
    • It is linked from the band, the footer and the ? sheet.
    • Browse test; hub spec §6.3. Deployed.
  • 2026-09-28 H12 done (oxygenhub#9, closed by the change). Spec §5.1.1 first.
    • Account links. 32 random bytes, stored as a BLAKE3 hash, single use and expiring. Migration 0012.
    • 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.
    • 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.
    • Reset by an administrator. From /admin (24 h, shown once, audited).
    • Using a reset link. It rotates the session salt (every session ends) and voids other links. TOTP stays on; banned accounts are refused.
    • Mail. A shared mail module with file:// for development. Notification mail only goes to verified addresses.
    • Tests. tests/recovery.rs covers the whole flow through a file mailbox.
    • Earth. Earth has no SMTP, so resets there go through the admin link. Existing addresses count as unverified until verified.
  • 2026-09-28 H13 done (oxygenhub#10, closed by the change):
    • Notifications record their thread, repository and reason (participating or watching) and can be marked done. Migration 0013 backfills threads and repositories from the URLs.
    • The inbox shows one row per thread, with updates and new counts.
    • Filters: inbox, unread and done views, reason, kind family and repository, with counts.
    • Per-thread read, unread and done. Done threads return when a new event arrives.
    • Opening a row marks its thread read.
    • notify::add takes a struct.
    • Tests extended in tests/issues.rs. Hub spec §10.
    • The API keeps per-item read; per-thread actions in the API are a follow-up for the VS Code inbox.
  • 2026-09-28 Andy asked for uploaded profile pictures and organisation logos: added as H13b (oxygenhub#11), taken next.
  • 2026-09-28 H13b done (oxygenhub#11, closed by the change):
    • Upload a picture under Settings › Profile › Picture, or an organisation logo under its settings (owners only).
    • PNG, JPEG or WebP, recognised by content; at most 5 MiB and 8 000 px a side.
    • 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.
    • /avatar/{name} serves the picture with an ETag, falling back to the identicon; Remove restores the identicon.
    • Avatar CSS no longer pixelates photos.
    • Unit tests for processing and refusals; end-to-end test in tests/accounts.rs. Hub spec §6.3. Deployed.
    • 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.
  • 2026-09-28 H14 done (oxygenhub#12, closed by the change):
    • 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.
    • Contrast: WCAG AA in both themes, enforced by tests/contrast.rs over the CSS variables.
      • Light theme: mute, acc, ok, now and the string, function and comment syntax colours adjusted.
      • Dark theme: comment colour adjusted.
      • Text no longer uses --dim.
    • 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.
    • Checked at 390 px on Earth. Hub spec §6.13.
  • 2026-09-28 H15 done (oxygenhub#13, closed by the change):
    • Organisation webhooks. A Webhooks section in organisation settings: add, ping, enable or disable, delete, deliveries and redeliver.
      • Repository and organisation webhooks now share one module (web/webhooks.rs) and one template macro.
      • Every action checks that the hook belongs to the scope it is managed from.
    • 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.
    • Tested in tests/accounts.rs. Hub spec §6.3, §12.2. Deployed.
    • Earth's repositories can be maintained from /admin/storage now; I have not run it on Andy's data.
  • 2026-09-28 H16 done (oxygenhub#14, closed by the change). Measured on Earth first (oxygenhub/docs/performance.md).
    • 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.
    • 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.
    • ?plain=1 takes the lean path.
    • Warm views: spec.md 196 → 8 ms, repo/mod.rs 400 → 18 ms, state.rs 317 → 14 ms, blame 428 → 23 ms.
    • Unit test for the LRU; browse test checks the second view hits the cache. Hub spec §16.3.
    • Follow-up for oxy-core: log_for_path_in should reuse a loaded state instead of rebuilding it.
  • 2026-09-28 O12 done (oxygen#1, closed by the change; the first issue in the oxygen repository):
    • 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.
    • log_for_path_in and blame_in wrap them and load once instead of twice.
    • Test history_and_blame_of_a_loaded_state_match_the_full_walk; gate green; release binary rebuilt.
    • The hub uses them for last change, blame and tree rows.
    • 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).
  • 2026-09-28 H17 done (oxygenhub#3, closed by the change):
    • POST /issues takes labels, assignees and a milestone (triage). Unknown names are refused before anything is created.
    • 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.
    • POST /labels creates or changes a label and DELETE /labels/{name} removes it (triage).
    • 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.
    • Dogfooding:
      • Each Earth repository now has the labels bug, feature, performance, accessibility, security and gap.
      • Every issue so far is labelled.
      • The work log's issue helper sets labels from now on.
  • 2026-09-28 H18 done (oxygenhub#4, closed by the change):
    • Any bare #N in a pushed change's message, on any bookmark, adds "referenced by change on " to that issue's or proposal's timeline, with the hash linked.
    • closes #N still records only "closed by change"; owner/name#N from other repositories is left alone.
    • refs::references has a unit test; the issues end-to-end test covers issue and proposal timelines. Hub spec §8.
    • From now on, the refs #N lines in work commits show on the Earth issues.
  • 2026-09-28 H19 done (oxygenhub#5, closed by the change). Spec §8.2 first.
    • Projects belong to a person or organisation: /projects/{owner} and /projects/{owner}/{n} ("projects" is now a reserved name).
    • 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).
    • 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.
    • Visibility: public or private (owner, organisation members and admins). Cards from repositories the viewer cannot read are not shown at all.
    • A closed project is read-only; deleting one leaves the issues.
    • Issue and proposal pages list their projects; profiles and organisation pages link to them.
    • Migration 0015. Test project_boards plus reference parsing. Screenshot-checked on a scratch hub.
    • The screenshot tool now disables the browser cache (an earlier shot had served a stale style.css).
    • Gap found: no projects API → H21 (oxygenhub#15).
  • 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.
  • 2026-09-28 Webhooks, from Andy's report (oxygenhub#16 and #17, closed by the change):
    • Why Discord got nothing. Every delivery, ping included, was the OxygenHub envelope, which Discord rejects ("Cannot send an empty message").
    • 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.
    • Settings.
      • 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.
      • The table shows the last delivery result.
      • Each hook has an edit form; a blank URL or secret keeps the current one.
    • Also found. Issues opened, commented on, closed or reopened in the web interface emitted no webhook events at all; now they do.
    • Tests. Unit tests for the formats and masking; end-to-end test webhook_formats_editing_and_web_issue_events. Hub spec §12.2.
  • 2026-09-28 H21 done (oxygenhub#15, closed by the change):
    • /api/v1/projects/{owner}: list and create.
    • /api/v1/projects/{owner}/{n}: the board as the caller may see it, and PATCH.
    • /cards: add a reference or note; columns by id or name.
    • /cards/{id}: move or remove.
    • 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.
  • 2026-09-28 Repository overview, from Andy's review (oxygenhub#18, closed by the change):
    • The heads and conflicts strips sit inside the main column.
    • Clone is a button beside search: a popup with a selectable command, copy, and the bare URL.
    • 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".
    • 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.
    • A public, internal or private badge by the repository name.
    • Tests in browse.rs and a unit test for detection. Screenshot-checked on Earth.
    • Andy's key is not linked to his account yet (Settings › Keys), so his contributor entry is not a link.
  • 2026-09-28 V6 done (oxygen-vscode#1, closed by the change). oxygen-vscode 0.8.0:
    • The History graph's change picker, and a right-click menu on its rows (webview context menus), offer:
      • Split for heads with several files: pick files; they become the first change.
      • Squash into a dependency, picked when there are several.
      • Rebase onto a bookmark, or a change or tag typed in.
    • Each asks first, then shows oxy's summary with an Undo button (oxy undo).
    • Client methods split, squash and rebase, tested against the real oxy with undo. check green (9 tests); packaged and installed into the Rust profile.
  • 2026-09-28 V7 done (oxygen-vscode#2, closed by the change). oxygen-vscode 0.9.0:
    • Amend mode (pencil or close icon in the Source Control title bar): Ctrl+Enter amends; the box starts from the last change's message.
    • Previous Commit Messages: remembered per repository in workspace state, 30 kept.
    • Signing indicator: the placeholder says signed, sealed at push, or unsigned, read from oxy identity show at most every 30 s.
    • 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.
    • Pure logic in src/commitbox.ts with unit tests; identity() tested against the real oxy. check green (14 tests); installed into the Rust profile.
  • 2026-09-28 V8 done (oxygen-vscode#3, closed by the change). oxygen-vscode 0.10.0 and oxygenhub-vscode 0.6.2:
    • The line-number gutter (editor/lineNumber/context) and the editor context menu offer Open on Remote, Copy Remote Link and Copy Permalink.
    • The link covers the clicked line or the selection (#L10-L20).
    • 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.
    • A note appears when uncommitted edits may shift the lines, or when remote links point past unpushed changes.
    • 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.
  • 2026-09-28 V9 done (oxygen-vscode#4, closed by the change):
    • Both READMEs rewritten as walkthroughs by task, each claim checked against the code:
      • Oxygen: commit, conflicts, sync, history rewriting, bisect, line links, trust.
      • OxygenHub: propose, review, land, issues, releases, notifications.
    • Each README has a Settings table.
    • Every CHANGELOG entry is dated from the history.
    • 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).
  • 2026-09-28 O10c done (oxygen#2, closed by the change), after splitting it.
    • 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.
    • Fixed without a format change:
      • A seeded fast hasher for the graph maps (fasthash.rs, tested).
      • The predecessor map was written for every line and read nowhere; it is removed.
      • An inline edge set replaces a BTreeSet per vertex.
      • Lines are moved in with one lookup each.
    • 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).
    • Gates green for oxygen and oxygenhub; release binary rebuilt; hub redeployed.
    • The rest is split into:
      • O10d: skip reading files that are clean by the manifest; next.
      • O10e: per-file checkpoints, a spec §25 format change. Andy: this one waits for your agreement.
    • Also this iteration: V6 to V8 had been installed into the Rust profile only; now into the default profile too, as the loop asks.