diff --git a/docs/notes/object-hydration.md b/docs/notes/object-hydration.md
index 3d52128..3d4950c 100644
--- a/docs/notes/object-hydration.md
+++ b/docs/notes/object-hydration.md
@@ -30,10 +30,24 @@ thing at the git layer. The needed closure must be present _before_ git runs.
life. Revisit (3) only if large repos become real. This is the "scaling wall for
large repos" the architecture refers to.
+## Progress
+
+- **Advertisement needs no objects (being done — `tasks/13`).** The `info/refs`
+ GET only lists refs, which live in s3lite; it can serve from `packed-refs` with
+ zero object hydration (spiked: `git upload-pack --advertise-refs` advertises
+ refs whose objects are absent, branches and annotated tags alike). This carves
+ the most common cold round-trip off the full-hydrate path first.
+- **Bounded closure for the data POSTs is deferred.** The sharp part: computing
+ "objects reachable from ref X" without the objects present needs a precomputed
+ **reachability + pack-location index** (built at push, consumed at hydrate),
+ because stock git can't fetch mid-operation and packs are all-or-nothing. That
+ index is the real design work; parked until it's understood well enough to spec.
+
## Open sub-points
-- Computing the served closure for reads (reachable from advertised refs, capped
- by what the client may request).
+- The reachability+location index for bounded POST hydration: shape (commit graph
+ + object→pack map in s3lite), how it's kept consistent with content-before-
+ pointer, and its interaction with any future server-side `gc`/repack.
- Eviction policy for warm repos under disk pressure.
- Whether a write needs the _full_ history or just enough for the
fast-forward / merge-base check (bounded hydration).
diff --git a/tasks/13-advertise-without-hydration.md b/tasks/13-advertise-without-hydration.md
new file mode 100644
index 0000000..98e0892
--- /dev/null
+++ b/tasks/13-advertise-without-hydration.md
@@ -0,0 +1,69 @@
+# Serve the ref advertisement without hydrating objects
+
+## Spec
+
+The `info/refs` advertisement — the GET that opens every clone, fetch, and
+`ls-remote` — must not hydrate object history. It only lists refs, which gitmote
+already holds in s3lite. Today a cold advertisement downloads the whole repo just
+to print ref names; it shouldn't touch a single object.
+
+This is the first, separable slice of bounded hydration
+([docs/notes/object-hydration.md](../docs/notes/object-hydration.md)); the full
+per-operation closure for the data POSTs stays deferred (it needs a reachability
+index — see the note).
+
+## Current
+
+[materialize.go](../internal/repo/materialize.go) `Materialize` runs on **every**
+request — including the `info/refs` GET — and always `hydrateObjects` (full
+closure) then `writeRefs` via `git update-ref`, which *requires each ref's target
+object present*. So the advertisement path drags the entire repo onto disk before
+git prints a ref list.
+
+## Why it's safe (spiked)
+
+`git upload-pack --advertise-refs` advertises refs whose target objects are
+**absent** — verified against git 2.50:
+
+- Refs written into `packed-refs` with no objects present → full, correct
+ advertisement (SHAs, `symref=HEAD`, capabilities, exit 0).
+- An annotated-tag ref with its tag object absent → advertised fine; git just
+ **omits the `^{}` peel line** (correct, only a lost negotiation hint). With a
+ pre-computed `^<commit>` peel line in `packed-refs`, it emits the peel too —
+ still no object needed.
+
+The advertisement and the later data POST are both served by the *same* stock
+git, so there is no capability/protocol drift risk — we only skip hydration.
+
+## Change
+
+- Add a refs-only path to the Materializer (e.g. `MaterializeRefs(ctx, name)`):
+ ensure the bare repo exists, regenerate `packed-refs` **directly from
+ `meta.ListRefs`** (write the file — not `update-ref`, which validates object
+ presence), set `HEAD` via `symbolic-ref` from `default_branch`. **No object
+ hydration.** MVP omits annotated-tag `^{}` peels (advertisement stays correct);
+ storing peels in meta for the optimization is an optional follow-up.
+- In [backend.go](../internal/githttp/backend.go) `ServeHTTP`, when
+ `endpoint == "info/refs"` (either service), call `MaterializeRefs`. The data
+ POSTs (`git-upload-pack`, `git-receive-pack`) keep the full `Materialize` —
+ bounding *those* closures is the deferred task.
+- Factor the `packed-refs` generation so the refs-only and full paths agree on
+ ref formatting.
+
+**Notes / out of scope:** refs-only `packed-refs` and a later full-`Materialize`'s
+loose refs coexist (loose overrides — fine). Pruning refs deleted from meta is a
+pre-existing cache gap, unchanged here. The data-transfer POSTs still full-hydrate
+until the bounded-closure task lands.
+
+## Verify
+
+- **No objects touched:** an `info/refs` advertisement (both services) served from
+ a repo whose objects were never hydrated returns the correct ref list — assert
+ via a spy `store` that `Get` is **never** called on the advertisement path, and
+ the advertised refs/SHAs match `meta.ListRefs`. Cover a repo with an annotated
+ tag (advertises, peel omitted).
+- **Data path intact:** a real clone/fetch and a push still work end-to-end (the
+ POSTs full-hydrate as before) — existing githttp read/write tests stay green.
+- **Regression:** `make e2e-local` + `make e2e-restore` green; `go test ./...`.
+- Update materialize.go's "full-hydrate for the MVP" comment to reflect that the
+ advertisement is now objectless.
diff --git a/tasks/README.md b/tasks/README.md
index f8027a5..421f6db 100644
--- a/tasks/README.md
+++ b/tasks/README.md
@@ -6,8 +6,14 @@ lands** (commits are the record — this list never becomes a changelog).
Ordered by dependency; each task lands in a non-breaking, tested state.
-_No active tasks._ The **gitmote can host itself** milestone is reached: it runs
-on Scaleway at `gitmote.atmin.net`, deploys itself on every green master push,
-survives cold starts (litestream restore), and enforces the single writer by a
-lease (s3lite `RoleAuto`), so rolling deploys are safe by construction.
-Speculative directions live in [../docs/evolution/](../docs/evolution/).
+The **gitmote can host itself** milestone is reached: it runs on Scaleway at
+`gitmote.atmin.net`, deploys itself on every green master push, survives cold
+starts (litestream restore), and enforces the single writer by a lease (s3lite
+`RoleAuto`), so rolling deploys are safe by construction. Speculative directions
+live in [../docs/evolution/](../docs/evolution/).
+
+- [13-advertise-without-hydration.md](13-advertise-without-hydration.md) — serve
+ the `info/refs` advertisement from s3lite refs (via `packed-refs`) with zero
+ object hydration. First, separable slice of bounded hydration; the full
+ per-operation closure for data POSTs is deferred (needs a reachability index —
+ see [../docs/notes/object-hydration.md](../docs/notes/object-hydration.md)).