Renames the written-usage rule into practice across the contract surface: docs/ (architecture, research, plans, sdd), tasks/ (all 43 task files, taskgraph validate passes), AGENTS.md, README.md, CHANGELOG.md, and the .opencode/agents/ prompts. Every replacement is a sense-preserving rewrite (colons, semicolons, commas, parentheses, sentence splits, varied), never a mechanical swap; no spaced hyphens were substituted. Enum fields, frontmatter, tables, wire data, and cited identifiers survive verbatim; en dashes inside numeric ranges keep their meaning. Verification: taskgraph validate (43 tasks), repo grep clean (single exempt hit: the verification command's own search pattern in AGENTS.md), cargo test, clippy --all-targets -D warnings, fmt --check, doc all green.
7.8 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |||
|---|---|---|---|---|---|---|---|---|---|---|
| w1-config | Wave 1: the config surface (serve-config, dial-config, Limits) | completed |
|
moderate | low | project | implementation |
Description
Land the pure configuration types both directions implement against, the shapes every later wave's constructors take:
- Serve-config (server direction,
mapping.md/overview.md), the assembly-supplied serving configuration: the OQ-08 fields (shell argv and exec wrapper argv, eachOption<Vec<String>>; when unset, its row declines with a clean error, never a fallback, no crate default; the wrapper form is platform-neutral), theIdentityProviderhandle (Arc<dyn IdentityProvider>, which the assembly injects; the crate never resolves identities itself), the host-key source handle (the wave-1 key-source seam), the tunnelChannelClienthandle (the route-1 bridge's family-fabric entry, held as anOption; when absent, tunnel rows decline even under the feature), and theLimits.cwd/envinNegotiateRequestare assembly-supplied serve-config values, never wire-derived (OQ-09 posture). - Dial-config (client direction,
client.md§The dial surface): address, key source (the wave-1 seam; never static keys, never env vars), theHostKeyPolicyhandle, and the bounded-resource knobs:keepalive_interval/keepalive_max(15 s / 3 default posture per the reverse-runner evidence),inactivity_timeout, window/packet knobs. Limits: the bounded-resources budget model (overview.md's table): the in-crate max-concurrent-channels cap, exec-parse length caps (consumed by the wave-3 grammar), and the russh knob mapping with documented default postures (max_auth_attemptsdefault 10, sized for the countingnone-probe per ADR-004 decision 6;window_size/maximum_packet_size/channel_buffer_sizeset;inactivity_timeoutset, assembly-tunable). Every session carries it; no unbounded loops, no unbounded buffers.
Types only: no russh Config construction (wave 2), no dialing
(wave 4). The OQ-08 unset-declines semantics live in the type shape
(Option fields + doc contracts), not in construction-time
validation.
Acceptance Criteria
- Serve-config carries the OQ-08 fields as
Optionwith unset-declines doc contracts; no crate default shell/wrapper IdentityProvider, host-key source, andChannelClientare injected handles; the crate holds no identity/trust policy of its own- Dial-config carries key source +
HostKeyPolicy+ the keepalive/inactivity/window knobs with the documented defaults Limitscarries the in-crate caps (channels, exec-parse) and the russh knob defaults (max_auth_attempts10, keepalive 15 s / 3 client posture)- Unit tests cover default construction and the
Option-field semantics cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --checkpass
References
- docs/architecture/overview.md §Public surface, §Bounded-resources policy
- docs/architecture/mapping.md (OQ-08 rows, the data-plane/limits section)
- docs/architecture/client.md §The dial surface
- docs/architecture/open-questions.md (OQ-08, OQ-09 resolutions)
- docs/architecture/decisions/004-auth-dispositions.md (decision 6:
the
max_auth_attemptsdefault)
Notes
Landed
src/config.rs(ServeConfig,DialConfig,Limits, four posture constants) + lib.rs re-exports. Design decisions pinned against the docs and the alkcall/russh 0.65 source:
Limitsis the single budget model both directions carry (Copy, plain tuning data): the in-crate caps (max_concurrent_channels64,max_exec_command_len8192, with numbers chosen here and documented as assembly-tunable posture constants) plus the russh-knob mapping (max_auth_attempts10 per ADR-004 decision 6;window_size2 MiB /maximum_packet_size32 KiB /channel_buffer_size100, russh's own defaults kept;inactivity_timeout600 s carried as plainDurationso the "set (assembly-tunable)" posture is structural, with wave 2/4 mapping it to russh'sOptionas always-Some;keepalive_interval/keepalive_maxSome(15 s)/3 riding inside as the client-direction posture, satisfying the dial-config criterion without a duplicate knob set). russh's rekeyLimitsstay russh-enforced at their RFC-4253 defaults and are not carried (documented; wave-2 material).- Dial-config carries the knobs via its
Limitsfield: the criterion's "keepalive/inactivity/window knobs with the documented defaults" read as the budget model riding the config; the accepted criterion onLimitsitself pins that placement, so a second knob set onDialConfigwould have duplicated state.- No
Defaultfor the two configs: the required handles (Arc<dyn IdentityProvider>,Arc<dyn HostKeySource>,Arc<dyn KeySource>,Arc<dyn HostKeyPolicy>) have no crate-defaultable shape (identity resolution and trust are injection-only, per the criterion);ServeConfig::new/DialConfig::newtake them, and every tunable is apubfield +Limits::default().- **The OQ-08 fields are plain
Option<Vec<String>>pubfields
- doc contracts**; no construction-time validation (the
error.rsno-config-error contract). Two request-time check helpers exist (shell_configured/exec_wrapper_configured, plustunnel_configured) for the wave-2 dispatch to call, reading theOption, never validating.cwd/env(OQ-09 posture) carried onServeConfigasOption<PathBuf>/BTreeMap<String, String>(empty defaults; never wire-derived; doc contract).channel_clientis not feature-gated: alkcall is a base dependency and a field presence is not a capability; thetunnelfeature gate applies at wave 2 on top (absent → decline even under the feature; unfeatured → decline too). Doc contract records both gates.- Verified the alkcall 0.10 surfaces in-source (registry):
core::auth::IdentityProvideris a sync,Send + Sync + 'statictrait (dyn-compatible with no async-trait help on alkssh's side;ChannelClientand theNoopIdentityProvidertest fixture ridealkcall::channels::client/alkcall::core::auth), and the russh 0.65.0 knob names (window_size,maximum_packet_size,channel_buffer_size,inactivity_timeout,keepalive_interval,keepalive_max,max_auth_attempts) match this module's fields one-for-one.
Summary
Config surface landed:
src/config.rswithLimits(in-crate caps:max_concurrent_channels64,max_exec_command_len8192; russh knobs:max_auth_attempts10, window 2 MiB, packet 32 KiB, buffer 100, inactivity 600 s, keepalive Some(15 s)/3, with every number a named posture constant or documented ADR anchor),ServeConfig(OQ-08shell_argv/exec_wrapper_argvas unset-declinesOption<Vec<String>>, OQ-09session_cwd/session_env, injectedIdentityProvider+HostKeySource+ optionalChannelClient,limits), andDialConfig(address, injectedKeySource+HostKeyPolicy,limitswith the keepalive/inactivity/window posture riding inside). No construction-time validation, no crate defaults for the unset rows, noDefaultfor the configs (handles only). Tests: defaults carry the documented postures;Optionfields flip independently; handles hold byArc::ptr_eq; the dial's key source and policy stay fail-closed through the config. Verified:cargo test(36),cargo test --all-features,cargo clippy --all-targets -- -D warnings,cargo clippy --all-features --all-targets -- -D warnings,cargo fmt --checkall pass;taskgraph validateclean.