fix(cli): add Name and Updated columns to agents plan list table output #3347
No reviewers
Labels
No labels
auto/needs-reevaluation
controller-managed
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
cleveragents/cleveragents-core!3347
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/plan-list-rich-output-columns"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
🔄 Code Review — REQUEST CHANGES
Reviewed PR #3347 with focus on error-handling-patterns, edge-cases, and boundary-conditions.
The PR adds the
Updatedcolumn to theagents plan listrich table output and includes both Behave and Robot Framework tests. The core change is small and well-scoped. However, there are several issues that must be addressed before merge.Required Changes
1. [PROCESS] PR Missing Milestone
v3.7.0, but this PR has no milestone assigned.v3.7.0to the PR per CONTRIBUTING.md requirements ("Every PR must be assigned to the same milestone as its linked issue").2. [PROCESS] PR Missing
Type/LabelType/label.Type/Buglabel to match the linked issue's type.3. [ERROR-HANDLING] Misleading None Guard on Non-Optional Field
Location:
src/cleveragents/cli/commands/plan.py(new code inlifecycle_list_plans())Code:
Issue:
PlanTimestamps.updated_atis typed asdatetime(notdatetime | None) withdefault_factory=datetime.now(seesrc/cleveragents/domain/models/core/plan.py:330). Theif updated_at else ""branch is dead code —updated_atwill always be adatetimeinstance, neverNoneor falsy.This is problematic because:
updated_atcan beNoneRequired: Remove the dead guard. Use the direct call:
If there's a genuine concern about data integrity, add an explicit validation at the top of the loop body that raises rather than silently degrades.
4. [EDGE-CASE] Timezone Inconsistency in Timestamp Formatting
Location:
src/cleveragents/cli/commands/plan.py(new code inlifecycle_list_plans())Issue: The existing
Elapsedcalculation (pre-existing code) correctly handles timezone-aware vs naive datetimes:But the new
Updatedcolumn simply callsupdated_at.strftime("%Y-%m-%d %H:%M")without any timezone normalization. Ifupdated_atis timezone-aware (e.g., UTC), the displayed time may confuse users who expect local time. If it's naive, it's implicitly local time.This creates an inconsistency:
Elapsedis timezone-aware, butUpdatedis not.Required: At minimum, add a code comment documenting the timezone assumption. Ideally, normalize to local time for display consistency, or include the timezone offset in the format string (e.g.,
"%Y-%m-%d %H:%M %Z").5. [TEST] No Assertion on Actual Updated Timestamp Value
features/plan_cli_spec_alignment.feature(new scenarios)"Updat"appears in the output. This verifies the column header exists but does not verify that the actual timestamp value is rendered correctly. A bug in thestrftimeformat or a wrong field reference would not be caught.datetime.now(), you can assert the current date (e.g.,"2026-04") appears in the output, or better yet, use a fixed timestamp in the test fixture and assert the exact formatted value.Observations (Non-blocking)
6. [NOTE] Robot Integration Test Uses Mocking (Pre-existing Pattern)
list_columns()function inrobot/helper_plan_cli_spec.pyusesMagicMockandpatch, which technically violates the "no mocking in integration tests" rule from CONTRIBUTING.md. However, this is a pre-existing pattern used by all other functions in the same file. This should be addressed in a separate cleanup issue, not in this PR.7. [NOTE] Column Order vs Spec
Plan ID, Name, Phase, State, Action, Project(s), Updated. The actual order includesInvariants(betweenActionandProject) and retainsElapsed(afterUpdated). TheInvariantscolumn is pre-existing and theElapsedretention is documented as a design decision. This is acceptable but worth noting for spec reconciliation.8. [NOTE] Commit Message Format
ISSUES CLOSED: #2611✅Good Aspects
plan.py"Updat","Invar") is a pragmatic approach for Rich table truncationDecision: REQUEST CHANGES 🔄
Items 1-5 must be addressed. Items 1-2 are process requirements. Items 3-5 are code quality issues aligned with the project's error-handling and testing standards.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
Code Review — PR #3347
Reviewed with focus on api-consistency, naming-conventions, and specification-compliance.
This PR adds the
Updatedcolumn to theagents plan listrich table output and includes both Behave and Robot Framework tests. The core change is well-scoped and the PR description is thorough. Below are my findings organized by review focus area.✅ What Looks Good
fix(cli): ...) withISSUES CLOSED: #2611footer ✅Closes #2611present in PR body ✅Type/Bugis present ✅-> Nonereturn annotations and docstrings ✅_make_plan()helper uses keyword-only args (*) which is clean API design ✅🔍 Findings — Specification Compliance
1. [SPEC] Branch Name Deviates from Issue Metadata
Branch: fix/cli-plan-list-columnsfix/plan-list-rich-output-columns2. [SPEC] Column Header Naming — Verify Exact Match with Spec
Plan ID, Name, Phase, State, Action, Project(s), Updated"ID"(not"Plan ID") and"Project"(not"Project(s)")."Plan ID"and"Project(s)"? If so, the pre-existing column headers"ID"and"Project"are also spec-noncompliant — but that's out of scope for this PR. Worth noting for a follow-up issue.Updatedcolumn name itself appears to match the spec. ✅3. [SPEC] Column Order Includes Non-Spec Columns
Plan ID, Name, Phase, State, Action, Project(s), UpdatedInvariants(between Action and Project) andElapsed(after Updated).Elapsedretention as a design decision. TheInvariantscolumn is pre-existing.🔍 Findings — API Consistency
4. [API] Timestamp Format Consistency Across CLI Commands
Updatedcolumn usesstrftime("%Y-%m-%d %H:%M").plan statusfor itsUpdatedfield? And by other CLI commands that display timestamps? If other commands use a different format (e.g., including seconds, or timezone), this creates an inconsistency.CLI_TIMESTAMP_FORMAT), use it. If not, consider creating one to prevent future drift.5. [API] Dead Code Guard Creates Misleading API Contract
updated_str = updated_at.strftime("%Y-%m-%d %H:%M") if updated_at else ""impliesupdated_atcan beNone.PlanTimestamps.updated_atis typed asdatetimewithdefault_factory=datetime.now— it is neverNone.updated_atis optional when it isn't.6. [API] Timezone Handling Inconsistency
Elapsedcolumn correctly handles timezone-aware vs naive datetimes.Updatedcolumn does not normalize timezone. Ifupdated_atis UTC-aware, the displayed time may confuse users expecting local time.🔍 Findings — Naming Conventions
7. [NAMING] Step Definition Naming is Consistent ✅
step_plan_list_no_filtersfollows the existingstep_plan_list_*naming pattern."I run plan list with no filters"is clear and follows the existing pattern.8. [NAMING] Robot Helper Function Naming ✅
list_columns()follows the existing pattern (list_filters(),status_fields(),cancel_reason())._COMMANDSdict key"list-columns"follows the existing kebab-case pattern.9. [NAMING] Sentinel String Naming in Robot Helper
"plan-cli-list-columns-ok"follows the existing pattern ("plan-cli-list-filters-ok","plan-cli-status-fields-ok"). ✅🔍 Findings — Test Quality
10. [TEST] Partial String Matching — Pragmatic but Fragile
"Updat"and"Invar"rather than full column names."Updat"could match other content (e.g., "Updated at:" in a different context). However, given the test is checking table output specifically, this risk is low.list_columns()already usesenv={"COLUMNS": "200"}to force a wide terminal. Could the Behave tests do the same? This would allow full column name assertions.11. [TEST] No Assertion on Actual Timestamp Value
"Updat"appears in the output (column header)."2026-04-05 10:01") appears.strftimeformat or wrong field reference would not be caught._make_plan()usesdatetime.now(), you could assert the current year-month prefix appears.12. [TEST] Robot Helper Uses Mocking (Pre-existing Pattern)
list_columns()usesMagicMockandpatch, which technically violates the "no mocking in integration tests" rule.🔍 Findings — Process
13. [PROCESS] PR Missing Milestone
v3.7.0.Summary
Overall Assessment: The core change is correct and well-scoped. The main concerns are: (1) the dead code guard that creates a misleading API contract and violates fail-fast principles, (2) missing timestamp value assertion in tests, and (3) the missing milestone. Items 1 and 3 should be addressed before merge. Item 2 would strengthen the test suite but is not strictly blocking.
Automated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
Code Review — PR #3347
Focus areas: api-consistency, naming-conventions, code-patterns
Reviewed the single commit
e3386e53which adds theUpdatedcolumn toagents plan listrich table output and includes Behave + Robot Framework tests. The core change is well-scoped (4 lines inplan.py, plus test scaffolding). Below is my independent assessment.1. [CODE-PATTERN / FAIL-FAST] Dead None Guard on Non-Optional Field
Location:
src/cleveragents/cli/commands/plan.py— new code inlifecycle_list_plans()PlanTimestamps.updated_atis typed asdatetime(notdatetime | None) withdefault_factory=datetime.now(seesrc/cleveragents/domain/models/core/plan.py:330). Theif updated_at else ""branch is unreachable dead code.This violates the project's fail-fast principle: if
updated_atwere somehowNonedue to a data integrity bug, silently rendering an empty string would mask the error rather than surfacing it. The correct fix is:If there's a genuine concern about data integrity, add an explicit
assertor raise rather than silently degrading.Severity: Medium — dead code that misleads maintainers and violates project error-handling conventions.
2. [API-CONSISTENCY] Timezone Handling Inconsistency Between Columns
Location:
src/cleveragents/cli/commands/plan.py—lifecycle_list_plans()The pre-existing
Elapsedcolumn correctly handles timezone-aware vs naive datetimes:But the new
Updatedcolumn callsupdated_at.strftime("%Y-%m-%d %H:%M")without any timezone normalization. Ifupdated_atis timezone-aware (e.g., UTC), the displayed time will be in UTC, whileElapsedis computed relative to local time. This creates an inconsistency within the same table row.Recommendation: At minimum, add a code comment documenting the timezone assumption. Ideally, normalize to local time for display consistency (e.g.,
updated_at.astimezone().strftime(...)to convert to local time), or include the timezone offset in the format string ("%Y-%m-%d %H:%M %Z").Severity: Low-Medium — potential user confusion when timestamps are timezone-aware.
3. [API-CONSISTENCY] Column Order vs Spec Definition of Done
Location:
src/cleveragents/cli/commands/plan.py— column definitionsIssue #2611's Definition of Done explicitly states:
The actual column order in the implementation is:
Differences:
Invariantsis inserted betweenActionandProject(pre-existing, not introduced by this PR)Elapsedis retained afterUpdated(documented as a design decision in the PR body)IDvs spec'sPlan ID(pre-existing)Projectvs spec'sProject(s)(pre-existing)The pre-existing deviations are out of scope for this bug fix, but the PR body should acknowledge that the DoD's column order requirement is only partially met. The PR description does note the
Elapsedretention as a design decision, which is good.Severity: Informational — the new
Updatedcolumn is correctly positioned relative to the existing columns.4. [CODE-PATTERN] Test Assertions Only Verify Column Headers, Not Values
Location:
features/plan_cli_spec_alignment.feature— new scenariosThe three new Behave scenarios check for partial column header strings (
"Name","Updat","Invar") but do not verify that actual data values are rendered correctly. For example, theUpdatedcolumn test doesn't verify that a formatted timestamp appears in the output.A bug in the
strftimeformat string, a wrong field reference, or a missing value in the row tuple would not be caught by these tests. The Robot Frameworklist_columns()helper does verify the plan name appears in output ("local/smoke-plan"), which is better, but still doesn't verify the timestamp value.Recommendation: Add at least one assertion that verifies a formatted timestamp value appears in the output. Since the test fixture creates plans with
datetime.now(), you could assert the current year-month (e.g.,"2026-04") appears, or better yet, use a fixed timestamp in the test fixture and assert the exact formatted value.Severity: Medium — tests verify structure but not correctness of the new column's data.
5. [NAMING-CONVENTIONS] Partial String Assertions — Pragmatic but Fragile
Location:
features/plan_cli_spec_alignment.featureThe use of partial strings (
"Updat"forUpdated,"Invar"forInvariants) is documented as a pragmatic choice for Rich table truncation in narrow terminals. This is reasonable for CI robustness, but:"Name"is checked as a full string, which could false-positive match other content (e.g., "Namespace" in the filters panel, or "namespaced_name" in error output)"Name") and partial-string ("Updat","Invar") checks is a minor naming/pattern inconsistencySeverity: Low — the Robot Framework test uses
env={"COLUMNS": "200"}for a wide terminal, which is a better approach. Consider applying the same technique to the Behave tests.6. [PROCESS] PR Missing Milestone
The linked issue #2611 is assigned to milestone
v3.7.0, but this PR has no milestone assigned. Per CONTRIBUTING.md, PRs must be assigned to the same milestone as their linked issue.Severity: Process — must be fixed before merge.
Positive Observations
ISSUES CLOSED: #2611)updated_str,updated_at) follows existing patterns (elapsed_str,created)"dim") is consistent with theElapsedcolumn's styleType/Buglabel matching the linked issuestrftimeformat ("%Y-%m-%d %H:%M") is compact and consistent with CLI conventionsSummary
The core change is small, correct in intent, and well-tested at the structural level. The main actionable items are:
updated_atDecision: COMMENT — Items 1 and 6 should be addressed. Item 4 is strongly recommended. The rest are informational.
Automated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
@ -144,0 +152,4 @@Scenario: Plan list rich output includes Updated columnGiven plan spec alignment plans existWhen I run plan list with no filtersThen the plan spec list should succeed[TEST] This scenario only verifies the column header (
"Updat") is present, not that an actual formatted timestamp value appears in the output. Consider adding an assertion for the timestamp value (e.g., the current year-month, or use a fixed timestamp in the fixture and assert the exact formatted string).@ -2487,6 +2488,10 @@ def lifecycle_list_plans(# Invariant countinvariant_count = str(len(plan.invariants)) if plan.invariants else "0"# Last-updated timestamp[CODE-PATTERN] Dead None guard:
PlanTimestamps.updated_atis typed asdatetime(not Optional) withdefault_factory=datetime.now. Theif updated_at else ""branch is unreachable dead code that misleads maintainers and violates the project's fail-fast principle.Simplify to:
Also consider timezone normalization for consistency with the
Elapsedcolumn's timezone-aware handling above.e3386e53dcd63372d8aeReview Feedback Addressed
Addressed all required items from the code reviews. Commit amended and force-pushed.
Changes Made
1. ✅ Milestone assigned
v3.7.0(matching linked issue #2611).2. ✅ Removed dead None guard on
updated_at(plan.py)updated_str = updated_at.strftime("%Y-%m-%d %H:%M") if updated_at else ""updated_str = plan.timestamps.updated_at.strftime("%Y-%m-%d %H:%M")PlanTimestamps.updated_atis typed asdatetime(neverNone), so the guard was dead code that misled maintainers and violated the fail-fast principle.3. ✅ Added timezone documentation comment (
plan.py)updated_atis stored as-is (naive local time by default) and no timezone conversion is applied, consistent with howcreated_atis stored.4. ✅ Added timestamp value assertion in Behave tests
And the plan spec list output should contain the current year-month timestampstep_plan_list_output_contains_timestampthat asserts the current year-month prefix (e.g."2026-04") appears in the output — verifying theUpdatedcolumn renders actual data, not just a header.step_plan_list_no_filtersto force the module-level Rich console to use 200-column width (by temporarily settingconsole._width = 200), ensuring theUpdatedcolumn is not dropped due to narrow terminal width during testing.Non-blocking Items (Acknowledged, Not Changed)
fix/plan-list-rich-output-columnsdiffers from the issue metadata'sfix/cli-plan-list-columns. This is a low-severity process deviation; the branch is already created and the PR is open, so renaming would require force-push coordination."Updat"and"Invar"partial checks remain for the header assertions. The new timestamp value assertion uses the full year-month string. The Robot helper already usesCOLUMNS=200for full-string checks.Automated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
d63372d8ae8891160bd8Review Summary — APPROVED (posted as COMMENT due to self-review API restriction)
Reviewed PR #3347 with focus on security-concerns, input-validation, and access-control.
This PR adds the
Updatedcolumn (and previously missingNamecolumn) to theagents plan listrich table output, addressing issue #2611. The change is well-scoped: a small display enhancement inlifecycle_list_plans()plus comprehensive Behave and Robot Framework tests.This is a second-round review following two prior stale COMMENT reviews and an amended force-push that addressed all previously identified issues.
✅ Security Deep Dive Results
Given special attention to security concerns, input validation, and access control:
No injection vectors: The
Updatedcolumn rendersplan.timestamps.updated_at— adatetimefield from the domain model, not user input. Thestrftime()call operates on trusted internal data. No user-controlled strings are interpolated into the display without sanitization.No new input parameters: The PR does not introduce any new CLI flags, arguments, or user-facing input paths. All existing filter parameters (
--namespace,--phase,--state,--project,--action, regex) are pre-existing and unchanged.No access control changes: The
plan listcommand's authorization model is unchanged. No new data is exposed that wasn't already accessible —updated_atis a standard timestamp field on the Plan model.Pre-existing regex handling: The
re.compile(regex)call on user input (line ~2431) is pre-existing code, not introduced by this PR. It properly wraps the compile in a try/except forre.error. No ReDoS concerns are introduced by this change.No secrets or credentials: No hardcoded values, API keys, or sensitive data introduced.
Verdict: No security concerns. ✅
✅ Standard Criteria Checks
Specification Alignment
Updatedcolumn added to the plan list rich table, matching spec requirementNamecolumn present (showingnamespaced_name)Invariants,Elapsed)CONTRIBUTING.md Compliance
fix(cli): add Name and Updated columns to agents plan list table output— follows Conventional Changelog formatISSUES CLOSED: #2611in commit footerType/Bugpresent# type: ignoresuppressions observed in changed filesTest Quality
step_plan_list_output_contains_timestampverifies the current year-month prefix appears in output (addressing prior review feedback)console._width = 200ensures columns aren't truncated during testinglist_columns()helper andPlan List Rich Output Includes Required Columnstest case withCOLUMNS=200env var-> Nonereturn annotations and docstringsCode Correctness
if updated_at else ""guard was removed in the amended commit, now using directplan.timestamps.updated_at.strftime("%Y-%m-%d %H:%M")Prior Review Feedback Addressed
Minor Observations (Non-blocking)
Branch name deviation: The branch
fix/plan-list-rich-output-columnsdiffers from issue #2611's metadata (fix/cli-plan-list-columns). This is a low-severity process deviation acknowledged by the implementor — not worth rebasing for.Robot helper mocking pattern: The
list_columns()function inrobot/helper_plan_cli_spec.pyusesMagicMockandpatch, which technically violates the "no mocking in integration tests" rule. This is a pre-existing pattern across all functions in the file and should be addressed in a separate cleanup issue.Partial string matching: The
"Updat"and"Invar"partial checks in Behave are pragmatic for Rich table truncation. The Robot helper usesCOLUMNS=200for full-string checks, which is the better approach. The Behave tests now also use a wide console, so full strings could potentially be used there too — but this is a minor improvement for a future PR.Decision: APPROVED ✅
The PR is a clean, well-scoped bug fix with comprehensive test coverage. All prior review feedback has been addressed. No security, input validation, or access control concerns were found.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer