Bring three command view modules (run, list, show) into alignment with the
canonical folder structure that all other command view modules already follow. The
inconsistencies accumulate across three distinct problems in those commands.
Expected canonical structure (followed by 10 of 13 commands):
<command>/
mod.rs
view_data/<dto>.rs ← presentation-layer DTO struct
views/
json_view.rs ← JsonView struct + render method
text_view.rs ← TextView struct + render method
Deviating commands:
| Command | Problem |
|---|---|
run |
No views/ subfolder — json_view.rs and text_view.rs sit directly in run/ |
run |
No view_data/ — DTO struct is defined inline inside json_view.rs |
list |
No view_data/ — uses application-layer DTO (EnvironmentList) directly |
show |
No view_data/ — uses application-layer DTO (EnvironmentInfo) directly |
Target Files (after refactor):
src/presentation/cli/views/commands/run/views/json_view.rs(moved fromrun/json_view.rs)src/presentation/cli/views/commands/run/views/text_view.rs(moved fromrun/text_view.rs)src/presentation/cli/views/commands/run/view_data/run_details.rs(new — extracted DTO)src/presentation/cli/views/commands/list/view_data/list_details.rs(new)src/presentation/cli/views/commands/show/view_data/show_details.rs(new)
Scope:
- Move and reorganize files only — no logic changes
- Extract the inline DTO from
run/json_view.rstorun/view_data/run_details.rs - Introduce presentation-layer view DTOs for
listandshowto replace direct use of application-layer types in the view layer - Update
mod.rsfiles and all import paths - Out of scope: changing the
Render<T>trait,ViewRenderError, or any serialization logic
This plan must be completed before implementing Proposal #2
of standardize-json-view-render-api.
That plan's Proposal #2 (standardize return types) modifies every json_view.rs file.
If the file moves in this plan happen after Proposal #2 is merged, the diff will be
harder to review (logic changes + file moves mixed). The correct order is:
- This plan (folder structure only — pure moves and one DTO extraction)
standardize-json-view-render-apiProposal #1 (addViewRenderError+Render<T>trait)standardize-json-view-render-apiProposal #2 (convert all return types)
Total Active Proposals: 3 Total Postponed: 0 Total Discarded: 0 Completed: 3 In Progress: 0 Not Started: 0
- Phase 0 - Fix
runmodule layout (High Impact, Low Effort): ✅ 2/2 completed (100%) - Phase 1 - Fix
listandshowmodule layout (Medium Impact, Low Effort): ✅ 1/1 completed (100%)
None
None
The run command module places json_view.rs and text_view.rs directly inside run/,
with no views/ subfolder:
run/
mod.rs
json_view.rs ← should be views/json_view.rs
text_view.rs ← should be views/text_view.rs
Every other command uses a views/ subfolder to hold its view implementations. The flat
layout makes run visually inconsistent and breaks the pattern that allows contributors
to navigate any command directory predictably.
Inside run/json_view.rs, a presentation-layer DTO struct is defined inline:
/// DTO for JSON output of run command
#[derive(Debug, Serialize)]
pub struct RunData { ... }In all other commands this type lives in a dedicated view_data/<dto>.rs file. Embedding
the DTO inside the view file conflates two responsibilities: data shaping and rendering.
It also makes the DTO harder to discover and test independently.
list/views/json_view.rs imports EnvironmentList from
application::command_handlers::list::info. show/views/json_view.rs imports
EnvironmentInfo from application::command_handlers::show::info.
The standard pattern is for the view layer to own its input DTOs in view_data/, so that:
- The presentation layer is not directly coupled to application-layer module paths
- The
view_data/location serves as the explicit contract between command handler and view - Contributors can navigate to
view_data/to understand what a view expects, without reading the application layer
For list and show, this means creating thin wrapper or re-export modules in
view_data/ that bring the relevant types into the presentation layer's own namespace.
Status: ✅ Completed
Impact: 🟢🟢🟢 High
Effort: 🔵 Low
Priority: P0
Depends On: None
Completed: 2026-02-28
Commit: 8e3f8a41
run/json_view.rs and run/text_view.rs are not in a views/ subfolder. The run/mod.rs
declares them as direct children:
mod json_view;
mod text_view;Contributors looking for the run command's view implementations follow the pattern from
other commands (views/json_view.rs) and do not find a views/ directory.
Move files:
run/json_view.rs → run/views/json_view.rs
run/text_view.rs → run/views/text_view.rs
Create run/views/mod.rs:
mod json_view;
mod text_view;
pub use json_view::JsonView;
pub use text_view::TextView;Update run/mod.rs:
pub mod view_data;
mod views;
pub use views::JsonView;
pub use views::TextView;All public exports from run/mod.rs remain the same — callers are unaffected.
- ✅
runmatches the layout of all other 12 command view modules - ✅ Contributors can navigate to
views/json_view.rspredictably
- Create directory
src/presentation/cli/views/commands/run/views/ - Move
run/json_view.rstorun/views/json_view.rs - Move
run/text_view.rstorun/views/text_view.rs - Create
run/views/mod.rsexportingJsonViewandTextView - Update
run/mod.rsto usemod viewsand re-export through it - Verify all tests pass
- Run
cargo run --bin linter alland fix issues
Pure file move — no logic changes. Compilation is the test. All existing unit tests and E2E tests must pass unchanged.
Status: ✅ Completed
Impact: 🟢🟢 Medium
Effort: 🔵 Low
Priority: P0
Depends On: Proposal #1
Completed: 2026-02-28
Commit: ff0c6d7b
The DTO struct for the run command's JSON output is defined inside json_view.rs:
// run/views/json_view.rs (after Proposal #1)
#[derive(Debug, Serialize)]
pub struct RunData {
pub environment_name: String,
pub state: String,
pub services: Vec<ServiceInfo>,
pub grafana: Option<GrafanaInfo>,
}This mixes data shaping (what the DTO looks like) with rendering (how it is serialized).
All other commands place their DTOs in a dedicated view_data/<dto>.rs file.
Create run/view_data/run_details.rs:
//! View data DTO for the run command output.
use serde::Serialize;
use crate::application::command_handlers::show::info::{GrafanaInfo, ServiceInfo};
/// DTO for JSON output of run command.
#[derive(Debug, Serialize)]
pub struct RunDetailsData {
pub environment_name: String,
pub state: String,
pub services: Vec<ServiceInfo>,
pub grafana: Option<GrafanaInfo>,
}Create run/view_data/mod.rs:
pub mod run_details;
pub use run_details::RunDetailsData;Update run/views/json_view.rs to import RunDetailsData from run::view_data and
remove the inline struct definition.
Rename the struct to RunDetailsData for consistency with the naming convention used
in other commands (e.g., ConfigureDetailsData, DestroyDetailsData).
- ✅ Consistent DTO placement across all 13 command view modules
- ✅ DTO is independently discoverable and testable
- ✅ Clear separation between data shape and rendering logic
- Create
src/presentation/cli/views/commands/run/view_data/run_details.rswithRunDetailsData - Create
src/presentation/cli/views/commands/run/view_data/mod.rs - Update
run/mod.rsto declarepub mod view_data - Update
run/views/json_view.rsto useRunDetailsDatafromsuper::super::view_data(or the crate-level path) and remove the inline struct - Update all call sites constructing the old inline struct to construct
RunDetailsData - Verify all tests pass
- Run
cargo run --bin linter alland fix issues
Changes struct name and file location only. Update any test that constructs or names the old struct. Observable JSON output is unaffected (field names do not change).
Status: ✅ Completed
Impact: 🟢🟢 Medium
Effort: 🔵 Low
Priority: P1
Depends On: None (can be parallel with Phase 0)
Completed: 2026-02-28
Commit: 3d63edc6
list and show use application-layer types directly in their view files:
// list/views/json_view.rs
use crate::application::command_handlers::list::info::EnvironmentList;
// show/views/json_view.rs
use crate::application::command_handlers::show::info::EnvironmentInfo;Both are missing a view_data/ subfolder. The standard pattern puts the DTO (or its
re-export) in view_data/ so that the view files only need to look one level up for
their input type, and the application-layer path is not scattered across view files.
Create list/view_data/list_details.rs:
//! View data for the list command.
//!
//! Re-exports the application-layer DTO as the canonical view input type.
//! The presentation layer references this module rather than importing directly
//! from the application layer.
pub use crate::application::command_handlers::list::info::EnvironmentList;Create show/view_data/show_details.rs:
//! View data for the show command.
pub use crate::application::command_handlers::show::info::EnvironmentInfo;Add view_data/mod.rs for each:
// list/view_data/mod.rs
pub mod list_details;
pub use list_details::EnvironmentList;
// show/view_data/mod.rs
pub mod show_details;
pub use show_details::EnvironmentInfo;Update list/mod.rs and show/mod.rs to declare pub mod view_data.
Update imports in list/views/json_view.rs, list/views/text_view.rs,
show/views/json_view.rs, and show/views/text_view.rs to use the local view_data
re-exports instead of the application-layer paths.
Using re-exports rather than new wrapper structs avoids duplicating type definitions while
still establishing view_data/ as the explicit boundary. If the application-layer DTO
ever changes its module path, only the view_data/ file needs updating — not every view
file in the command.
- ✅ All 13 command modules have
view_data/ - ✅ View files do not import directly from the application layer
- ✅ Single place to update if application DTO paths change
- ✅ Consistent navigation: every command has the same discoverable layout
- Create
src/presentation/cli/views/commands/list/view_data/list_details.rs - Create
src/presentation/cli/views/commands/list/view_data/mod.rs - Update
list/mod.rsto declarepub mod view_data - Update imports in
list/views/json_view.rsandlist/views/text_view.rs - Create
src/presentation/cli/views/commands/show/view_data/show_details.rs - Create
src/presentation/cli/views/commands/show/view_data/mod.rs - Update
show/mod.rsto declarepub mod view_data - Update imports in
show/views/json_view.rs,show/views/text_view.rs, and all other files undershow/views/that import application-layer types - Verify all tests pass
- Run
cargo run --bin linter alland fix issues
Pure import path changes. Compilation is the primary test. No behavior changes.
- Start Date: 2026-02-27
- Actual Completion: TBD
- Technical feasibility validated
- Aligns with Development Principles
- Implementation plan is clear and actionable
- Priorities are correct (high-impact/low-effort first)
- All active proposals implemented
- All tests passing
- All linters passing
- Documentation updated
- Changes merged to main branch
- Development Principles
- Contributing Guidelines
- Module Organization
- Standardize JsonView Render API — must be done after this plan
The run command was likely added in a hurry and missed the views/ subfolder because
no other existing command at that time had the wrong structure to copy from. The list
and show commands may have intentionally imported application DTOs directly, since those
types are already well-shaped for display — but the consistency cost of skipping
view_data/ outweighs the slight brevity of a direct import.
The show command's views/ subfolder already contains multiple sub-views
(basic.rs, grafana.rs, https_hint.rs, infrastructure.rs, next_step.rs,
prometheus.rs, tracker_services.rs, mod.rs) in addition to json_view.rs and
text_view.rs. This is intentional — show renders richer data — and is not a deviation.
Created: 2026-02-27 Last Updated: 2026-02-28 Status: ✅ Completed