- ServeConfig gains cfg-gated SessionSeam / GitSeam Option fields (whole-Option struct per seam — the shape decision recorded in the task notes): unset → the owned rows decline even though the feature is on, no construction-time validation - SessionSeam carries the alktty backends map + backend selector + ownership provider; GitSeam carries the alkgit backend quartet + caller-owned pipeline semaphore + push_options_negotiated - optional deps alktty 0.8.1 / alkgit 0.1.0 wired feature-to-dep (pins verified against crates.io: licenses, MSRV, shared alkcall resolution); gix-hash 0.28 as dev-dep only (alkgit does not re-export ObjectId for the trait doubles) - per-feature construction/unset-declines/Debug rows in config.rs; accessors session_seam_configured / git_seam_configured; docs state what each seam handle carries Verified: cargo test (per-feature + --all-features), clippy ×2 (-D warnings), fmt, cargo doc --no-deps, cargo tree feature isolation (git-build: no alktty; session-build: no alkgit), taskgraph validate
198 lines
10 KiB
Markdown
198 lines
10 KiB
Markdown
---
|
|
id: w3-seam-config
|
|
name: Wave 3 — serve-config seam handles (alktty/alkgit per-feature)
|
|
status: completed
|
|
depends_on: [review-wave-2, w1-config]
|
|
scope: narrow
|
|
risk: low
|
|
impact: component
|
|
level: implementation
|
|
---
|
|
|
|
## Description
|
|
|
|
Extend [`crate::ServeConfig`] with the per-feature family-seam handles
|
|
the wave-3 mappings consume. This is wave 3's first *course
|
|
correction* to the wave-1 config surface — the assembly-supplied
|
|
composition inputs were never carried (mapping.md's seam-entries table
|
|
names them inside the seam signatures; the wave-1 config bullet
|
|
pre-dates that reading), and the mappings cannot call
|
|
`drive_session[_pre_negotiated]` / `serve_duplex_resolved` without
|
|
them. Landing them here, before the mapping tasks, keeps the
|
|
un-set-declines posture structural and the review surface unified:
|
|
|
|
- **The `session` seam handle** (feature-gated), unset → the session
|
|
rows decline even with the feature on (the decline matrix's "an
|
|
unset field declines its row though the feature is on" note,
|
|
config-driven). Contents, per alktty 0.8.1's actual
|
|
`drive_session[_pre_negotiated]` signature — decide the exact final
|
|
shape (flat `Option` fields vs an `Option`-struct per seam; record):
|
|
- the alktty backends map (`Arc<HashMap<String,
|
|
Arc<dyn TtyBackend>>>` — assembled by the assembly; alkssh never
|
|
touches a real backend, the `local` shapes stay alktty-side);
|
|
- the backend **selector** — the `NegotiateRequest.backend` string
|
|
alkssh writes (assembly-supplied, never wire-derived; decide and
|
|
record);
|
|
- the ownership provider `Option<Arc<dyn
|
|
alkcall::core::ownership::OwnershipProvider>>` (an alkcall type —
|
|
no new dep; pass-through to `drive_session_pre_negotiated`,
|
|
family ACL layer riding along).
|
|
- **The `git` seam handle** (feature-gated), unset → the git rows
|
|
decline even with the feature on. Contents, per alkgit 0.1.0's
|
|
`serve_duplex_resolved` signature: the four backend traits
|
|
(`GitRegistry` / `GitRefs` / `GitPackGen` / `GitPackIngest`), the
|
|
caller-owned server-wide `pipeline: Arc<tokio::sync::Semaphore>`,
|
|
and `push_options_negotiated: bool` (the assembler's config flag;
|
|
false in the v1 default).
|
|
- **Manifest** (the wave-1 rule: per-feature sibling deps land with
|
|
their first consuming wave — the config fields consume them first,
|
|
so they are not dead entries): optional deps
|
|
`alktty = "0.8.1"`, `alkgit = "0.1.0"` wired feature-to-dep
|
|
(`session = ["dep:alktty"]`, `git = ["dep:alkgit"]`) so a git-build
|
|
never compiles alktty and vice versa; verify the pins against
|
|
crates.io before committing (verified at decomposition: 0.8.1 /
|
|
0.1.0, both over the shared `alkcall 0.10` req → 0.10.1; MSRVs 1.88
|
|
≤ our 1.89; MIT OR Apache-2.0). **alktunnels is NOT landed here** —
|
|
the config carries no alktunnels types beyond the already-landed
|
|
`Arc<ChannelClient>`; its optional dep lands with its first
|
|
consuming code (`w3-tunnel-direct`).
|
|
|
|
No construction-time validation (the unset-declines contract lives in
|
|
the type shape, checked at request time by the mappings); the tunnel
|
|
handle stays `Option<Arc<ChannelClient>>` as landed; `Debug` impls and
|
|
the config module docs updated (the direction list now states what
|
|
each seam handle carries).
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] `ServeConfig` gains the feature-gated seam handles (the final
|
|
shape decided and recorded); unset → the owned rows decline,
|
|
structurally carried, no construction-time validation
|
|
- [x] The optional deps + `dep:` feature wiring land with crates.io
|
|
pins verified; a git-build never compiles alktty and vice versa
|
|
(no code path outside the cfg gate touches the types)
|
|
- [x] The none-build's `ServeConfig::new` shape is unchanged;
|
|
feature-gated fields absent from featureless builds
|
|
- [x] Tests: the per-feature construction shapes, the
|
|
unset-declines-preserved rows, the `Debug` posture
|
|
- [x] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
|
|
`cargo clippy --all-features --all-targets -- -D warnings`,
|
|
`cargo fmt --check`, `cargo doc --no-deps` pass
|
|
|
|
## References
|
|
|
|
- docs/plans/implementation.md §Wave 1 (config bullet), §Wave 3
|
|
- docs/architecture/mapping.md §The seams (the signature shapes),
|
|
§Session-channel dispatch (the unset note), §Decline matrix
|
|
- docs/architecture/decisions/002-russh-pin.md (the manifest rule),
|
|
docs/architecture/decisions/003-feature-graph.md (the feature graph)
|
|
- alktty 0.8.1: `src/adapter.rs:205-241` (`drive_session[
|
|
_pre_negotiated]`), `src/negotiation.rs:64-92` (`NegotiateRequest`)
|
|
- alkgit 0.1.0: `src/duplex.rs:1412-1426` (`serve_duplex_resolved`),
|
|
`src/backend.rs:31-162` (the four traits)
|
|
|
|
## Notes
|
|
|
|
### What landed (2026-10-28)
|
|
|
|
- `Cargo.toml`: optional `alktty = "0.8.1"` /
|
|
`alkgit = "0.1.0"` (both `default-features = false`), wired
|
|
`session = ["dep:alktty"]`, `git = ["dep:alkgit"]`. alktty's `local`
|
|
feature stays off — alkssh never touches a real backend; the `local`
|
|
shapes stay alktty-side. Feature-graph comment updated (wave-3
|
|
seam-config is the deps' first consumer; alktunnels still lands with
|
|
`w3-tunnel-direct`).
|
|
- `src/config.rs` (re-exported through `lib.rs`): the two seam-handle
|
|
types + the two cfg-gated `ServeConfig` fields + the cfg-gated
|
|
`session_seam_configured()` / `git_seam_configured()` accessors +
|
|
config-module docs updated (the direction list and the
|
|
unset-declines rule both state the seam handles now).
|
|
|
|
### Shape decision (the task's decide-and-record item): whole-`Option` struct per seam
|
|
|
|
`SessionSeam{backends, backend, ownership}` and
|
|
`GitSeam{registry, refs, packgen, ingest, push_options_negotiated,
|
|
pipeline}` held as `cfg`-gated `Option` fields
|
|
(`session_seam` / `git_seam`), each `Clone` (all payload types `Arc`
|
|
or plain data — the spawned serving tasks clone the seam), each with a
|
|
hand-rolled `Debug`. **Flat `Option` fields were rejected**: each seam
|
|
is consumed atomically by exactly one seam call
|
|
(`drive_session[_pre_negotiated]` / `serve_duplex_resolved`), so a
|
|
struct admits no half-supplied seam states (backends without a
|
|
selector; three traits without the fourth) that would all map to the
|
|
same all-or-nothing decline — the unset-declines contract reads as one
|
|
`Option` per seam, "unset → the owned rows decline even though the
|
|
feature is on" (the decline matrix's config-driven note), checked at
|
|
request time by the mappings, never at construction. The already-landed
|
|
tunnel handle stays the flat
|
|
`Option<Arc<ChannelClient>>` — a single-field seam needs no struct.
|
|
Naming: struct names carry the crate (the consuming mappings will see
|
|
it in the field docs); the fields carry the row vocabulary
|
|
(`session_seam` / `git_seam`, matching the decline matrix's row names).
|
|
|
|
### Pins verified against crates.io (2026-10-28, this task re-verified)
|
|
|
|
- `alktty 0.8.1`: published 2026-10-22, not yanked,
|
|
`MIT OR Apache-2.0`, `rust_version = "1.88"` ≤ our 1.89;
|
|
`alkcall ^0.10.1` — shares our `alkcall = "0.10"` resolution
|
|
(0.10.1), so no alkcall fork in the lockfile.
|
|
- `alkgit 0.1.0`: `MIT OR Apache-2.0`, `rust_version = "1.88"`;
|
|
`alkcall ^0.10` — same shared resolution.
|
|
- Signatures confirmed against the published crates (the registry
|
|
sources under `~/.cargo/registry/src/`): alktty's
|
|
`drive_session[_pre_negotiated]` (adapter.rs:205-241) takes
|
|
`(writer, reader, [req,] backends: Arc<HashMap<String, Arc<dyn
|
|
TtyBackend>>>, ownership: Option<Arc<dyn OwnershipProvider>>,
|
|
identity: Option<Identity>)` — the identity is the per-connection
|
|
resolved one, not config; alkgit's `serve_duplex_resolved`
|
|
(duplex.rs:1412) takes the four traits +
|
|
`push_options_negotiated: bool` + `pipeline: Arc<Semaphore>`.
|
|
`TtyBackend` / `NegotiateRequest` / `TerminalParamsWire` and
|
|
`GitRegistry/GitRefs/GitPackGen/GitPackIngest` are all re-exported
|
|
from the two lib roots.
|
|
|
|
### One course correction absorbed: `ObjectId` is not re-exported by alkgit
|
|
|
|
`GitPackGen::generate`/`common_haves` name `Vec<gix_hash::ObjectId>`
|
|
and alkgit's lib root does not re-export `ObjectId` — a backend-trait
|
|
doubled in tests has to name the type, so `gix-hash = "0.28"` (matching
|
|
alkgit's `^0.28` req; the same crate already in the lockfile) lands as
|
|
a **`[dev-dependencies]` entry only**, exercised exclusively by the
|
|
cfg-gated git test module. The lib dependency graph keeps the
|
|
feature-to-dep isolation below; the dev graph is universal by design
|
|
(tokio and friends are too). Flagged for `w3-git-mapping`: the
|
|
hand-rolled in-mem quartet will lean on this dev-dep for `ObjectId`
|
|
(same as this task's noop doubles, modeled on alkgit's own
|
|
`backend.rs:164-254` noop shapes — own code, not copied).
|
|
|
|
### Dependency isolation — verified structurally, not by assertion
|
|
|
|
`cargo tree -e features --features git`: zero `alktty` occurrences;
|
|
`cargo tree -e features --features session`: zero `alkgit`
|
|
occurrences; `--all-features`: both present. Combined with the cfg
|
|
gates (all `alktty`/`alkgit` type references live inside
|
|
`#[cfg(feature = "session")]` / `#[cfg(feature = "git")]` blocks —
|
|
imports, fields, accessors, Debug, constructor, tests), the "a
|
|
git-build never compiles alktty and vice versa" invariant is
|
|
compile-proven for both consumption sides.
|
|
|
|
### The unset-declines wire-level rows stay the mappings' consumption
|
|
|
|
The wave-3 mappings own the request-time decline checks; this task
|
|
lands the config-level contract rows (constructor posture, `Option`
|
|
configured() semantics, seam-vs-OQ-08-field independence, Debug
|
|
posture — 8 unit rows across the two feature modules) and leaves the
|
|
wave-2 harness rows untouched (featured-but-unset already declines at
|
|
the row the stubs own; the real serving paths make those rows
|
|
meaningful in `w3-git-mapping` / `w3-tty-session`, whose seam
|
|
consumption reads exactly these fields).
|
|
|
|
### Verification (all green)
|
|
|
|
`cargo test` (none build, 9 binaries + doctests) ·
|
|
`cargo test --features session` / `--features git` /
|
|
`--all-features` (67 unit rows under all-features) ·
|
|
`cargo clippy --all-targets -- -D warnings` ✓ ·
|
|
`cargo clippy --all-features --all-targets -- -D warnings` ✓ ·
|
|
`cargo fmt --check` ✓ · `cargo doc --no-deps` 0 warnings ·
|
|
`taskgraph validate` ✓. |