alkgit 0.1.0

Git smart protocol for the alk family: pkt-line substrates, protocol V2 serving (upload-pack/receive-pack), producer/consumer halves on alkcall channels, and the backend seam traits — the shipped alkstore+alkblobs composition is the alkgit-substrate crate (ADR-010/025)
Documentation
# ADR-005: Session substrate types — duplex + stateless over one state machine

## Status

Accepted (substrate inputs amended by ADR-016: both substrates receive
the service dimension explicitly)

## Context

POC-1 and POC-3 found recurring glue that must not be re-implemented per
adapter or per handler:

- The split → tokio-util compat → packetline bridge (POC-1).
- `StreamingPeekableIter`'s stop-delimiter contract: flush returns `None`,
  the iterator is inert until `.reset()`; forgetting reset silently
  swallows the next command (POC-1's second hang).
- The three-layer return type `Option<io::Result<Result<PacketLineRef,
  decode::Error>>>`.
- The EOF-mid-parse error loop: an error-shaped read that resets-and-retries
  spins a tokio worker at 100% (POC-3 recorded bug class) — error branches
  must break unconditionally.
- The http-side request-body adapter (axum `BodyDataStream` →
  `futures_io::AsyncRead`, ~40 lines, POC-3).
- The smart-http framing facts: first POST carries the capability dump
  (skip two flush-delimited sections); capabilities before `0001` delim,
  args after; responses end at flush, never `0002` (remote-curl dies on
  it); the flush is one-shot per response (idempotent-flush guard is
  mandatory); a flush-only POST is a probe answered 200-empty.

If adapters or handlers hand-roll any of this, each gets it subtly wrong.

## Decision

`alkgit` owns the substrate layer; doors and handlers see
friendly types:

1. **`Session` (duplex)** — wraps (stream, limits, service) into the
   pkt-line session: owns the split/compat bridge, the request reader (command line
   + pre-delim lines + args + flush, with the `reset()` discipline and the
   unconditional-error-break rule), and the writer (sideband sink with
   65000-byte chunks, idempotent flush). The service selects the
   advertisement and state machine at start (ADR-016). Seed: POC-1's
   `serve_v2`.
2. **`StatelessRequest` (http)** — wraps (request-reader, response-writer,
   service) per POST: performs the capability-dump skip, the delim-aware arg split,
   and the flush-only response rule (no `0002`, idempotent flush,
   200-empty for flush-only probes); the door's route supplies the
   service (nothing in a POST body distinguishes the services — ADR-016).
   Seed: POC-3's `httpservice.rs` + `BodyReader`.
3. **One protocol core** — the V2 state machines (ADR-003) run under both
   substrates; the stateless substrate presents each POST as a command
   invocation. Version dispatch happens at session start, in one place.
4. **Error taxonomy** — substrate errors are a transport-level
   `thiserror` enum; io errors are terminal (session ends), protocol
   errors map to pkt-line error bands / http status responses per the
   respective substrate's convention.

The sideband sink (band-1 chunks ≤ 65000 bytes, blocking-side `io::Write`
that parks on a bounded channel on the http path) is part of the substrate:
exactly one task drives a given sink (POC-2's `SidebandSink` contract).

## Consequences

- Adapters (http, ssh, downstream) cannot get the framing wrong; the
  POC-hang and spin-bug classes are structurally prevented.
- The packetline raw API is confined to transport internals.
- State machines are substrate-agnostic, which is what makes smart-http's
  statelessness cheap (per-request state, no session to restore).

## References
- `docs/research/poc-1-findings.md` §"Answered hypotheses" 3 (packetline contracts),
  follow-ups 1–2
- `docs/research/poc3-findings.md` §"Course corrections" 1, follow-ups 2, 4
- `docs/research/gitoxide.md` §"Wire format"
- ADR-002 (substrate shapes), ADR-003 (V2-first), ADR-004 (sink = io::Write),
  ADR-016 (service selection in the substrate inputs)
- transport.md §substrate