Six implementation tasks + the gate under tasks/implementation/, chained sequentially (the mappings replace stubs in the same Handler impl and share the decline-matrix test binaries): - w3-seam-config: the serve-config's per-feature seam handles (alktty backends/ownership/selector; the alkgit quartet/pipeline/ push-options flag) + the optional sibling deps — wave 3's first course correction to the wave-1 config surface - w3-decline-tracing: the wave-2 gate's booked finding — the decline/serve-path tracing vocabulary, landed before the mappings - w3-git-mapping: the full exec grammar replaces the wave-2 probe + the serve_duplex_resolved dispatch + the store-and-take handoff - w3-tty-session: the translation bridge + the exit-derivation contract (rules 1-6, the signum table) - w3-tunnel-direct: the route-1 bridge (accept only after the fabric open succeeds) + set_identity propagation + the capacity-across- fabric-window mechanics - w3-tunnel-listen: the -R listen flow + the forwarded-tcpip data leg (the POC-untested leg; the stop-and-book provision) - review-wave-3: the gate The plan carries the wave-3 decomposition record with four interpretations flagged for the gate (the serve-config course correction, the already-discharged server-side sftp bullet, the w3-tunnel-listen open mechanics, the booked-items' wave-3 homes). taskgraph validate: 26 tasks, clean. Sibling pins verified against crates.io at decomposition time: alktty 0.8.1 / alkgit 0.1.0 / alktunnels 0.4.1 / alkcall 0.10.1.
124 lines
6.4 KiB
Markdown
124 lines
6.4 KiB
Markdown
---
|
||
id: w3-git-mapping
|
||
name: Wave 3 — the git mapping (exec grammar + alkgit dispatch)
|
||
status: pending
|
||
depends_on: [w3-decline-tracing]
|
||
scope: moderate
|
||
risk: medium
|
||
impact: phase
|
||
level: implementation
|
||
---
|
||
|
||
## Description
|
||
|
||
Land the git branch of the spine (ADR-006 decision 2, exec-only) — the
|
||
alkforge primary purpose: the full exec grammar replaces the wave-2
|
||
skeleton probe, and the git row becomes a real dispatch to the
|
||
published alkgit seam. Everything POC-3 stage A proved, re-landed as
|
||
contract.
|
||
|
||
- **The full grammar** (replaces `dispatch::classify_exec` — the
|
||
wave-2 interpretation's named replacement; its 7 probe unit rows
|
||
migrate): `git-{upload,receive}-pack '<path>'` — quoted/unquoted
|
||
paths; strip the quotes and the leading `/` (POC-3 stage A rows:
|
||
what real OpenSSH git sends); unknown services
|
||
(`git-send-pack`, `git-upload-archive`) → decline-with-reason, the
|
||
honest surface; service-only (no repo) → decline-with-reason —
|
||
POC-3 §A: SSH exec must carry the repo; per-length limits consumed
|
||
at last — `Limits::max_exec_command_len` enforced **before** any
|
||
parse or allocation (decline-first, alloc-never; the wave-2
|
||
carried-not-enforced reservation). Decide the wire shapes per
|
||
decline class (open/reply vocabulary, whether "unknown service" and
|
||
"not git at all" share a reason shape) and record.
|
||
- **Repo path → repo_id, no existence oracle**: the parse validates
|
||
via `alkgit::validate_repo_id` (1–2 segments, charset, ≤ 100 bytes)
|
||
and hands the id through — alkssh never resolves; the
|
||
**collapsed generic denial before any protocol byte** is
|
||
`serve_duplex_resolved`'s `SubstrateError::AccessDenied` posture,
|
||
contract-pinned here: over the SSH channel, no pack/ref byte
|
||
precedes the denial band (the POC-3 §A.5 byte-order evidence, now a
|
||
test row). Decide the `.git`-suffix normalization against the
|
||
alkgit registry contract (what vanilla git sends vs
|
||
`validate_repo_id`'s grammar) and record.
|
||
- **The dispatch** — the git row becomes real: the taken channel
|
||
(the store-and-take handoff lands **here first** — the accepted
|
||
channel is stored at the open, `channels.remove(&id)` →
|
||
`into_stream()` in the spawned serving task; the session task
|
||
reuses the store) → `alkgit::serve_duplex_resolved(repo_id,
|
||
identity, service, duplex, limits, registry, refs, packgen,
|
||
ingest, push_options_negotiated, pipeline)` — the serve-config's
|
||
`git` seam handle (`w3-seam-config`), the resolved identity riding
|
||
the `identity` param. Process-mode dialect: alkgit speaks first
|
||
(the V0 advertisement, no service prefix) — nothing alkssh-side;
|
||
verify the dialect holds over the channel duplex with a byte-level
|
||
row.
|
||
- **Exit-status both directions** (POC-3 §A.5): clean serve end →
|
||
`exit-status(0)`... decide the code mapping (`SubstrateError`
|
||
outcomes → the code — POC evidence: denied → the client observed
|
||
`ExitStatus{1}`; vanilla git exits 0 on success) — sent before
|
||
close, from the spawned task via the cloned `Session::handle()`
|
||
(the wave-2 mechanics reused). Record the decision.
|
||
- **Identity** — the parked wave-2 deferred item discharges here:
|
||
the resolved identity (the bridge's `auth_succeeded` ride-along)
|
||
reaches the seam — assert it live (the authorize-path/witness row
|
||
the harness can observe; decide the cheapest row) so the
|
||
visible-surface invariant is observable through the git row.
|
||
- **Contract rows through the harness, per build** — the
|
||
`tests/dispatch_core.rs` skeleton rows upgraded: git-only serves
|
||
the git row through the doubles (clone/push semantics over the
|
||
in-mem composition), declines plain exec/pty/shell (parse-then-fail,
|
||
no resource alloc); session-only still declines git-parseable exec;
|
||
none-build still declines at open; under `--all-features` the
|
||
routing holds. The declined rows keep their wire shapes
|
||
(`channel_failure`, channel usable / serving-path failure+close per
|
||
class).
|
||
- **The real-tool smoke** — one `git clone` (2.43+) through the
|
||
harness against hand-rolled in-mem alkgit doubles (alkgit's `Mem*`
|
||
are test-private — alkssh hand-rolls the minimal quartet per
|
||
`alkgit/src/backend.rs:164-254`'s noop shapes; never sibling-checkout
|
||
paths, never copies of the doubles' code). Decide the tooling
|
||
posture (require git in the environment vs conditional-skip —
|
||
skipped-with-reason at most, never silently green) and record.
|
||
|
||
## Acceptance Criteria
|
||
|
||
- [ ] The full grammar replaces the probe (quoted/unquoted, unknown-
|
||
service vs non-git decline rows, service-only decline, the
|
||
`max_exec_command_len` pre-parse cap); the probe's unit rows
|
||
migrate/extend
|
||
- [ ] The git row dispatches to `serve_duplex_resolved` over the
|
||
channel duplex; process-mode dialect verified byte-level;
|
||
declined routes and the `AccessDenied`-before-protocol-bytes
|
||
order pinned by contract rows
|
||
- [ ] The store-and-take handoff lands (channel stored at open, taken
|
||
in the serving task), structured so the session task reuses it
|
||
- [ ] The exit-status mapping (clean end / terminal error) decided +
|
||
recorded; status before close from the spawned task
|
||
- [ ] The identity ride asserted live through the seam (the parked
|
||
wave-2 item discharged here)
|
||
- [ ] The decline-matrix rows per build green through the harness
|
||
(git-only / session-only / none / all-features), each with its
|
||
contract stamp; the real-tool clone smoke row
|
||
- [ ] `cargo test`, `cargo test --features git --test <git rows>`,
|
||
`cargo test --all-features`, clippy ×2 (`-D warnings`), fmt,
|
||
`cargo doc --no-deps` pass
|
||
|
||
## References
|
||
|
||
- docs/architecture/mapping.md (the git channel-type row; §Session-
|
||
channel dispatch — decline-first-alloc-never; §the structural
|
||
constraint)
|
||
- docs/architecture/decisions/006-mapping-bridge-and-dispatch.md
|
||
(decision 2 — exec-only; decision 4 — spawn-and-return)
|
||
- docs/architecture/decisions/004-auth-dispositions.md (the identity
|
||
ride), docs/architecture/auth.md §Server direction (step 4)
|
||
- docs/research/poc-3-mapping-bakeoff.md §Stage A (the grammar rows,
|
||
the denial byte-order, the exit evidence, the spawn rule)
|
||
- docs/research/mapping-table.md (the validated research ancestor)
|
||
- alkgit 0.1.0: `serve_duplex_resolved` (`src/duplex.rs:1412`),
|
||
`SubstrateError` (`src/substrate.rs:116`), `validate_repo_id`
|
||
(`src/registry.rs:132`), the trait shapes (`src/backend.rs:164-254`)
|
||
|
||
## Notes
|
||
|
||
> To be filled at implementation time. |