steid

@jamesgill /

steid/plans/current.md
6.7 KBCode·Blame·Raw
ab7fea9chore: plans setup1mo
1# Current
2
3> Keep this file short. One active step, one ordered backlog. Completed work moves to
4> [progress.md]progress.md. If this file starts reading like a changelog, it has
5> drifted — that's exactly what went wrong last time.
6
2eb8681docs: put git next, plan the repo model24d
7## Active: Milestone 3 — Repo model
8
9**Goal:** repositories exist as records and as bare git repos on disk, and they appear
10on the profile. No git protocol yet — that is milestone 4. This milestone fills the
11Repositories section and gets the storage layout right before anything serves it.
12
13**Out of scope:** clone, push, browsing a tree, README rendering, forks, stars.
14Deleting a repo — worth having, but it makes the filesystem/database consistency
15problem twice as interesting, so not in the first pass.
16
17### Steps
18
c6d74a8docs: record the repo model decisions24d
19- [x] Domain: `RepoId`, `RepoName`, `Visibility` (Public/Private), `Repository`
c5b3ff5feat: repository persistence24d
20- [x] Domain: `RepoRepository` port — `find_by_id`, `find_by_org_and_name`,
2eb8681docs: put git next, plan the repo model24d
21 `list_by_org`, `save`
c5b3ff5feat: repository persistence24d
22- [x] Infrastructure: in-memory + SQLite implementations, migration
2eb8681docs: put git next, plan the repo model24d
23- [ ] Application: `GitStorage` port — `init_bare`, `repo_path`
24- [ ] Infrastructure: `DiskGitStorage`, shelling out to `git init --bare`
25- [ ] Application: `create_repo` use case — owner only, validates, creates record and
26 bare repo
27- [ ] Application: `list_repos` / `view_repo` read models — visibility-aware
28- [ ] Web: `/{handle}/repos/new` form, `/{handle}/repos/{name}` page
29- [ ] Web: the profile's Repositories section lists what the viewer may see
30- [ ] `/api/users/{handle}/repos`
ab7fea9chore: plans setup1mo
31
32### Done when
33
2eb8681docs: put git next, plan the repo model24d
34The owner creates a repo through the UI, a bare repo appears at
35`{data_dir}/{handle}/{name}.git`, and it is listed on the profile. A private repo is
36invisible to a signed-out visitor. `git clone` does **not** work yet — that is
37milestone 4.
38
c6d74a8docs: record the repo model decisions24d
39### Settled
2eb8681docs: put git next, plan the repo model24d
40
c6d74a8docs: record the repo model decisions24d
41- **Repo name rules:** `OrgName`'s, plus `.` and `_` for names like `.github` and
42 `foo.js`. Lowercased, max 100. Also rejects a name of nothing but dots and any name
43 ending `.git` — the first is traversal, the second would live at `foo.git.git`.
44- **Reserved repo names:** `import`, `new`, `search`. Only names directly under
45 `/{handle}/repos/` can collide.
46- **Visibility defaults to public**, matching a portfolio-first product.
47- **Repositories carry an optional description**, capped at 300 characters — a sentence
48 for the profile listing, not a README. *Added without being asked for; remove if it
49 is not wanted.*
c5b3ff5feat: repository persistence24d
50- **`list_by_org` returns every repository regardless of visibility.** Filtering is an
51 authorization decision and belongs to the use case, so the page and `/api` cannot end
52 up applying different rules. The cost is that a private repo is briefly in memory
53 before being filtered, which is fine in-process.
3954b45docs: record milestone 2 phase 125d
54
be4fac5docs: correct the Topcoat guidance in CLAUDE.md24d
55### Open
56
57- **Test fixtures for `DiskGitStorage`:** `tempfile` as a dev-dependency, or write under
58 `target/`? Leaning `tempfile` — self-cleaning and parallel-safe. Unanswered.
59
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
60### Watch for
d7b99d9docs: record milestone 0 progress and routing findings1mo
61
2eb8681docs: put git next, plan the repo model24d
62- **The database and the filesystem cannot share a transaction.** Creating a repo
63 writes a row and a directory. Neither previous attempt solved this properly — see
64 [architecture.md]architecture.md#db-plus-filesystem-writes. A compensating delete is
65 good enough to ship, but write down that an orphaned directory is possible if the
66 process dies between the two, rather than rediscovering it.
67- **Path traversal.** `{data_dir}/{handle}/{name}.git` is built from user input. A name
68 containing `..` or `/` must be impossible before it reaches the filesystem, and
69 `RepoName` is the place to make it impossible rather than sanitising at the call site.
70- **Visibility is an authorization decision**, so it belongs in the use case. A private
71 repo must be absent from listings, not merely unlinked — and `/api` must agree with
72 the page.
73- **`git` becomes a runtime dependency** from this milestone. The runbook should say so.
076dbc9docs: close milestone 1, open milestone 21mo
74
f0444b7docs: plan milestone 2 in two phases1mo
75### Carried over — small, unblocked
076dbc9docs: close milestone 1, open milestone 21mo
76
2eb8681docs: put git next, plan the repo model24d
77- **Fonts are not loaded.** The theme names Geist and IBM Plex Mono; both fall back
78 today. Topcoat's `font-fontsource` feature handles it.
79- **Light mode is untested.** The palette defines it; nobody has looked at it.
f0444b7docs: plan milestone 2 in two phases1mo
80- **No rate limiting** on `/auth/login` or `/auth/setup`.
076dbc9docs: close milestone 1, open milestone 21mo
81- **`sweep_expired` is never called**, so expired session rows accumulate. Expiry is
82 enforced on read, so this is tidiness, not a hole.
2eb8681docs: put git next, plan the repo model24d
83- **CSRF.** `SameSite=Lax` covers the common case. Forms now exist, so this is decidable
84 rather than hypothetical.
ab7fea9chore: plans setup1mo
85
86## Backlog
87
88Ordered. Pull from the top.
89
2eb8681docs: put git next, plan the repo model24d
901. **Milestone 4 — Git over HTTP.** `git http-backend` subprocess, PATs over HTTP
91 Basic. See [0001]decisions/0001-git-over-http-not-ssh.md. The `body_limit` cap will
92 reject large pushes until raised.
932. **Milestone 5 — Repo browsing.** Tree, blob, commit log.
943. **Milestone 6 — Writing.** Posts, markdown, `/{handle}/posts/{slug}`. Still open
95 whether writing or projects/showcases is the better first portfolio feature.
ab7fea9chore: plans setup1mo
96
97## Open questions
98
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
99- **Topcoat is early** (v0.5.0, first released 2026-07-22, breaking changes expected
100 by its own authors). Expect churn that isn't feature work.
ca76e1bdocs: fix milestone cross-references after the reorder24d
101- Body size limits will reject large pushes at Milestone 4 — `topcoat-router` has a
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
102 `body_limit` layer that needs raising on the git routes. Recorded here because it
103 will surface as a confusing failure rather than a clear one.
104- Topcoat ships Tailwind without Node, which reopens the design system attempt #1
105 dropped purely to avoid an npm build step — see [ui.md]ui.md.
106
107## Routing findings (Milestone 0)
108
109- **Topcoat 0.5 requires rustc ≥ 1.95.** On an older toolchain `cargo add topcoat`
110 silently resolves to an empty `topcoat v0.0.0` placeholder instead of failing. Local
111 stable is now 1.97.1.
112- `Router::builder().discover()` collects `#[page]`-annotated items **at link time**,
113 so pages can live in any module. Layering is our choice, not the framework's.
114- `module_router!` derives each URL from the module tree rather than a path string.
aaefaabfeat: root handles, grouped routes, reserved-handle denylist1mo
115 Still deferred. Application routes now group cleanly (`auth/login`, `api/me`), but
116 handles sit at the root ([0004]decisions/0004-root-handles-grouped-routes.md), so a
117 parameterised root segment still has to coexist with static ones. Worth checking how
118 `module_router!` handles that before committing to it.
bd48b4bdocs: serve git over smart HTTP, reorder roadmap portfolio-first1mo
119- Path and query params are read from `Cx` via `path_param!` / `#[query_params]`, not
120 injected as handler arguments. Parses are memoized per request.
121- Layouts wrap by path prefix and nest outermost-first, and a layout can catch a page's
122 `NotFoundError` to render a branded 404.
123- `HOST` / `PORT` configure the bind address, so `STEID_LISTEN_ADDR` is gone.
124- `Body` is a boxed `http_body::Body` used for both requests and responses, with
125 `into_data_stream()` to read and `Body::new()` to wrap a stream — pack data can
ca76e1bdocs: fix milestone cross-references after the reorder24d
126 stream both directions without buffering. This is what makes Milestone 4 viable.