Refactor: Use ToOwned::to_owned; add accessible mode and status#265
Conversation
#263) * Update rstest-bdd documentation * docs(testing): add developer guide and migration plan for rstest-bdd v0.5.0 - Introduce `docs/developers-guide.md` detailing testing workflow, behavioural test strategy, and authoring policies. - Add `docs/execplans/rstest-bdd-v0-5-0-behavioural-suite-migration.md` as a living ExecPlan for the migration to rstest-bdd v0.5.0. - Include staged plan with constraints, risks, decisions, progress, and validation steps. - Provide comprehensive context to ensure migration completeness and maintain test coverage. This documentation will serve as the source of truth for contributors updating or maintaining the behavioural test suite during and after the migration. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * feat(bdd-tests): upgrade rstest-bdd to v0.5.0 and adopt new usage patterns - Upgraded rstest-bdd and rstest-bdd-macros dependencies to v0.5.0 in Cargo.toml and Cargo.lock. - Refactored BDD step functions to use typed wrappers with FromStr implementations, enabling direct domain type usage. - Adopted inferred step patterns for simple no-argument steps to reduce boilerplate. - Updated behavioural test documentation to reflect new usage guidelines. - Completed migration plan stages from dependency bump through refactors to quality gates. - Improved test clarity and maintenance by removing repetitive string conversions and explicit step patterns. This improves test suite expressiveness and leverages new features introduced in rstest-bdd v0.5.0. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * docs(documentation): fix documentation grammar, links, and examples after PR review - Corrected heading grammar in developer's guide. - Replaced stale or broken migration and ADR links with valid, existing documentation targets. - Updated scenario fixture examples to use idiomatic `_scenario_db: ScenarioDb` without placeholders. - Expanded acronyms on first use and aligned citations. - Verified that linting and tests pass after documentation fixes. These changes address feedback and comments from the PR review to ensure documentation accuracy and consistency. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * docs(developers-guide): fix punctuation for clarity in documentation guidance Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * docs(documentation): improve formatting and clarity in multiple docs - Add code block syntax to shell commands in developers-guide.md - Fix table alignments and formatting in execplans and translators-guide.md - Adjust line breaks and wording for better readability across guides - No functional changes, documentation polish only Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> --------- Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Reviewer's GuideAdds an OutputMode abstraction with tri-state CLI/config integration to support an accessible, static-text output mode, wires a new StatusReporter into the runner pipeline for localized stage/completion reporting, updates localization and docs, and extends unit/BDD tests to cover detection and reporting without altering existing behaviours in standard mode. Sequence diagram for runner build with accessible output modesequenceDiagram
actor User
participant Shell
participant Cli
participant Runner as Runner_run
participant OutputMode
participant Reporter as AccessibleReporter
participant NinjaGen as generate_ninja
participant Process as run_ninja
User->>Shell: netsuke --accessible true build
Shell->>Cli: parse_args()
Cli-->>Shell: Cli{accessible = Some(true), command = Build}
Shell->>Runner: run(&Cli)
activate Runner
Runner->>OutputMode: resolve(explicit = Some(true))
OutputMode-->>Runner: OutputMode::Accessible
Runner->>Reporter: construct AccessibleReporter
Note right of Reporter: reporter : Box<dyn StatusReporter>
Runner->>Runner: select command
Runner->>Runner: handle_build(&Cli, &BuildArgs, &Reporter)
activate NinjaGen
Runner->>NinjaGen: generate_ninja(&Cli, &Reporter)
%% Stage 1: Network policy
NinjaGen->>Reporter: report_pipeline_stage(NetworkPolicy, None)
Reporter-->>Reporter: report_stage(1, 5, "Configuring network policy")
%% Stage 2: Manifest load
NinjaGen->>Reporter: report_pipeline_stage(ManifestLoad, None)
Reporter-->>Reporter: report_stage(2, 5, "Loading manifest")
%% Stage 3: Build graph
NinjaGen->>Reporter: report_pipeline_stage(BuildGraph, None)
Reporter-->>Reporter: report_stage(3, 5, "Building dependency graph")
%% Stage 4: Generate Ninja
NinjaGen->>Reporter: report_pipeline_stage(GenerateNinja, None)
Reporter-->>Reporter: report_stage(4, 5, "Generating Ninja file")
NinjaGen-->>Runner: NinjaContent
deactivate NinjaGen
%% Stage 5: Execute build
Runner->>Reporter: report_pipeline_stage(Execute, Some(STATUS_TOOL_BUILD))
Reporter-->>Reporter: report_stage(5, 5, "Executing Build")
Runner->>Process: run_ninja(program, &Cli, build_path, targets)
Process-->>Runner: Ok(())
Runner->>Reporter: report_complete(STATUS_TOOL_BUILD)
Runner-->>Shell: Ok(())
deactivate Runner
Shell-->>User: exit 0, status lines on stderr
Class diagram for OutputMode and StatusReporter integrationclassDiagram
direction LR
class OutputMode {
<<enum>>
+Accessible
+Standard
+is_accessible() bool
+resolve(explicit : Option_bool) OutputMode
+resolve_with(explicit : Option_bool, read_env : Fn_str_to_Option_String) OutputMode
}
class StatusReporter {
<<interface>>
+report_stage(current : u32, total : u32, description : &str) void
+report_complete(tool_key : &'static str) void
}
class AccessibleReporter {
+report_stage(current : u32, total : u32, description : &str) void
+report_complete(tool_key : &'static str) void
}
class SilentReporter {
+report_stage(current : u32, total : u32, description : &str) void
+report_complete(tool_key : &'static str) void
}
class PipelineStage {
<<enum>>
+NetworkPolicy
+ManifestLoad
+BuildGraph
+GenerateNinja
+Execute
+ALL : [PipelineStage;5]
+index() u32
+description(tool_key : Option_StStr) String
}
class StatusModule {
+PIPELINE_STAGE_COUNT : u32
+report_pipeline_stage(reporter : &dyn StatusReporter, stage : PipelineStage, tool_key : Option_StStr) void
}
class Cli {
+accessible : Option_bool
+default() Cli
}
class Runner {
+run(cli : &Cli) Result
+handle_build(cli : &Cli, args : &BuildArgs, reporter : &dyn StatusReporter) Result
+handle_clean(cli : &Cli, reporter : &dyn StatusReporter) Result
+handle_graph(cli : &Cli, reporter : &dyn StatusReporter) Result
+handle_ninja_tool(cli : &Cli, tool : &str, tool_key : &'static str, reporter : &dyn StatusReporter) Result
+generate_ninja(cli : &Cli, reporter : &dyn StatusReporter) Result_NinjaContent
}
class LocalizationKeys {
+STATUS_STAGE_LABEL
+STATUS_STAGE_MANIFEST_LOAD
+STATUS_STAGE_NETWORK_POLICY
+STATUS_STAGE_BUILD_GRAPH
+STATUS_STAGE_GENERATE_NINJA
+STATUS_STAGE_EXECUTE
+STATUS_COMPLETE
+STATUS_TOOL_BUILD
+STATUS_TOOL_CLEAN
+STATUS_TOOL_GRAPH
+CLI_FLAG_ACCESSIBLE_HELP
}
StatusReporter <|.. AccessibleReporter
StatusReporter <|.. SilentReporter
StatusModule ..> PipelineStage : uses
StatusModule ..> StatusReporter : uses
PipelineStage ..> LocalizationKeys : uses
AccessibleReporter ..> LocalizationKeys : uses
Runner ..> StatusReporter : depends_on
Runner ..> OutputMode : resolves
Runner ..> StatusModule : reports_stages
Cli ..> OutputMode : passes_explicit_flag
Cli ..> LocalizationKeys : flag_help_key
OutputMode ..> env : reads_environment
class env {
<<external>>
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
* Update rstest-bdd documentation * docs(testing): add developer guide and migration plan for rstest-bdd v0.5.0 - Introduce `docs/developers-guide.md` detailing testing workflow, behavioural test strategy, and authoring policies. - Add `docs/execplans/rstest-bdd-v0-5-0-behavioural-suite-migration.md` as a living ExecPlan for the migration to rstest-bdd v0.5.0. - Include staged plan with constraints, risks, decisions, progress, and validation steps. - Provide comprehensive context to ensure migration completeness and maintain test coverage. This documentation will serve as the source of truth for contributors updating or maintaining the behavioural test suite during and after the migration. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * feat(bdd-tests): upgrade rstest-bdd to v0.5.0 and adopt new usage patterns - Upgraded rstest-bdd and rstest-bdd-macros dependencies to v0.5.0 in Cargo.toml and Cargo.lock. - Refactored BDD step functions to use typed wrappers with FromStr implementations, enabling direct domain type usage. - Adopted inferred step patterns for simple no-argument steps to reduce boilerplate. - Updated behavioural test documentation to reflect new usage guidelines. - Completed migration plan stages from dependency bump through refactors to quality gates. - Improved test clarity and maintenance by removing repetitive string conversions and explicit step patterns. This improves test suite expressiveness and leverages new features introduced in rstest-bdd v0.5.0. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * docs(documentation): fix documentation grammar, links, and examples after PR review - Corrected heading grammar in developer's guide. - Replaced stale or broken migration and ADR links with valid, existing documentation targets. - Updated scenario fixture examples to use idiomatic `_scenario_db: ScenarioDb` without placeholders. - Expanded acronyms on first use and aligned citations. - Verified that linting and tests pass after documentation fixes. These changes address feedback and comments from the PR review to ensure documentation accuracy and consistency. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * docs(developers-guide): fix punctuation for clarity in documentation guidance Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * docs(documentation): improve formatting and clarity in multiple docs - Add code block syntax to shell commands in developers-guide.md - Fix table alignments and formatting in execplans and translators-guide.md - Adjust line breaks and wording for better readability across guides - No functional changes, documentation polish only Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * feat(cli): add accessible output mode for screen reader support Added a new accessible output mode that provides static, labelled status lines during the build pipeline, suitable for screen readers and dumb terminals. This mode is auto-enabled when TERM=dumb or NO_COLOR is set, or can be explicitly enabled or disabled via the new --accessible CLI flag, environment variable NETSUKE_ACCESSIBLE, or config file. The implementation introduces an OutputMode enum with detection logic and a StatusReporter trait with AccessibleReporter and SilentReporter implementations. Status reporting is integrated into the build pipeline, emitting stage progress and completion messages on stderr in accessible mode. Key changes include: - src/output_mode.rs: output mode detection and resolution - src/status.rs: status reporting trait and implementations - src/cli/mod.rs: new accessible field with config and CLI parsing updates - src/runner/mod.rs: wiring status reporting into pipeline stages - Localization updates for accessible output messages - BDD scenarios and step definitions for accessible mode detection and CLI parsing - Documentation updates in users-guide.md and roadmap.md marking 3.8.1 done This feature enhances accessibility for users requiring non-animated terminal output, improving usability and compliance. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * refactor(tests/bdd): refactor accessible mode verification steps to reuse logic Refactored accessible_mode_is_enabled and accessible_mode_is_disabled step functions in BDD tests to use a new helper function verify_accessible_mode that takes the expected accessible mode as argument. This reduces code duplication and improves readability. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * refactor(runner,status): centralize pipeline stage reporting and update runner code - Introduce PipelineStage enum to represent build pipeline stages with index and localized description. - Add report_pipeline_stage function to simplify reporting stages via StatusReporter. - Update src/runner/mod.rs to use new reporting functions instead of raw indices and descriptions. - Clean up status.rs to use PipelineStage enum and remove redundant functions. - Move tests from runner/mod.rs to separate runner/tests.rs file for better organization. - Minor docs and localization fixes related to accessible mode. This refactor improves maintainability by centralizing stage reporting logic and reducing duplicated code in the build pipeline. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * feat(runner): add accessible output mode with static progress reporting This change introduces an accessible output mode for the Netsuke build pipeline that emits static, screen-reader-friendly status lines during each pipeline stage. The accessible mode is auto-detected or can be enabled via a CLI flag, environment variables like TERM=dumb or NO_COLOR. Key changes: - Pipeline stage count adjusted from 6 to 5 with updated stage names and ordering. - Added StatusReporter trait methods to support tool-specific progress messages. - Status lines output via stderr using write lock to avoid interleaving issues. - Fluent messages extended with tool-aware keys for localized stage and completion messages. - Documentation updated with accessible output usage and behavior. - Tests added for detection logic and output correctness. This foundational work enables future enhancements such as animated spinners while maintaining accessibility. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * docs(execplans): clarify documentation on build stages and tool execution Refined descriptions of stages 5 and 6 in build handling to explicitly mention which functions these stages relate to (`handle_build` / `handle_ninja_tool`), and formatted the entries for improved clarity and readability in the accessible output mode documentation. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * feat(cli,output): add accessible output mode with CLI flag and detection Introduce an accessible output mode that replaces animated progress indicators with static, labelled status lines suitable for screen readers, dumb terminals, and CI environments. - Add OutputMode enum and resolve logic considering explicit flags, NO_COLOR, and TERM environment variables. - Integrate accessible flag in CLI config and argument parsing. - Implement status reporting trait, with AccessibleReporter and SilentReporter. - Add localization keys and translations for accessible output messages. - Include BDD tests verifying detection and CLI parsing behavior. - Update documentation and user guide with accessible output usage and behavior. This feature enhances usability and accessibility for diverse terminal environments and user needs. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> * refactor(output_mode,bdd_tests,status): unify output mode type usage and deduplicate pipeline stage count - Use OutputMode type consistently in BDD test world state for accessible output mode instead of String. - Update BDD steps to set and verify OutputMode directly, improving type safety. - Refactor pipeline stage count in status module to derive from an array of all stages, preventing desynchronization. - Minor doc updates and improve formatting commands in documentation QA steps. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com> --------- Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Hey - I've found 7 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `src/runner/mod.rs:107-111` </location>
<code_context>
match command {
- Commands::Build(args) => handle_build(cli, &args),
+ Commands::Build(args) => handle_build(cli, &args, reporter.as_ref()),
Commands::Manifest { file } => {
- let ninja = generate_ninja(cli)?;
+ let ninja = generate_ninja(cli, reporter.as_ref())?;
</code_context>
<issue_to_address>
**question:** Manifest-only runs never reach the Execute stage or a completion message, which may confuse accessible-mode users.
Currently `build`, `clean`, and `graph` emit all 5 stages (including `Execute`) plus `status.complete`, but `Manifest` only emits the first 4 and then exits. In accessible mode this shows `1/5`–`4/5` with no final `5/5` or completion message.
If this command is meant to be a partial pipeline, consider either using a smaller stage count for it or treating manifest generation as its own pipeline while still emitting a final completion message. Otherwise, accessible users may read the missing final stage/completion as a failed or aborted run.
</issue_to_address>
### Comment 2
<location> `src/runner/tests.rs:1-10` </location>
<code_context>
+//! Unit tests for the runner module's path resolution helpers.
+
+use super::*;
+use rstest::rstest;
+use std::path::PathBuf;
+
+#[rstest]
+#[case(None, "out.ninja", "out.ninja")]
+#[case(Some("work"), "out.ninja", "work/out.ninja")]
+#[case(Some("work"), "/tmp/out.ninja", "/tmp/out.ninja")]
+fn resolve_output_path_respects_directory(
+ #[case] directory: Option<&str>,
+ #[case] input: &str,
+ #[case] expected: &str,
+) {
+ let cli = Cli {
+ directory: directory.map(PathBuf::from),
+ ..Cli::default()
+ };
+ let resolved = resolve_output_path(&cli, Path::new(input));
+ assert_eq!(resolved.as_ref(), Path::new(expected));
+}
</code_context>
<issue_to_address>
**suggestion (testing):** Extend runner tests to cover status reporting integration and reporter selection
The refactored `runner` now resolves an `OutputMode`, constructs a `StatusReporter`, and calls `report_pipeline_stage`/`report_complete` around `generate_ninja`, `handle_build`, and the Ninja tools, but these tests only cover `resolve_output_path`.
Please also add tests that:
- Assert that `run` chooses `AccessibleReporter` vs `SilentReporter` based on `Cli.accessible`/environment (e.g., via a fake `Cli` and a test-only `OutputMode::resolve`, or a helper that injects a stub reporter).
- Use a test `StatusReporter` that records calls to verify: (a) `generate_ninja` emits the four non-execute stages in order, and (b) `handle_build`/`handle_clean`/`handle_graph` emit the `Execute` stage and a matching `report_complete`.
This will unit test the new status reporting wiring instead of relying only on higher-level tests.
Suggested implementation:
```rust
//! Unit tests for the runner module's helpers.
use super::*;
use rstest::rstest;
use std::path::{Path, PathBuf};
#[rstest]
#[case(None, "out.ninja", "out.ninja")]
#[case(Some("work"), "out.ninja", "work/out.ninja")]
#[case(Some("work"), "/tmp/out.ninja", "/tmp/out.ninja")]
fn resolve_output_path_respects_directory(
#[case] directory: Option<&str>,
#[case] input: &str,
#[case] expected: &str,
) {
let cli = Cli {
directory: directory.map(PathBuf::from),
..Cli::default()
};
let resolved = resolve_output_path(&cli, Path::new(input));
assert_eq!(resolved.as_ref(), Path::new(expected));
}
/// Test reporter used to capture status events emitted by the runner in unit tests.
///
/// This is intentionally minimal – it just records the stages reported so that tests can
/// assert on their order and on whether `report_complete` is called for each execute stage.
#[derive(Default, Debug)]
struct TestReporter {
pipeline_stages: Vec<PipelineStage>,
completed_stages: Vec<PipelineStage>,
}
impl StatusReporter for TestReporter {
fn report_pipeline_stage(&mut self, stage: PipelineStage) {
self.pipeline_stages.push(stage);
}
fn report_complete(&mut self, stage: PipelineStage) {
self.completed_stages.push(stage);
}
}
#[rstest]
fn run_chooses_accessible_reporter_when_accessible_flag_set() {
// Given a CLI that explicitly requests accessible output
let cli = Cli {
accessible: Some(true),
..Cli::default()
};
// When we resolve the output mode and construct a status reporter
let output_mode = OutputMode::resolve(&cli);
let reporter = build_status_reporter(&cli, &output_mode);
// Then we should be using the accessible reporter
// (we deliberately don't care about the exact concrete type as long
// as it is the one used for accessible output).
assert!(reporter.is_accessible());
}
#[rstest]
fn run_chooses_silent_reporter_when_accessible_not_set_and_silent_mode() {
// Given a CLI that does not request accessible output and a silent output mode
let cli = Cli {
accessible: None,
..Cli::default()
};
let output_mode = OutputMode::Silent;
let reporter = build_status_reporter(&cli, &output_mode);
// Then we should be using the silent reporter to avoid spurious status output
assert!(reporter.is_silent());
}
#[rstest]
fn generate_ninja_reports_all_non_execute_stages_in_order() {
let cli = Cli::default();
let mut reporter = TestReporter::default();
generate_ninja_with_status(&cli, &mut reporter).expect("generate_ninja_with_status failed");
// We expect all non-execute stages to be reported in order.
// Adjust the expected sequence to match the actual PipelineStage variants.
use PipelineStage::*;
let expected = vec![Parse, Analyze, Plan, Generate];
assert_eq!(reporter.pipeline_stages, expected);
// generate_ninja should not emit Execute or completion events by itself.
assert!(
!reporter.pipeline_stages.contains(&Execute),
"generate_ninja should not report Execute stage"
);
assert!(
reporter.completed_stages.is_empty(),
"generate_ninja should not report completion"
);
}
#[rstest]
fn handle_build_reports_execute_and_completion() {
let cli = Cli::default();
let mut reporter = TestReporter::default();
handle_build_with_status(&cli, &mut reporter).expect("handle_build_with_status failed");
use PipelineStage::Execute;
// Execute stage should be reported exactly once
assert_eq!(
reporter
.pipeline_stages
.iter()
.filter(|&&s| s == Execute)
.count(),
1
);
// And there should be a matching completion event
assert_eq!(
reporter
.completed_stages
.iter()
.filter(|&&s| s == Execute)
.count(),
1
);
}
#[rstest]
fn handle_clean_reports_execute_and_completion() {
let cli = Cli::default();
let mut reporter = TestReporter::default();
handle_clean_with_status(&cli, &mut reporter).expect("handle_clean_with_status failed");
use PipelineStage::Execute;
assert_eq!(
reporter
.pipeline_stages
.iter()
.filter(|&&s| s == Execute)
.count(),
1
);
assert_eq!(
reporter
.completed_stages
.iter()
.filter(|&&s| s == Execute)
.count(),
1
);
}
#[rstest]
fn handle_graph_reports_execute_and_completion() {
let cli = Cli::default();
let mut reporter = TestReporter::default();
handle_graph_with_status(&cli, &mut reporter).expect("handle_graph_with_status failed");
use PipelineStage::Execute;
assert_eq!(
reporter
.pipeline_stages
.iter()
.filter(|&&s| s == Execute)
.count(),
1
);
assert_eq!(
reporter
.completed_stages
.iter()
.filter(|&&s| s == Execute)
.count(),
1
);
}
```
To make these tests compile and accurately exercise the runner wiring, you will need to:
1. **StatusReporter trait**
- Ensure the `StatusReporter` trait is visible to `tests.rs` (likely via `pub(crate)` in `runner` or a re-export).
- Align the trait methods with the test `impl` above, or adjust the test `TestReporter` to match your actual trait:
- The tests assume:
```rust
pub trait StatusReporter {
fn report_pipeline_stage(&mut self, stage: PipelineStage);
fn report_complete(&mut self, stage: PipelineStage);
}
```
- If your signatures include results, timings, or other metadata, extend `TestReporter` accordingly and update the tests to ignore or assert the additional fields as appropriate.
2. **PipelineStage enum**
- Export `PipelineStage` to `tests.rs` and update the expected sequence in `generate_ninja_reports_all_non_execute_stages_in_order` to match your real non-execute stages (the placeholder is `[Parse, Analyze, Plan, Generate]`).
- Ensure there is an `Execute` variant (or change the tests to use whichever variant you use to represent the execution stage).
3. **OutputMode / reporter selection**
- Expose a helper used by `run` for reporter selection:
```rust
pub(crate) fn build_status_reporter(cli: &Cli, mode: &OutputMode) -> ReporterHandle { ... }
```
where `ReporterHandle` is whatever type you already use (e.g. `Box<dyn StatusReporter>` or a wrapper around it).
- Implement methods used in the tests:
```rust
impl ReporterHandle {
pub fn is_accessible(&self) -> bool { /* true for AccessibleReporter */ }
pub fn is_silent(&self) -> bool { /* true for SilentReporter */ }
}
```
or adjust the tests to assert on concrete types or discriminants you expose instead of `is_accessible`/`is_silent`.
- Ensure `OutputMode::resolve(&Cli)` exists and mirrors the logic used in `run` to resolve the output mode from `Cli` and environment (e.g., checking `Cli.accessible` and any relevant env vars).
4. **Status-aware wrappers around pipeline stages**
- Add small, test-visible helpers that wrap the internal functions and perform the status reporting that `run` does, so tests can call them without going through the full CLI plumbing:
```rust
pub(crate) fn generate_ninja_with_status(
cli: &Cli,
reporter: &mut dyn StatusReporter,
) -> Result<(), RunnerError> {
// call report_pipeline_stage for each non-execute stage,
// then delegate to the existing generate_ninja implementation
}
pub(crate) fn handle_build_with_status(
cli: &Cli,
reporter: &mut dyn StatusReporter,
) -> Result<(), RunnerError> {
// report Execute stage, call handle_build, then report_complete
}
pub(crate) fn handle_clean_with_status( ... ) -> Result<..., ...> { /* same pattern */ }
pub(crate) fn handle_graph_with_status( ... ) -> Result<..., ...> { /* same pattern */ }
```
- These helpers should match the internal wiring already used in `run`, so the tests directly verify that `generate_ninja`, `handle_build`, `handle_clean`, and `handle_graph` are wrapped with `report_pipeline_stage`/`report_complete` in the correct order.
5. **run integration (optional additional test)**
- If you want tests to go through `run` directly, consider adding a `cfg(test)`-only hook that allows injecting a `TestReporter` into `run` (e.g., a test-only `fn run_with_reporter(cli: &Cli, reporter: &mut dyn StatusReporter)` that mirrors the standard `run` but replaces the reporter construction).
- You can then extend the tests with an additional case that invokes `run_with_reporter` and asserts that the same sequences of stages and completions are recorded as in the helper-based tests.
Adjust the test code types (`RunnerError`, function names, and enum variants) to match your existing `runner` implementation so these tests compile and accurately verify the new status reporting integration.
</issue_to_address>
### Comment 3
<location> `docs/execplans/3-8-1-add-accessible-output-mode.md:103` </location>
<code_context>
+- [x] Write ExecPlan to `docs/execplans/3-8-1-add-accessible-output-mode.md`.
+- [x] Stage A: Define `OutputMode` enum and detection logic in new module
+ `src/output_mode.rs`.
+- [x] Stage A: Add unit tests for `OutputMode` detection (rstest parameterized).
+- [x] Stage B: Add `accessible` field to `Cli` struct with OrthoConfig wiring.
+- [x] Stage B: Update `cli_overrides_from_matches` to include `accessible`.
</code_context>
<issue_to_address>
**suggestion (typo):** Align "parameterized" spelling with the stated en-GB-oxendict convention (e.g. "parametrised").
This checklist item (and the later reference to "rstest parameterized cases") use "parameterized", which conflicts with the en-GB-oxendict convention you mention in the Constraints section. Please change these to "parametrised" for consistency.
```suggestion
- [x] Stage A: Add unit tests for `OutputMode` detection (rstest parametrised).
```
</issue_to_address>
### Comment 4
<location> `docs/execplans/3-8-1-add-accessible-output-mode.md:127-131` </location>
<code_context>
+- BDD step "the environment variable X is set to Y" already exists in
</code_context>
<issue_to_address>
**suggestion (testing):** Clarify whether the BDD scenarios use real or "simulated" environment variables, as the current text is slightly inconsistent.
The “Surprises & discoveries” section says the existing `the environment variable X is set to Y` step mutates the real process environment, while accessible-output scenarios use simulated TERM/NO_COLOR values via `TestWorld`. But the later example feature (and `accessible_output.feature`) still show steps like `Given the environment variable "TERM" is set ...`. Please align the description and examples—either note explicitly that these steps use the simulated environment, or adjust the example wording—so it’s clear whether tests touch the real environment or not.
```suggestion
- BDD step `"the environment variable X is set to Y"` already exists in
`manifest/mod.rs` and mutates the real process environment.
- To avoid test interference, the accessible-output BDD scenarios do *not*
use that step. Instead, their steps such as
`Given the environment variable "TERM" is set to "xterm-256color"` are
bound to step definitions that store TERM/NO_COLOR values in `TestWorld`
and pass them into `resolve_with` via a closure, so the real process
environment is never changed.
```
</issue_to_address>
### Comment 5
<location> `src/status.rs:71` </location>
<code_context>
+/// hard-coded literals at call sites and ensures [`PIPELINE_STAGE_COUNT`]
+/// stays consistent with the stages that are reported.
+#[derive(Copy, Clone, Debug)]
+pub enum PipelineStage {
+ /// Stage 1: configuring the network policy.
+ NetworkPolicy = 1,
</code_context>
<issue_to_address>
**issue (complexity):** Consider simplifying the pipeline stage API by deriving the stage count from the last enum variant and splitting execute-specific descriptions into a dedicated function to avoid optional tool keys at most call sites.
You can drop a couple of abstractions without losing any functionality:
### 1. Simplify stage counting (drop `ALL` + clippy suppression)
`PipelineStage::ALL` is only used to get a constant length, and you already have a stable, 1-based enum. You can tie the count directly to the last variant and remove the indirection and `#[expect]`.
```rust
#[derive(Copy, Clone, Debug)]
pub enum PipelineStage {
NetworkPolicy = 1,
ManifestLoad = 2,
BuildGraph = 3,
GenerateNinja = 4,
Execute = 5,
}
impl PipelineStage {
#[must_use]
pub const fn index(self) -> u32 {
self as u32
}
}
// Total stages, tied to the last enum variant.
pub const PIPELINE_STAGE_COUNT: u32 = PipelineStage::Execute.index();
```
This keeps the “single source of truth” property without needing `ALL` or the clippy exemption.
### 2. Narrow the `description` API to avoid `Option` at call sites
Right now callers always have to think about `tool_key`, even though only `Execute` uses it. You can split the API so most call sites don’t see `Option` at all, and only the execute stage path deals with tool keys:
```rust
impl PipelineStage {
#[must_use]
pub fn description(self) -> String {
match self {
Self::NetworkPolicy => {
localization::message(keys::STATUS_STAGE_NETWORK_POLICY).to_string()
}
Self::ManifestLoad => {
localization::message(keys::STATUS_STAGE_MANIFEST_LOAD).to_string()
}
Self::BuildGraph => {
localization::message(keys::STATUS_STAGE_BUILD_GRAPH).to_string()
}
Self::GenerateNinja => {
localization::message(keys::STATUS_STAGE_GENERATE_NINJA).to_string()
}
Self::Execute => {
// Generic execute message without tool name.
localization::message(keys::STATUS_STAGE_EXECUTE).to_string()
}
}
}
#[must_use]
pub fn execute_description(tool_key: &'static str) -> String {
let tool = localization::message(tool_key).to_string();
localization::message(keys::STATUS_STAGE_EXECUTE)
.with_arg("tool", tool)
.to_string()
}
}
```
And then keep `report_pipeline_stage` simple and explicit for the execute case:
```rust
pub fn report_pipeline_stage(
reporter: &dyn StatusReporter,
stage: PipelineStage,
) {
reporter.report_stage(stage.index(), PIPELINE_STAGE_COUNT, &stage.description());
}
pub fn report_execute_stage(
reporter: &dyn StatusReporter,
tool_key: &'static str,
) {
reporter.report_stage(
PipelineStage::Execute.index(),
PIPELINE_STAGE_COUNT,
&PipelineStage::Execute.execute_description(tool_key),
);
}
```
Most call sites now just call `report_pipeline_stage(reporter, stage)` with no `Option`, and the single “execute with tool” path uses the dedicated helper, making the intent clearer with less cognitive overhead.
</issue_to_address>
### Comment 6
<location> `docs/execplans/3-8-1-add-accessible-output-mode.md:3` </location>
<code_context>
+# Add accessible output mode (roadmap 3.8.1)
+
+This ExecPlan is a living document. The sections `Constraints`, `Tolerances`,
+`Risks`, `Progress`, `Surprises & Discoveries`, `Decision Log`, and
+`Outcomes & Retrospective` must be kept up to date as work proceeds.
</code_context>
<issue_to_address>
**issue (review_instructions):** "ExecPlan" is used as an acronym without being defined on first use.
To align with the style guidance on acronyms, please expand and define this term on first use, for example: "This execution plan (ExecPlan) is a living document ..." and then use "ExecPlan" thereafter.
<details>
<summary>Review instructions:</summary>
**Path patterns:** `**/*.md`
**Instructions:**
Define uncommon acronyms on first use.
</details>
</issue_to_address>
### Comment 7
<location> `docs/execplans/3-8-1-add-accessible-output-mode.md:212` </location>
<code_context>
+ `define_keys!` macro.
+- `locales/en-US/messages.ftl` — English Fluent translations.
+- `locales/es-ES/messages.ftl` — Spanish Fluent translations.
+- `tests/bdd_tests.rs` — BDD entry point;
+ `scenarios!("tests/features", fixtures = [world: TestWorld])`.
+- `tests/bdd/fixtures/mod.rs` — `TestWorld` struct for BDD state.
</code_context>
<issue_to_address>
**issue (review_instructions):** "BDD" is introduced without expanding the acronym on first use.
Please expand BDD the first time it appears in the document, for example: "Behaviour-Driven Development (BDD) entry point". Subsequent references can use just "BDD".
<details>
<summary>Review instructions:</summary>
**Path patterns:** `**/*.md`
**Instructions:**
Define uncommon acronyms on first use.
</details>
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
e27bb17 to
6942e3d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e27bb17432
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@coderabbitai is this a bug that should be raised as an issue?
All test environment variable mutation should be guarded by a mutex lock (e.g., using the env-lock crate) or use sandbox encapsulation. I would consider the former the preferred option as the latter feels like a sledgehammer to crack a walnut. |
|
@CodeRabbit of course, DI with mockable is preferred to either approach :) |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 850 🏁 Script executed: Length of output: 648 🏁 Script executed: Length of output: 504 🏁 Script executed: Length of output: 191 🏁 Script executed: Length of output: 9010 🏁 Script executed: Length of output: 859
The The pub fn set_var(key: &str, value: &OsStr) -> Option<OsString> {
let _lock = EnvLock::acquire();
let previous = std::env::var_os(key);
// SAFETY: `EnvLock` serialises mutations.
unsafe { std::env::set_var(key, value) };
previous
}
The new accessible output mode tests in this PR use a different approach (simulated environment via closures) to avoid even the mutex overhead, but the existing manifest tests are already properly protected. No issue needs to be raised. |
|
Tip For best results, initiate chat on the files or code changes.
The new pub fn resolve<F>(config: Option<bool>, env_lookup: F) -> Self
where
F: Fn(&str) -> Option<String>,This allows tests to inject mock environment state without touching the real process environment—cleaner, faster, and no mutex overhead. The contrast is clear:
If you'd like, the existing manifest tests could be refactored to use the same DI pattern, eliminating the need for 🐰✨ |
- Simplify PipelineStage API: drop ALL array and clippy suppression, derive PIPELINE_STAGE_COUNT from Execute variant index, and split description() into a no-arg form for non-Execute stages and a dedicated execute_description(tool_key) for the Execute stage so callers no longer need to pass Option. - Fix incomplete manifest progress: the manifest command only runs stages 1-4 of 5 and never reaches Execute or completion, which looks like a stalled run in accessible mode. Pass SilentReporter for the manifest path to suppress misleading partial progress. - Add runner unit tests for status reporting integration: a RecordingReporter captures events to verify reporter selection (accessible vs standard), correct stage index/total emission for report_pipeline_stage and report_execute_stage, and report_complete tool key recording. - Expand "ExecPlan" and "BDD" acronyms on first use in execplan doc. - Clarify that accessible-output BDD scenarios store TERM/NO_COLOR in TestWorld rather than mutating the real process environment. - Re-wrap markdown via make fmt (users-guide, translator-tooling doc). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@coderabbitai have the following now been resolved? |
This comment was marked as resolved.
This comment was marked as resolved.
…ambiguity Replaced all instances of `ToString::to_string` with `std::string::ToString::to_string` in src/cli_l10n.rs to clarify the method calls and potentially avoid naming conflicts or ambiguity in trait resolution. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Replace previous usage of to_string conversion with a helper function styled_to_plain_text to convert clap's StyledStr instances to plain strings before passing them to the localization logic. This improves clarity and correctness by explicitly handling StyledStr types when localizing command descriptions, helps, and about text. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Use the ToOwned::to_owned method instead of str::to_string when mapping StringOrList inputs for better clarity. Also, update get_target_display_name to use path.as_str().to_owned() instead of ToString::to_string for consistent string conversion. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
- Added `format_error_message` helper for consistent error formatting. - Replaced redundant `map(ToString::to_string)` calls with `format_error_message`. - Simplified string mapping in tests by using `copied()` and `to_owned()` for clarity. - Imported `std::error::Error` as `StdError` for better error trait handling. These changes improve error message handling and code clarity in manifest-related tests. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
…tently Replaced multiple direct `.map(ToString::to_string)` calls in test modules with dedicated helper functions (e.g., `format_error_message` and `localised_message_to_string`) to improve code readability and maintainability in error message formatting throughout various test files. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Summary
Changes
Why this matters
Testing plan
Documentation
Notes for reviewers
Artifacts touched
How to test
📎 Task: https://www.devboxer.com/task/7b5005f7-eaac-40bc-a2d6-a010b3e9bcf3