# iter cli-diag-human — non-JSON CLI surfaces `WorkspaceLoadError` with bracketed `[code]` **Date:** 2026-05-14 **Started from:** 6755060 (post-rpe.1.tidy) **Status:** DONE ## Summary Closes the P2 todo "CLI human-mode diagnostic surface for `WorkspaceLoadError`" surfaced by the ct.1.8 tester (2026-05-11). Pre-iter, the non-JSON path of every CLI subcommand that called `ailang_surface::load_workspace(&path)?` propagated the error via anyhow's Display — producing `Error: ` on stderr — while the JSON path of `ail check --json` correctly routed through `workspace_error_to_diagnostic` and emitted the bracketed `[code]` prefix. The asymmetry made the human-mode output harder to triage and less consistent across the toolchain. Fix: new private helper `load_workspace_human(path) -> Result` in `crates/ail/src/main.rs` that wraps `ailang_surface::load_workspace`, threads any `WorkspaceLoadError` through the existing `workspace_error_to_diagnostic`, formats the resulting `Diagnostic` into a single stderr line of shape `error: [] : `, and exits non-zero. Pure I/O errors (where `workspace_error_to_diagnostic` returns `None` per the loader's own contract) still propagate as anyhow errors so the caller's `?`-flow stays well-behaved. Nine call sites in `main.rs` swapped from `ailang_surface::load_workspace()?` to `load_workspace_human()?`: the `ail check` (non-JSON arm), `build`, `run`, `emit-ir`, `prose`, `describe`, `deps`, `diff`, `manifest` paths. The JSON arm of `ail check` (line 596) kept its explicit `match` shape — it builds a structured diagnostics array on stdout, which is a different output contract. The `BareCrossModuleTypeRef` arm of `workspace_error_to_diagnostic` gained the existing migration-command hint ("Run `ail migrate-canonical-types` to fix legacy fixtures.") so that the ct.1.8 actionable-hint test (`check_human_mode_emits_actionable_message_to_stderr`) stays green. The hint was previously carried by `WorkspaceLoadError`'s thiserror Display impl; making `workspace_error_to_diagnostic` the single source for both JSON and human paths required moving it into the Diagnostic message body. ## Files touched - `crates/ail/src/main.rs` — new `load_workspace_human` helper (~25 lines, including doc comment); 9 call-site swaps; one Diagnostic message gained the migration-hint suffix. - `crates/ail/tests/cli_diag_human_workspace_load_error.rs` — new RED-pin test (tempdir-based fixture asserting `[module-not-found]` in stderr on a missing-import workspace). - `docs/roadmap.md` — entry struck through. ## Verification - RED test `check_non_json_emits_bracketed_module_not_found` flipped from RED to GREEN. - `cargo test --workspace` → 565 / 0 / 3 (was 564 pre-iter; +1 from this iter's RED pin). - `cargo clippy --workspace --all-targets` → 0 warnings. - `cargo doc --workspace --no-deps` → 0 warnings. ## Concerns - (none) ## Known debt - (none)