---
status: reviewed
last_updated: 2026-10-23 (the wave-5 release gate — OQ-19 resolved;
the core-side OQ-03 inventory settled)
---
# Open Questions
All unresolved architecture questions, centrally tracked. Status values:
`open` (needs resolution now), `partially resolved` (decision made but a
narrower question remains — named in the entry), `resolved` (decision
made, ADR recorded), `deferred(scope)` (waiting on external information
*or* on a not-yet-existing internal artifact — a booked work item —
the blocked-on condition states which), `deferred(unclear)` (pieces
exist, shape needs investigation). See `docs/sdd_process.md` for the
deferral protocol (blocker tasks in `tasks/architecture/`).
**Door type** classifies reversal cost: `one-way` decisions are
expensive/impossible to reverse once published (wire formats, public API
shapes); `two-way` decisions can be revisited while nothing is published.
Door type does not change urgency — all decisions here need resolution
when their impacts say so; it records how careful the resolution must be.
## Deferred / Blocked summary
| OQ-05 | deferred(scope) | ecosystem need for sha256 |
| OQ-03 | partially resolved | first-publish timing (release decision; **the core-side freeze inventory settled at the wave-5 gate, 2026-10-23** — enumerated in the gate record; the substrate-side inventory settles at the substrate crate's publish) |
| OQ-16 | deferred(scope) | first cross-assembly record sharing (distributed-git phase; tracker `architecture/oq-16-grant-identity-namespace`) |
| OQ-17 | resolved (ADR-024) | — |
| OQ-19 | **resolved (2026-10-23)** | — (all three riders served/dispositioned at the wave-5 gate, or recorded honestly) |
| OQ-20 | deferred(scope) | per-oid pruning of stale LFS reference rows — first concrete row-volume need (repo-lifetime scoping is correct v1) |
| OQ-21 | deferred(scope) | build: first client needing the LFS lock server (`lfs.locksverify false` is the verified workaround) — *plus* the composition-validation POC owed before the v0.1 publish |
OQ-02 and OQ-04 resolved this cycle (ADR-014, ADR-013; §3/§6 amended by
ADR-020); OQ-08/07/09/01 resolved in earlier cycles; **OQ-19 resolved
2026-10-23 at the wave-5 review gate** (all three riders served; none
retracted). OQ-06 (resolved by
ADR-012: `registry-file` default) is **superseded by ADR-019** — the
registry backing is now the substrate composition and `registry-file`
is scrapped. The substrate re-baseline cycle (ADR-019..023, 2026-10-19)
resolved the recomposition DQ ledger: DQ-1/2/3/4/6/8 in ADR-019,
the receive-pack wire facts in ADR-020, DQ-5 in ADR-022, the
replication posture in ADR-023; DQ-7's co-tenancy/connection facts
resolved as *assembly documentation* (ADR-019's negative
consequences, substrate.md §assembly shapes — measured data, tuning
knobs, and the connection budget, not an ADR-shaped decision). The
2026-10-20 crate-shape round (ADR-025, relitigating OQ-09's
granularity sub-decision) made alkgit a two-crate workspace — protocol
core + substrate composition; OQ-03's freeze inventory re-partitions
per crate. OQ-16 is deferred but blocks nothing in
v1 (grants key on cluster-local ids within one assembly).
## Theme: composition / crate shapes
### OQ-09: Slim-crate model — doors as family infrastructure, git as a payload service
- **Origin**: user session (2026-09-21); ADR-006, ADR-001, ssh.md
- **Status**: **resolved** — ADR-010 (pure protocol crate, the alktty/
alktunnels template): single `alkgit` crate, producer/consumer halves,
backend traits with feature-gated gix, no doors, no binary; ALPN
`alk/git`; http mounting → alkhttp `git` feature; git-over-ssh →
alkssh (russh scaffolding dropped); the monorepo/binary framing was an
init-agent artifact (vision amended). *(Amended 2026-10-20: the
crate-granularity sub-decision within that resolution was superseded
by ADR-025 — alkgit is now a two-crate workspace; the protocol-core
framing and everything else in the resolution stands.)*
- **Resolution**: [decisions/010-pure-protocol-crate.md] (the `gix`
engine feature later dissolved — ADR-021 §1; the impls are the
substrate crate — ADR-025; granularity superseded by
[decisions/025-two-crate-workspace.md]). The full
sub-decision set recorded there (ssh deletion, http home, crate
granularity, backend-trait surface, binary fate); ADR-025 owns crate
granularity hereafter.
- **Cross-references**: ADR-001/006 (superseded), ADR-025, OQ-01,
OQ-03, OQ-08, doors.md, backend.md, overview.md
### OQ-01: HTTP adapter home and composability (alkhttp `git` feature vs alkgit-owned factory)
- **Origin**: user session question (OQ-01, resolved 2026-09-21)
- **Status**: **resolved** (subsumed by OQ-09/ADR-010) — the smart-http
stateless substrate stays in `alkgit` (IO-abstract); the http mounting
(routes, content types, `with_extra_routes` wiring) becomes an alkhttp
`git` feature published after alkgit's first publish. The ADR-006
router-factory shape is superseded; the alkhttp-side feature is the
outcome.
- **Cross-references**: ADR-006 (superseded), ADR-010, doors.md
### OQ-03: Downstream embedding surface (what "embeds alkgit" means concretely)
- **Origin**: [overview.md], [transport.md], vision §"ALPN as a service"
- **Status**: **partially resolved** — the shape is settled (ADR-010
as amended by ADR-019..021; its granularity sub-decision and
ADR-021's graph superseded by ADR-025): embedding = the
protocol core `alkgit` + the five backend traits (own storage via
`alkgit` alone, or the shipped composition via `alkgit-substrate`
with its feature graph: `sqlite`/`postgres`/`lfs`) + optional door
features. ADR-025 (2026-10-20) re-partitioned the freeze inventory
across the two crates: the core's surface (the five traits, seam
types, `GitSession`, producer half, the `git/repo/*` op set, the
preamble shapes, `Limits`, errors) freezes at the core's first
publish; the composition's member list
(substrate.md §public API — fixes the members, not the identifiers)
freezes at the substrate crate's first publish. The *identifiers* on
both sides remain deferred to the publish-time freeze. What ADR-025
changed was the inventory's crate assignment, not its members. The
prior single-crate pinning history: ADR-012 added the `git/repo/*` op
set (names + schemas) to the inventory; ADR-013/014 added the
push/negotiation trait surface (`GitPackIngest` binding,
`GitPackGen::common_haves`); ADR-016 added the native-path preamble
wire shapes (the `{repo, service}` open-op params schema and the
git-daemon request-line grammar — the last native-path wire surface);
ADR-017 added the `GitSession` public API; the registry types/schemas
draft (backend.md §"Registry types and schemas", review 001 N-3)
pinned `RepoRecord`, `RegistryError`, and the `git/repo/*` op
schemas; ADR-018 pinned the object-storage trait signatures, the
shared backend-seam types (`RefLine`/`RefUpdate`/`RefOutcome`/
`PreparedPush`/`PushOptions`), and `StorageError` — the backend-seam
inventory is complete. The substrate round's additions: the
**revised `RepoRecord` schema** (no `storage_root` — ADR-019 §8) and
the substrate composition's public members (now
`alkgit-substrate`'s freeze set).
- **Door type**: one-way (API freeze is registry-visible to dependents)
- **Priority**: medium
- **Impacts**: blocks the first publish only, not implementation. The
re-partition also un-blocks the core's freeze from the substrate-side
validations (OQ-21's composition POC gates the substrate crate's
publish, not the core's — the core's freeze waits only on OQ-19's
wire validations against the real clients, which exercise the core).
- **Blocked on**: per crate — the *core's* wire-validation blocker is
**cleared** (OQ-19 solved 2026-10-23: all three riders served or
dispositioned; the core-side freeze inventory settled at the wave-5
gate — `tasks/implementation/core/review-wave-5.md` carries the
enumeration); the core's publish now waits only on release timing
(`core/w5-publish`). The *substrate crate's* publish still waits on
OQ-21's composition-validation POC. Both
additionally wait on first-publish timing (a release decision, not
an architecture question).
The API surface inventory lives in
[backend.md](backend.md) §public API, [transport.md](transport.md)
§public API, and [substrate.md](substrate.md) §public API.
- **Cross-references**: ADR-010, ADR-025, ADR-002, ADR-012, ADR-013,
ADR-014, ADR-016, ADR-018, ADR-019, ADR-021 (superseded), backend.md,
transport.md, substrate.md
## Theme: transport / protocol
### OQ-02: V2 multi-round negotiation (ack/NAK logic, `wait-for-done` retirement)
- **Origin**: [transport.md], poc2-findings §"does NOT settle"
- **Status**: **resolved** — ADR-014 (V2 negotiation ack loop). The
duplex-capture walkthrough against git 2.43.0 (ground-truth negotiation
mock, cross-checked against `fetch-pack.c`) resolved the grammar: no-
`done` rounds get `acknowledgments` (`ACK <oid>` per recognized have,
`NAK` when none, flush — never `ready`, so FLUSH is always the
terminator); the `done` round generates closure(wants) − closure(haves)
with no cross-round server state (clients re-send wants + commons every
round); `wait-for-done` stays and the advertisement text is unchanged;
the ack check is a new backend-trait method (`common_haves`) keeping
the honest boundary at the seam. Captures:
`docs/research/negotiation-captures.md`.
- **Resolution**: [decisions/014-v2-negotiation-ack-loop.md]
- **Cross-references**: ADR-003, ADR-004, ADR-005, ADR-009, ADR-014,
transport.md §fetch, backend.md §"The trait family" (GitPackGen)
### OQ-04: receive-pack (push) — validation gap
- **Origin**: [transport.md], poc3-findings §"does NOT settle"
- **Status**: **resolved** — ADR-013 (receive-pack state machine). The
walkthrough against real `git push` captures (git 2.43.0: file://,
git://, smart-http, plus raw stdio requests into real `git receive-pack`)
resolved every listed unknown: the push path is V0-framed by upstream
design (no version negotiation, ADR-003 governs fetch only); the
advertisement is a V0-shaped ref advertisement (caps on the first ref
line, `capabilities^{}` sentinel only for empty repos) with served set
`report-status(-v2) delete-refs side-band-64k atomic ofs-delta
object-format=sha1` (+ `push-options` config-gated); shallow request
lines are rejected up front for v1 (symmetric with fetch); thin packs
accepted with bases from the server odb (default client behavior, no
capability); ingestion =
`Bundle::write_to_directory_eagerly` +
`gix-fsck` + one `gix-ref` transaction per push (`.keep`-guarded pack
landing) — *(the ingestion composition and the pack-arrival shape
are amended by ADR-020 on the substrate baseline: unpack-to-loose
under the pin bridge, the bounded packless-push peek)*; CAS timing is unpack-first-then-per-ref (observed upstream
order); status report is band-1 pkt-line-framed with inner flush when
sideband was selected. Captures:
`docs/research/push-captures.md`.
- **Resolution**: [decisions/013-receive-pack-state-machine.md]
(amended by [decisions/020-receive-pack-duplex-amendments.md])
- **Cross-references**: ADR-003, ADR-004, ADR-005, ADR-009, ADR-013,
ADR-020, transport.md §receive-pack, backend.md §"The trait family"
(GitRefs, GitPackIngest), doors.md, substrate.md
### OQ-05: sha256 support policy
- **Origin**: [transport.md], git-protocol.md §"Open items"
- **Status**: deferred(scope)
- **Door type**: two-way
- **Priority**: low
- **Impacts**: none for v1 (sha1 pinned); feature-flag passthrough
compiles but is untested end-to-end.
- **Blocked on**: ecosystem need (a real client/repo requiring sha256) or
upstream gix sha256 maturity; POC-2 left the pipeline hash-generic but
untested. Tracker task: `tasks/architecture/oq-05-sha256.md`.
- **Cross-references**: ADR-003, ADR-004
## Theme: identity / auth
### OQ-08: Registry identity space + vault placement (narrowed)
- **Origin**: [overview.md], [doors.md], [backend.md]; originally "identity
sources per front door"
- **Status**: **resolved** — ADR-011 (per-repo authorization): the
identity model is alkcall's resolved `Identity` keyed on its stable
logical id (alkcall ADR-025 property, referenced); alkgit stores no
identity records; grants live in repo records; the public+read /
always-authenticated-write policy is alkgit-core's `authorize`
function (the clause ADR-007 pinned but the static ACL engine could
not express); vault placement resolved as **nothing to place in v1**
(no credential-shaped material in metadata).
*(Grant-key id format declared deliberately open by ADR-015 §7 — the
cross-identity-namespace question this leaves is tracked as OQ-16.)*
- **Resolution**: [decisions/011-per-repo-authorization.md]
- **Cross-references**: ADR-007, ADR-008, ADR-011, ADR-012, OQ-06, OQ-07,
backend.md §GitRegistry, doors.md
### OQ-16: Grant-key identity namespace (globally-comparable ids for multi-hub grants)
- **Origin**: ADR-015 §7; user session (2026-09-26)
- **Status**: deferred(scope)
- **Door type**: two-way (schema field format; grants are already opaque
strings, so this constrains only the *contents*, not the alkgit API)
- **Priority**: low (blocks nothing in v1 — grants key on alkcall's
cluster-local id within one assembly, which is the only deployment
v1 serves)
- **Question**: what identity-id format do grants key on when repo
records are shared across assemblies (the distributed/replicator
deployment: contract-watching hubs mirroring grant state)? Today the
id is alkcall's `Identity.id` — stable within one cluster, but
meaningless across hubs. Candidate shapes: `{namespace}/{id}`
compound ids (the `{user|org}/{repo}` convention familiar from
self-hosted git — e.g. `alkdev/alkgit` locally, `alkimiadev/alkgit`
when mirrored — extended to identity: `gh:alkimiadev`,
`alk:alkdev`), did-key / contract-derived identifiers (the planned
smart-contract org/user/repo model), or a two-part
(authority, local-id) tuple.
- **Constraints any resolution must satisfy** (recorded from ADR-015 §7):
grants remain flat sets keyed on opaque strings; alkgit never parses,
validates, or interprets the id — collision-freedom is the
identity-provider's guarantee; a downstream namespace can be
introduced at the assembly seam without an alkgit change.
- **Impacts**: only the downstream identity-provider seam and the
future platform app / replicator grant-sync format. The v1
`authorize` policy function, the grant schema shape, and the op gate
are all id-format-agnostic by construction.
- **Blocked on**: the first deployment that shares repo records across
assemblies (the distributed-git phase; also touches alkcall's
identity model if a family-wide id format is preferred over a
payload-level namespace convention). Tracker task:
`tasks/architecture/oq-16-grant-identity-namespace.md`.
- **Cross-references**: ADR-011 §1, ADR-015 §7, OQ-08
## Theme: storage / metadata
### OQ-06: Registry/metadata backing store (gix feature)
- **Origin**: [backend.md] (was storage.md), ADR-008
- **Status**: **resolved then superseded** — ADR-012 (registry backing +
write surface): the alknet repo/adapter pattern applied — `GitRegistry`
read trait + `GitRegistryStore` write supertrait; default backing was
the `registry-file` feature, persistence adapters future/additive.
**Superseded 2026-10-19 by ADR-019** (the substrate re-baseline):
the registry backing is now the co-tenant relational composition over
the substrate engines behind the family adapter pattern (engine
twins for sqlite/postgres + the in-memory default adapter), and
`registry-file` is scrapped outright (no shim, no also-ran fallback —
the family's scrap discipline). The read/write trait split itself
survives verbatim (the ADR-018 signatures held with zero drift).
- **Resolution**: [decisions/012-registry-backing-and-ops.md] §1–2, §4
(superseded) → [decisions/019-substrate-baseline.md] §3
- **Cross-references**: ADR-008, ADR-010 (granularity superseded by
ADR-025), ADR-011, ADR-019, backend.md §crate and feature model,
substrate.md
### OQ-17: Relational registry schema migrations (versioning story for the co-tenant tables)
- **Origin**: POC-R's "not settled" list; ADR-019 → substrate.md
(the shipped composition's tables)
- **Status**: **resolved** — ADR-024 (2026-10-19): migrations are
construction-run on the adapter's own connection, ordered monomorphic
per-twin steps one transaction each, concurrent-open-safe via the
alkstore race-verification pattern; one alkgit-owned stamp table
versions the co-tenant schema as a unit (records + refs + seq + LFS
rows); additive-first with the coexistence contract as its declared
consequence and no refuse-gate while steps are additive (a
non-additive step is major-version with its own ADR owning the
refusal semantics); the LFS refcount column ships day-one in the
initial schema (no migration owed); the in-memory adapter is
stamp-free. No operator surface is owed (ADR-010).
- **Resolution**: [decisions/024-registry-schema-migrations.md]
- **Cross-references**: ADR-019 §3, ADR-022 §4, ADR-024, substrate.md,
lfs.md, backend.md
### OQ-19: Implementation-phase wire validations (unborn-HEAD rider; `common_haves` over the substrate odb)
- **Origin**: [transport.md] §ls-refs (the N-5 rider);
poco-findings §"what this does NOT settle" + pocx-findings (the
NAK-only ack caveat)
- **Status**: **resolved** (2026-10-23, the wave-5 review gate): all
three riders validated or dispositioned, none retracted —
(a) the unborn-HEAD symref line served against a real client on both
substrates (`git ls-remote` + an unborn-HEAD clone complete over the
duplex git:// and stateless http doors, `tests/real_client.rs`); the
capture **corrected the wire emission shape** to upstream's literal
`unborn` token (a zero-oid HEAD makes a real client want the null
oid and error the session — ADR-018's amendment note; the inbound
parser accepted both shapes all along). (b) the positive-ACK capture
landed machinery-side (`core/w5-oq19-positive-acks`) — validated on
both substrates; **the substrate-odb facet re-runs in track B wave
7** against the real Find before the substrate crate's freeze. (c)
the real-client token inventory (`core/w5-oq19-real-client-tokens`):
`include-tag` accepted + honored by omission (un-gated in git's V2
send path, no `fetch=` slot to retract through), `session-id=`
honestly retracted (never advertised → never sent), the rest
declined fail-closed as unimplemented.
- **Door type**: two-way (capability tokens retractable until
published) — *now frozen with the core's publish (OQ-03 core-side)*
- **Priority**: low (resolved; was neither blocking anything but the
freeze)
- **Question**: two advertised/machinery promises lacked capture-level
validation over the final substrate shape: (a) serving an unborn
HEAD's symref line for `ls-refs=unborn` on both substrates with a
real client (the capability token was client-accepted in POC-1; the
*line* was never exercised; the unborn fixture is trivial now that
refs are rows); (b) `common_haves` actually recognizing haves over
the substrate Find in a multi-round ack loop (POC-X's fetch ran
multi-round with NAK-only acks — the refinement is a negotiator
optimization, not a correctness need: the trim rides haves-closure).
(A third rider joined from the wave-2 gate: the negotiation
machine's fail-closed declines of tokens real clients send.)
- **Impacts**: the advertisement text (drop the token if (a) fails)
and the negotiation behavior's validation record. Not structural.
- **Resolution**: the wave-5 wire-validation sessions (the tracker
task `tasks/architecture/oq-19-wire-validations.md` carries the
outcomes per rider; OQ-03's core-side freeze inventory settled at
the gate — `tasks/implementation/core/review-wave-5.md`).
- **Cross-references**: ADR-003, ADR-014, transport.md, backend.md,
OQ-03
### OQ-07: Admin API operation set (v1 scope)
- **Origin**: [overview.md], alk-stack.md §"The gitea lesson"
- **Status**: **resolved** — ADR-012 (§3, §5): the crate ships a minimal
CRUD op set (`git/repo/{create,delete,update,get}`) as thin call ops
over `GitRegistryStore`, `Visibility::External`, always-on, gated by
the global admin scope and per-repo grants. The old "Internal ops over
an admin listener" framing is superseded (right mechanism, wrong axis
— visible-surface = authorized-surface is served by the ACL). The
gate table as written used scope+ownership and is replaced by ADR-015
(the `manage` grant tier — admin scope OR manage grant, handler-
evaluated; create seeds the creator's `{read, write, manage}` grants).
Ops stay record-scoped; anything beyond repo records (users, orgs) is
a downstream platform crate (the recorded split trigger, ADR-012 §5) —
the app compiles roles/teams into flat grants (ADR-015 §6).
- **Resolution**: [decisions/012-registry-backing-and-ops.md] §3, §5
(gate amended by [decisions/015-manage-grant-and-op-gate.md])
- **Cross-references**: ADR-007, ADR-011, ADR-012, ADR-015, alkcall
ADR-017/011, backend.md §"The two op kinds"
## Theme: LFS
### OQ-20: Per-oid pruning of stale LFS reference rows
- **Origin**: [lfs.md] (the pointer-lifecycle table's scoping note)
- **Status**: deferred(scope) — the repo-lifetime scoping is correct v1
behavior; per-oid pruning is a future shape awaiting a real need
(the register's earlier `open` here was a classification slip: the
blocked-on condition was stated all along, so this was never a
resolve-now question)
- **Door type**: two-way (row policy; nothing published)
- **Priority**: low
- **Question**: the v1 row store scopes rows to the repo's lifetime
(written at reference assertion; removed with the repo or by pointer
removal). Rows for oids no longer referenced by any live ref in their
repo (history rewritten, branch deleted) linger until repo deletion.
Per-oid pruning — a row-reclamation op that drops rows whose
pointers are no longer reachable (via the ref-graph walk alkgit
already does for object liveness) — is the future pointer-lifecycle
shape that reclaims them sooner. The sweep-window semantics make the
cost of *not* having it bounded but nonzero (rows are tiny; the
media may be re-protected while any row remains).
- **Impacts**: only the pointer-lifecycle ops' future surface and the
LFS liveness source's precision.
- **Blocked on**: a concrete need (row volume) or the first LFS-heavy
deployment; the repo-lifetime scoping is correct v1 behavior.
Tracker task: `tasks/architecture/oq-20-lfs-row-pruning.md`.
- **Cross-references**: ADR-022 §4, lfs.md, OQ-17
### OQ-21: The LFS lock server (`lfs.locks`)
- **Origin**: [lfs.md] (the constraint note), POC-L (unimplemented)
- **Status**: deferred(scope) — two layers, kept distinct: the *surface
build* waits on a client need; a *composition validation* is owed
before the v0.1 publish (the second layer is not a wait — see
Investigation).
- **Door type**: two-way (additive surface)
- **Priority**: low for the build; medium for the validation (it gates
the publish posture, not any v1 behavior)
- **Impacts**: none on v1 behavior — `lfs.locksverify false` is the
client-verified posture and no lock capability is advertised. What
the validation gates is the recorded feasibility posture: the specs
assert "the routes and alkstore lock machinery (named TTL locks)
exist and compose" — and the *compose* half is currently unevidenced
(POC-L recorded the lock server as unimplemented). v0.1 should not
go out sitting on a guessed posture behind the published substrate
pair.
- **Investigation** (owed before first publish): a small POC walking
the real `lfs.locks` API flows (create/list/verify/unlock) over the
substrate stack — alkstore's named-TTL lock machinery against the
API's actual semantics, the lock rows co-tenant on the engine the
two substrate crates share (the sqlite single-writer contact point
with alkblobs' machinery on the same file — contention behavior is
part of what the POC measures), the door routing over the batch
adapter. Both outcomes are wins taken before publish: the posture
gets its evidence and the build deferral stands unchanged; or
substrate gaps surface now, while they are cheap upstream asks
rather than "real" problems discovered at this level.
- **Blocked on**: the build on the first deployment/client that needs
the lock API (contention control for shared big-media files); the
validation on its booked work item — it can run any time before
the v0.1 publish (a not-yet-existing internal artifact, the
deferred(scope) booked-item case) — a natural next slot alongside
OQ-19's wire-validation sessions.
- **Cross-references**: lfs.md, doors.md, OQ-03 (the publish gate),
OQ-19 (the sibling owed-validation, same honesty rule)