# Current

> Keep this file short. One active step, one ordered backlog. Completed work moves to
> [progress.md](progress.md). If this file starts reading like a changelog, it has
> drifted — that's exactly what went wrong last time.

## Active: Milestone 5 — Repo browsing

**Goal:** a repository's contents are readable on the web — tree, blob, commit log — so
a visitor can look at code without cloning it. The first milestone where the profile
starts to look like a portfolio rather than a list of names.

**Not planned yet.** Steps get laid out at the start of the milestone rather than
guessed at the end of the previous one. Two things are already known and should shape
that plan:

- **Start with domain value objects** — `ObjectId`, `RefName`, `TreeEntry` — before any
  adapter. A query port returning `String`s is an anaemic pass-through that pushes
  validation into the page, which is what
  [0006](decisions/0006-git-binary-behind-narrow-ports.md) rejected in advance.
- **Measure fork/exec per page view first.** Milestone 3 paid it once per repository
  creation and Milestone 4 once per git request; browsing would pay it several times per
  page. This is the point where `gix` for the read path gets reconsidered, and 0006 says
  to make that call with a measurement rather than an intuition.

### Open

- **Whether writing (Milestone 6) should come first.** The ladder puts browsing next, but
  the vision is portfolio-first and writing is the more distinctive feature. Browsing is
  the more expected one. Worth a moment's thought before starting rather than after.

### Carried over — small, unblocked

- **A client that disappears mid-request leaves the body-copy task waiting.** The copy
  into git's stdin runs in its own task and nothing cancels it if the connection drops.
  Bounded by the backend exiting and closing the pipe, but not by anything deliberate.
- **`REMOTE_USER` is not set on the backend**, so a push is recorded in the repository's
  reflog without naming who made it. Steid knows the actor by then; it simply is not
  passed through. Small, and worth doing before anything reads reflogs.
- **No rate limiting on token authentication.** A token is 256 bits so guessing is not
  the worry; unbounded hashing on an open endpoint is.
- **Tokens have no expiry and no last-used timestamp.** Both deliberate omissions for
  now — see [0007](decisions/0007-tokens-over-http-basic.md) — but a token list with no
  "last used" makes it hard to know which are safe to revoke.
- **A subprocess per git request.** Unlike Milestone 3's once-per-creation, this is on a
  hot path and has not been measured. Milestone 5 is where that bill comes due.
- **Streaming is by construction, not by measurement.** The response body is never
  collected, but no clone large enough to prove it has been run.
- **An orphaned repo directory is possible** if the process dies between the record
  write and the filesystem write, and it then blocks re-creating that name. The durable
  fix is a reconciliation sweep on boot
  ([architecture.md](architecture.md#db-plus-filesystem-writes)); clearing one is a
  manual `rm` today, since repo deletion does not exist.
- **The duplicate-name check races.** The loser is caught by `init_bare` or the unique
  constraint, but surfaces as an opaque storage error rather than "name taken".
- **Bare repos created on macOS carry `ignorecase = true`.** A migration gotcha if the
  data directory ever moves to Linux.
- **Fonts are not loaded.** The theme names Geist and IBM Plex Mono; both fall back
  today. Topcoat's `font-fontsource` feature handles it.
- **Light mode is untested.** The palette defines it; nobody has looked at it.
- **No rate limiting** on `/auth/login` or `/auth/setup`.
- **`sweep_expired` is never called**, so expired session rows accumulate. Expiry is
  enforced on read, so this is tidiness, not a hole.
- **CSRF.** `SameSite=Lax` covers the common case. Forms now exist, so this is decidable
  rather than hypothetical.

## Backlog

Ordered. Pull from the top.

1. **Milestone 6 — Writing.** Posts, markdown, `/{handle}/posts/{slug}`. Still open
   whether writing or projects/showcases is the better first portfolio feature.

## Open questions

- **Topcoat is early** (v0.5.0, first released 2026-07-22, breaking changes expected
  by its own authors). Expect churn that isn't feature work.
- Topcoat ships Tailwind without Node, which reopens the design system attempt #1
  dropped purely to avoid an npm build step — see [ui.md](ui.md).

## Routing findings (Milestone 0)

- **Topcoat 0.5 requires rustc ≥ 1.95.** On an older toolchain `cargo add topcoat`
  silently resolves to an empty `topcoat v0.0.0` placeholder instead of failing. Local
  stable is now 1.97.1.
- `Router::builder().discover()` collects `#[page]`-annotated items **at link time**,
  so pages can live in any module. Layering is our choice, not the framework's.
- `module_router!` derives each URL from the module tree rather than a path string.
  Still deferred. Application routes now group cleanly (`auth/login`, `api/me`), but
  handles sit at the root ([0004](decisions/0004-root-handles-grouped-routes.md)), so a
  parameterised root segment still has to coexist with static ones. Worth checking how
  `module_router!` handles that before committing to it.
- Path and query params are read from `Cx` via `path_param!` / `#[query_params]`, not
  injected as handler arguments. Parses are memoized per request.
- Layouts wrap by path prefix and nest outermost-first, and a layout can catch a page's
  `NotFoundError` to render a branded 404.
- `HOST` / `PORT` configure the bind address, so `STEID_LISTEN_ADDR` is gone.
- `Body` is a boxed `http_body::Body` used for both requests and responses, with
  `into_data_stream()` to read and `Body::new()` to wrap a stream — pack data can
  stream both directions without buffering. This is what makes Milestone 4 viable.
