feat(observe): land the decline-path event vocabulary (w3-decline-tracing)
The wave-2 gate's booked finding: observability on the decline paths. `tracing` rides the manifest per the ADR-002 toolkit but nothing in src/ emitted — a decline's "reason" was only the routing decision documented per build. - src/observe.rs: the event vocabulary, adopting the family posture read from alktty/alktunnels/alkcall/alkhttp (free tracing fns, message-last lowercase, flat fields; no info!/trace!, no macros, no spans): feature_build + row + channel_id + decline_class (open/request/serving/auth) + reason + exec_route (exec rows); declines at debug, the serving-path failure arm the vocabulary's one warn - handler.rs: every decline site emits — auth dispositions (non-ed25519 / fingerprint-unresolved / the four unimplemented methods), open-level declines (unfeatured / never-feature / cap-exceeded / tunnel connect-failed stub), request-level capability declines (exec route-aware, subsystem, env, x11, agent, the -R listen forms), and the seam-stub serving declines with the spawned-path lifecycle (started / ended / failed — the shape the wave-3 mappings inherit) - tests/observe.rs: the event-presence pin — a hand-rolled global capture subscriber (the recorded test convention; no event-capture precedent exists in the family), field-matching rows across the default/git/session/tunnel/--all-features builds, and the safety scan (no username, no fingerprint ever in an event) - helper-level exact shapes pinned in src/observe.rs unit rows Verification: cargo test (default, --features git/session/tunnel, --all-features all green), clippy x2 --all-targets -D warnings, fmt --check, cargo doc --no-deps 0 warnings, taskgraph validate.
This commit is contained in:
1 parent
270365ea4c
commit
58d0b86ec5
6 files changed
+1490
-29
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: w3-decline-tracing
|
||||
name: Wave 3 — decline-path tracing (the wave-2 gate's booked finding)
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: [w3-seam-config]
|
||||
scope: narrow
|
||||
risk: low
|
||||
@@ -46,16 +46,16 @@ inconsistent events.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] The family tracing posture read and recorded; the event
|
||||
- [x] The family tracing posture read and recorded; the event
|
||||
vocabulary established (row + build + channel + decline class)
|
||||
and used at every decline site in `src/`
|
||||
- [ ] Serve-path skeleton events in place (start/ended/failure) so the
|
||||
- [x] Serve-path skeleton events in place (start/ended/failure) so the
|
||||
mapping tasks' spawned paths inherit shaped rows
|
||||
- [ ] The safety posture recorded (no secrets, no username; what
|
||||
- [x] The safety posture recorded (no secrets, no username; what
|
||||
identity detail may appear decided)
|
||||
- [ ] A test pins the event presence at the decline sites (or the
|
||||
- [x] A test pins the event presence at the decline sites (or the
|
||||
doc-test convention the family uses — decide + record)
|
||||
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
|
||||
- [x] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
|
||||
`cargo fmt --check`, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
@@ -70,4 +70,152 @@ inconsistent events.
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled at implementation time.
|
||||
### The family posture read (2026-10-28)
|
||||
|
||||
Read before choosing the event shape (the task's first bullet), across
|
||||
the family's `src/`:
|
||||
|
||||
- **alktty** (`adapter.rs`, `session.rs`, `channels.rs`,
|
||||
`local/pipe.rs`, `local/pty.rs`): free `tracing::{debug, warn}`
|
||||
imports; message-last lowercase sentences with a component prefix
|
||||
(`"tty: backend stdin write failed: {e}"`); interpolation in
|
||||
messages, no spans.
|
||||
- **alktunnels** (`producer.rs`, `consumer.rs`, `params.rs`): the same
|
||||
free-fn posture, plus structured fields where identity exists
|
||||
(`listen = …`, `error = %e`, `reason = …`); levels `debug` for
|
||||
structural observations, `warn` for recoverable failures, `error`
|
||||
for abnormal pump terminations.
|
||||
- **alkcall** (`protocol/*`, `channels/*`): the widest use — the same
|
||||
fields convention (`channel_id`, `request_id`, `error = %e`),
|
||||
component-prefixed messages (`"channel client: …"`, `"open wrapper:
|
||||
…"`), `debug`/`warn` only.
|
||||
- **alkhttp** (the closest structural analog): `warn`/`error` with
|
||||
`error = %e` (`"channels session ended"`).
|
||||
- **alkgit**: no tracing at all at this writing.
|
||||
|
||||
Never observed: `info!`/`trace!` events, subscriber setup in library
|
||||
code, macro or span wrappers. Adopted verbatim in `src/observe.rs`:
|
||||
free `tracing` fn calls, message-last lowercase with the
|
||||
`"decline: …"` / `"serving path …"` phrasing, flat per-event fields.
|
||||
|
||||
### The event vocabulary (landed in `src/observe.rs`)
|
||||
|
||||
- **`feature_build`** — the compile-time feature set as a stable
|
||||
string (`"none"`, `"git"`, `"session+git"`, …; features join in the
|
||||
order `session, git, tunnel, sftp`), so a captured event is
|
||||
self-describing per build; `sftp` ships in the string even though it
|
||||
gates no route (it is part of what the assembly deployed).
|
||||
- **`row`** — the greppable wire row: the channel type for open-level
|
||||
(`"session"`, `"direct-tcpip"`, `"x11"`, …), the request name for
|
||||
request-level (`"pty_request"`, `"tcpip_forward"`, …), the handler
|
||||
hook name for auth (`"auth_publickey"`, `"auth_none"`, …).
|
||||
- **`decline_class`** — `"open"` | `"request"` | `"serving"` |
|
||||
`"auth"`: the two wire decline classes (`mapping.md` §Wire decline
|
||||
reasons), plus the serving-path class (this wave the seam stub's
|
||||
decline *is* the serving path; the mappings emit the same class from
|
||||
their real spawned paths) and the auth class (the ADR-004
|
||||
disposition declines — added by this task: the auth hooks are
|
||||
decline sites too, and `auth.md`'s dispositions are the crate's
|
||||
auth-decline contract).
|
||||
- **`reason`** — the decline cause, mirroring the architecture's own
|
||||
classifications: `"unfeatured"`, `"never-feature"` (ADR-003 §5),
|
||||
`"cap-exceeded"`, `"connect-failed"` (tunnel stub / ADR-006
|
||||
decision 1), `"seam-stub"` (session-row stubs), `"unimplemented"`
|
||||
(subsystem rows — OQ-13 — and the wave-3 listen forms),
|
||||
`"declined-by-design"` (env — OQ-09), and on the auth class
|
||||
`"method-unimplemented"` / `"cert-unsupported"` / `"non-ed25519"` /
|
||||
`"fingerprint-unresolved"`.
|
||||
- **`channel_id`** — the wire channel id (u32), on every decline of a
|
||||
channel (notably: the event's id is *russh's server-side* id —
|
||||
`ChannelId` is not publicly constructible, so the helpers take the
|
||||
raw u32 via `ChannelId::number()`); the auth class and the global
|
||||
`-R` listen forms carry none (a separate `decline_global_request`
|
||||
helper — global requests have no channel).
|
||||
- **`exec_route`** — only on `exec_request`-row events: the parse
|
||||
route (`"git"` | `"pipe"`), so a git row's decline is
|
||||
distinguishable from a pipe row's in combined builds. A `None` route
|
||||
field is not recorded at all by `tracing` (discovered by the unit
|
||||
rows; recorded in the module docs). The `message` field rides first
|
||||
(the `event!` macro's shape — the unit rows pin the field order).
|
||||
|
||||
Levels: every decline and the lifecycle's started/ended emit at
|
||||
`debug` — a decline is the door's normal, honest posture (vanilla
|
||||
clients probe unfeatured rows routinely), matching the family's
|
||||
structural-observation level. Only [`serving_path_failed`] is `warn`
|
||||
(a serving path that could not complete its wire reporting is a real
|
||||
failure — the family's recoverable-failure level).
|
||||
|
||||
### The serving-path skeleton
|
||||
|
||||
`src/serve.rs` stays the mechanics only (spawn + exit-report); the
|
||||
lifecycle events hang off the spawned task's body in
|
||||
`handler.rs`'s `seam_stub_decline`: started (the spawned task's first
|
||||
action, so "started" is the task's honest start, not the hook's spawn
|
||||
call) → the serving-path decline (class `serving`, reason `seam-stub`)
|
||||
→ ended — or failed (`warn`, with the russh protocol error) when a
|
||||
report send could not complete. The wave-3 mapping branches
|
||||
(`w3-git-mapping`, `w3-tty-session`, `w3-tunnel-direct`) inherit the
|
||||
shaped rows by emitting the same helpers from their real serving tasks.
|
||||
|
||||
### The safety posture (conventions 6, OQ-03 — recorded)
|
||||
|
||||
**No secret material ever enters an event; never the username;
|
||||
the fingerprint stays out too.** Structurally enforced, not recalled:
|
||||
the vocabulary helpers take only `&'static str` values and the wire
|
||||
channel id — no parameter a username, command string, env name/value,
|
||||
repo path, key, or fingerprint could ride. The `reason` codes carry
|
||||
the decline's *cause class*, and the deciding context is the row +
|
||||
build + channel — deliberately *not* the identity behind the wire
|
||||
attempt. This is the recording of auth.md §Dispositions' standing
|
||||
rule at the crate level: log exposure is deployment policy, so the
|
||||
crate's events are safe-by-default at every level — the fingerprint
|
||||
(and any identity detail) is a deployer-side computation over its own
|
||||
logs, never crate-emitted. If a future decline site needs
|
||||
request-argument detail (e.g. the git grammar's decline reasons in
|
||||
`w3-git-mapping`), it must add an explicitly-vocabulary'd field and
|
||||
record its safety reasoning — dynamic strings do not ride
|
||||
`src/observe.rs` by default. The wire rows pin the posture by
|
||||
scanning every captured event: no username string, no `SHA256:`
|
||||
fingerprint, no `user*`-named field ever appears.
|
||||
|
||||
### The test convention (decided + recorded)
|
||||
|
||||
The family has no event-capture precedent to adopt — alktunnels'
|
||||
`tracing-subscriber` dev-dep drives fmt output, not assertions. The
|
||||
convention these rows pin: **a hand-rolled minimal
|
||||
`tracing::Subscriber`** capturing events into a shared buffer,
|
||||
installed once as the global default (`tests/observe.rs`) — the
|
||||
server side runs on spawned tasks, so a thread-local
|
||||
`with_default` capture cannot see them, and a global capture needs no
|
||||
new dependency. Two levels of pinning:
|
||||
|
||||
- Helper-level (`src/observe.rs` unit rows, thread-local
|
||||
`with_default`): exact field sets and order (message first), the
|
||||
`exec_route` presence semantics, the no-channel auth shape, the
|
||||
lifecycle messages, the `warn` failure arm, and the
|
||||
cfg-derived `feature_build` string.
|
||||
- Wire-level (`tests/observe.rs`, the loopback harness drives the
|
||||
real wire; capture installed up front via the mount helpers — a
|
||||
lazily-installed global loses the earliest emissions): presence by
|
||||
field-matching at every decline site across the builds — x11 open,
|
||||
`-R` listen (global), the fingerprint-unresolved and auth_none
|
||||
dispositions (driven on fresh mounts — a second auth exchange on an
|
||||
already-authenticated session is not a supported russh surface, as
|
||||
the probe discovered), the unfeatured session/pipe/pty rows, the
|
||||
git seam-stub's full lifecycle, the tunnel stub's `connect-failed`,
|
||||
the cap-exceeded row (zero-cap limits must ride the *server* config
|
||||
— `serve_config`, not just the client's `Limits`), and the
|
||||
username/fingerprint safety scan. Build-specific rows follow the
|
||||
`dispatch_core.rs` per-build cfg convention and were verified in
|
||||
the default, `git`, `session`, `tunnel`, and `--all-features`
|
||||
builds.
|
||||
|
||||
### Verification (all green)
|
||||
|
||||
`cargo test` default build 110 rows across 7 binaries + doctests ·
|
||||
`cargo test --all-features` ✓ · `cargo test --features git/session/tunnel`
|
||||
✓ (each build runs its decline rows) ·
|
||||
`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` ✓.
|
||||
Reference in new issue
Block a user