Extension discovery, dispatch, and guardrails for the qli CLI.
[discovery] walks XDG + PATH and builds the group/extension table.
[dispatch::run] wraps the child spawn in the guard sequence: banner →
[guard::check_requires_env] → [guard::run_confirm] → secrets →
[audit] start → spawn → audit finish/interrupted. Manifests
([manifest]) describe each group; secret resolution is pluggable via
[secrets::SecretsResolver].
Diagnostic policy (fail fast, fail loud)
Every error has one obvious surface — never silently swallowed. Four tiers, picked by user impact, not by code locality:
- Process-fatal — bubbled up as
anyhow::Errorfrommain, printederror: {msg}(exit 1). For startup failures and unrecoverable binary conditions. - Dispatch-fatal — typed [
dispatch::DispatchError] variants that abort one dispatched extension with full context. Surfaced throughanyhowso the user seeserror: failed to run X: Y. - Must-see warning —
eprintln!("warning: ..."). Never goes throughtracing(which-qcan silence). Used when behavior visibly degrades: discovery skipped a group, a signal handler couldn't install, an audit-finish write failed. - Trace —
tracinginfo/debug/trace. Routine progress only; silenceable. Use it when a later operation will fail loudly with full context if this trace event mattered.
Rule of thumb: if you write .ok() on a Result whose failure changes
user-visible behavior, you've picked the wrong tier — promote to 3 or 2.
Validation belongs at the earliest boundary (parse-time over
exec-time) so the error points at the source, not the symptom.