refactor(tui): adopt command shapes in utility group (FEAT-018) - #5525
Conversation
Lstarsky0
left a comment
There was a problem hiding this comment.
Went at the preservation claim first. Bare App is gone from all seven handlers — 31 references down to 0 — while AppAction and the rest stay exactly where the body says they do. Comparing string literals outside the test modules, the messages that left the handlers land verbatim in contract.rs: Attachment not found:, Attachment is not a file:, the unsupported-type sentence, both operation-digest strings. attach_media also keeps the original check order, so "not found" still prints the pre-canonicalize path and the other two print the canonical one. /automation's --confirm parsing is byte-identical apart from the parameter swap.
One thing isn't preserved. Base rendered the MCP recommendations with chained .replace("{restart_command}", …), which tolerates a locale entry whose placeholders don't line up — you get that locale's text, possibly with a literal {restart_command} sitting in it. apply_named_replacements (contract.rs:550) instead requires the placeholder set to equal the supplied names exactly; missing, extra and duplicate all return None, and the handler then falls back to a hardcoded English string. So a translator who drops {restart_command} from mcp_recommendations_safety turns that whole line English instead of leaving a visible artifact. kimi_import_and_new_mcp_recommendations_have_complete_locale_parity (localization.rs:4548) does cover those keys, but only for non-empty and different-from-English — placeholder parity is asserted for the Automation* and Coordination* packs (:3917) and the permission messages, not for these. So nothing in CI catches the drift, and the four fallback strings are hand-copies of the en catalog with nothing holding them to it.
Second one is about the shape rather than this diff. CommandContexts makes every facet an Option, but the only production builder — CommandContextBundle::contexts() at contract.rs:648 — sets all nine unconditionally, so None never happens and each migrated handler pays for it with .expect("… facet"). Five of those now. Nothing connects what a command declares it needs to what the host actually filled in; it's convention held in place by there being exactly one builder. This is FEAT-018 of about twenty, so whatever it settles is what the rest inherit.
Small: the doc comment right above that builder still says "Owns seven facet objects" (contract.rs:630) — this PR makes it nine. And the five-line comment explaining why an image is validated at command time rather than at send time didn't travel with the block into MediaAdapter::attach_media.
|
Thank you for tracing both behavior preservation and the boundary shape so carefully. Addressed in The weak full-envelope convention is now an enforced least-capability contract:
On localization: the existing I also updated the stale seven-facet comments to nine and restored the rationale for validating image bytes at command time beside the moved validation block. The strengthened invariant has been added to EPIC-006 and FEAT-019 through FEAT-046 so later migration slices and physical moves preserve the same pattern. Local verification is green: contract 10/10, TUI library 10,865 passed with 13 ignored, strict workspace Clippy with zero warnings, plus boundary 8/8, migration 54/54, CI-wiring 11/11, and live architecture gates. The temporary TUI-owned result/action data references are unchanged and remain explicitly assigned to FEAT-037 before the FEAT-038 physical utility-group move. Paulo Aboim Pinto |
…mand contract - CommandPresentationContext: stable-key translation with named replacements (D3) - CommandMediaContext + MediaAttachmentReceipt: atomic composer/media attach (D4) - CommandWorkspaceContext::operation_digest: session-aware canonical digest (D5) - Envelope: presentation + media optional slots with duplicate-slot asserts (D7) - Contract tests: object safety, transport, translation failures, media atomicity, digest, duplicate-slot rejection (9 total) Generated with Claude Code
…(D3) Review fix: unknown translation key now fails with a generic safe error in the contract test double and the assertion verifies the raw key is not echoed. Generated with Claude Code
- PresentationAdapter: stable-key translation with named replacement validation and English fallback (D3) - MediaAdapter: atomic media validation + composer insertion, portable receipt (D4) - WorkspaceAdapter::operation_digest: session-aware canonical digest with no-active/failure semantics (D5) - Bundle carries presentation + media adapters; construction performs no eager work (D7) - 16 contract adapter tests green; boundary + migration gates PASS Generated with Claude Code
Lib build flagged unused Locale import; moved to the cfg(test) module where it is used. Generated with Claude Code
…ct registration - /automation, /mcp: Contextual handlers consuming presentation facet for localized text (D3/D6) - /attach: Contextual handler resolving paths via workspace facet + atomic media facet (D4/D6) - /task: Contextual handler with workspace operation_digest for digest (D5/D6) - /jobs, /network, /update: Pure argument-only handlers (D6) - /network uses codewhale_config leaf APIs + reqwest::Url host parse; no TUI persistence/network_policy helpers - mod.rs bridges all seven via ContextualCommand::from_contract; FunctionCommand removed from utility - FEAT-015 transitional test updated: utility excluded from legacy-only assertion - 35 utility tests + full TUI lib 10857/0 green Generated with Claude Code
…ity dispatch - utility removed from scripts/command-migration-topology.json frontier (topology scope immutable) - utility removed from PENDING_GROUPS TUI projection - Live migration gate PASS: frontier [config,core,debug,memory,plugins,project,session,skills] exact source correspondence - Public dispatch tests: 7-entry portable inventory, pure /jobs+/update, contextual /automation+/task+/mcp+/attach+/network through public seam (Task 6.2) - FEAT-015 transitional dead-code allowances removed (ContextualCommand legacy/command_handler/is_legacy); PENDING_GROUPS/parts get targeted test/gate allows - Migration fixture updated to shrunk 8-group frontier; 54/54 fixtures + CI wiring 11/11 green Generated with Claude Code
…work normalization - MediaAdapter: video-path attach test (extension-gated, composer reference preserved) - Network: wildcard/trailing-dot/case host normalization, URL-path rejection, hostless URL rejection, exact conflict removal tests Generated with Claude Code
…d duration assertion) run.metrics.duration.as_millis() > 0 flaked under saturated serial test runs: the offline simulated loop performs real fs/tempdir work that can complete sub-millisecond, so the millisecond threshold intermittently measured 0ms. Aligned granularity with the per-tool assertion (as_nanos() > 0), which is deterministic. Pre-existing on origin/main; repaired under the Boy Scout Rule. Generated with Claude Code
Require contextual command registrations to declare their exact external capability set, expose only those facets at dispatch, and fail safely when required capabilities are absent. Pure handlers continue to bypass host context construction.\n\nHarden malformed locale placeholder handling through the authoritative English catalog and retain the attachment validation rationale requested during review.
Apply the four semantics-preserving Rust 1.98 Clippy suggestions exposed by the current-main merge. Keep RetryError concrete for its existing inspection API and document the narrowly scoped result_large_err allowance.
9531e3e to
e4aad4c
Compare
|
CI follow-up complete. The Rust 1.98 strict-Clippy failure was repaired in Paulo Aboim Pinto |
Record the integrated contributions behind PRs Hmbown#5523, Hmbown#5524, and Hmbown#5525, and acknowledge the independently reviewed overlap in Hmbown#5530 without counting it as a duplicate merge. Signed-off-by: CodeWhale Bot <bot@codewhale.net>
|
Thank you @aboimpinto — this shipped in v0.9.11 🎉 Your FEAT-018 utility-group command-shapes work (contract-backed dispatch seam, capability facets) is in the released tag It landed via the v0.9.11 release integration branch rather than this PR's merge button, so GitHub left this one open + conflicting against the released tree. Closing it as shipped — not rejected. Your EPIC-006 command-decomposition work is live and credited; thanks for driving it, and apologies this PR sat open. |
The envelope grew to ten facets (session, model, cost, mode_policy, system_prompt, skills, workspace, presentation, media, memory). The stale 'seven facets' comments were the exact hygiene item flagged in the FEAT-018 PR review (Lstarsky0, Hmbown#5525) and recorded as FEAT-019 D11.
The envelope grew to ten facets (session, model, cost, mode_policy, system_prompt, skills, workspace, presentation, media, memory). The stale 'seven facets' comments were the exact hygiene item flagged in the FEAT-018 PR review (Lstarsky0, #5525) and recorded as FEAT-019 D11. (cherry picked from commit 93aa513)
The envelope grew to ten facets (session, model, cost, mode_policy, system_prompt, skills, workspace, presentation, media, memory). The stale 'seven facets' comments were the exact hygiene item flagged in the FEAT-018 PR review (Lstarsky0, Hmbown#5525) and recorded as FEAT-019 D11. (cherry picked from commit 93aa513)
Summary
FEAT-018 converts the complete TUI utility command group to the external command shapes introduced by FEAT-014 and hosted by FEAT-015. The seven command files remain under
codewhale-tui; this PR changes their execution boundary without physically moving them.This PR:
/attach,/automation,/jobs,/mcp,/network,/task, and/updatethroughRegisterCommand<CommandResult>and the existingContextualCommand::from_contractbridge;Appand executable TUI-helper access from every utility handler;codewhale-command-contract;/jobs,/network, and/updateargument-only (CommandHandler::Pure), while/attach,/automation,/mcp, and/taskdeclare and receive only their exact minimum external facets;utilityfrom the migration frontier only after the whole group is portable;Tracking: EPIC-005 / EPIC-006 / FEAT-018 in #5316.
No-Issue: FEAT-018 is tracked in umbrella #5316, which must remain open for the remaining decomposition FEATs.
Dependency boundary
The intended dependency direction remains acyclic:
Utility handlers no longer name or call concrete
App, TUI services, TUI state, localization enums, composer helpers, work-runtime helpers, config-persistence helpers, or other executable TUI behavior. The concrete implementations remain on the TUI-owned adapter side and expose only external trait objects to handlers.codewhale-command-contracthas no dependency oncodewhale-tui, enforced by the existing boundary gate.Temporary FEAT-037 compatibility references
This PR intentionally does not move shared command outcome/action ownership. The following TUI-owned data-only references remain temporarily:
CommandResultin all seven utility modules;AppActioninautomation.rs,jobs.rs,mcp.rs, andtask.rs;AutomationActioninautomation.rs;ShellJobActioninjobs.rs;McpUiActioninmcp.rs.These types describe handler outputs; they provide no access to
Appor executable TUI behavior. They are therefore the bounded compatibility exception defined by FEAT-018, not a reverse executable dependency and not circular-dependency debt.The mandatory retirement sequence is:
CommandResult,AppAction, and only the proven action payload data into external ownership, retaining compatibility re-exports as needed.codewhale-commandsafter those ownership prerequisites are external.Keeping that ownership move out of FEAT-018 is deliberate: one FEAT remains one independently reviewable structural PR, without mixing command rewiring, shared-type relocation, and physical file movement.
Capability additions
CommandPresentationContext: stable translation keys with exact named replacements and safe failure behavior.CommandMediaContext: validates and inserts media atomically, returning only a portable receipt.CommandWorkspaceContext::operation_digest: delegates session-aware capture and canonical digest formatting to the host.CommandCapabilities: each contextual registration declares its exact minimum external facet set; the dispatcher exposes only those facets, rejects empty contextual declarations, and pure handlers build no host bundle.CommandContexts/ContextParts: add presentation and media slots without a command-specific envelope, host escape hatch, or second registry.Review hardening
Commit
643b76c5fincorporates the first-production-slice review findings: exact capability declarations are enforced by the external contract; undeclared facets remain absent; missing required facets return safe command errors; malformed localized placeholder contracts fall back through the authoritative English catalog; the nine-facet comment is current; and the attachment validation rationale is retained beside the moved validation block. The same invariant is now recorded for FEAT-019 through FEAT-046 and the EPIC-006 final gate. Commite4aad4c06repairs the Rust 1.98 strict-Clippy diagnostics exposed after rebasing onto currentmain, without changing the FEAT-018 behavior or dependency boundary. The complete GitHub Actions matrix is green on this head.Scope and behavior
groups/utilityis migrated.codewhale-tui.Testing
cargo fmt --all -- --checkcargo test -p codewhale-command-contract --lib --locked— 10 passedcargo test -p codewhale-tui --lib --locked— 10,865 passed, 13 ignored in the review-hardening gatepython3 scripts/test_check_command_crate_boundaries.py— 8 passedpython3 scripts/check-command-crate-boundaries.pypython3 scripts/test_check_command_migration_manifest.py— 54 passedpython3 scripts/check-command-migration-manifest.py --baseline-ref origin/mainpython3 scripts/test_ci_migration_wiring.py— 11 passed-D warnings— 0 warningscargo test --workspace --all-features --locked— 13,074 passed in the final serialized gatecodewhale-cliandcodewhale-tuigit diff --checkChecklist
Apputility handlersutilityremoved from both migration-frontier representationsPaulo Aboim Pinto