Skip to content

Create erk json objective view and erk json objective check machine commands#9162

Merged
schrockn merged 6 commits intomasterfrom
plnd/O9009-objective-json-machi-03-10-1328
Mar 10, 2026
Merged

Create erk json objective view and erk json objective check machine commands#9162
schrockn merged 6 commits intomasterfrom
plnd/O9009-objective-json-machi-03-10-1328

Conversation

@schrockn
Copy link
Member

@schrockn schrockn commented Mar 10, 2026

Refactor objective commands into modular architectures with shared operations, human CLI renderers, and thin machine adapters. Extract objective view into view/operation.py, view/cli.py, and view/json_cli.py with Rich terminal output preserved. Extract objective check into check/validation.py (pure logic), check/operation.py, check/cli.py, and check/json_cli.py with [PASS]/[FAIL] rendering. Register both machine commands under new src/erk/cli/commands/json/objective/ group, accessible as erk json objective view and erk json objective check. Human CLI output and API contracts remain unchanged; --json-output flags kept for backwards compatibility with deprecation warnings.

Objective #9009: Objective: Agent-Friendly CLI

Key Changes

  • Extract objective view into modular view/ subpackage with transport-independent operation layer
  • Extract objective check into modular check/ subpackage with pure validation logic separated from CLI rendering
  • Register json_objective_view and json_objective_check under erk json objective group
  • Preserve human command behavior and output formatting (Rich tables, [PASS]/[FAIL] indicators)
  • Keep --json-output flags on human commands for backwards compatibility during transition
Files Changed

Added (13 files)

  • src/erk/cli/commands/objective/view/__init__.py
  • src/erk/cli/commands/objective/view/operation.py - Request/result types and run_objective_view()
  • src/erk/cli/commands/objective/view/cli.py - Human command with Rich output
  • src/erk/cli/commands/objective/view/json_cli.py - Machine adapter
  • src/erk/cli/commands/objective/check/__init__.py
  • src/erk/cli/commands/objective/check/validation.py - Pure validation logic
  • src/erk/cli/commands/objective/check/operation.py - Request/result types and run_objective_check()
  • src/erk/cli/commands/objective/check/cli.py - Human command with [PASS]/[FAIL] output
  • src/erk/cli/commands/objective/check/json_cli.py - Machine adapter
  • src/erk/cli/commands/json/objective/__init__.py - JSON objective group registration

Modified (8 files)

  • src/erk/cli/commands/json/__init__.py - Register json_objective_group
  • src/erk/cli/commands/objective/__init__.py - Update imports to subpackages
  • src/erk/cli/commands/objective/plan_cmd.py - Import from check/validation
  • src/erk/cli/commands/exec/scripts/objective_plan_setup.py - Import from check/validation
  • src/erk/cli/commands/exec/scripts/objective_save_to_issue.py - Import from check/validation
  • tests/unit/cli/commands/objective/test_check_cmd.py - Update imports
  • tests/unit/cli/commands/objective/test_view_cmd.py - Update imports

Deleted (2 files)

  • src/erk/cli/commands/objective/view_cmd.py - Migrated to view/ subpackage
  • src/erk/cli/commands/objective/check_cmd.py - Migrated to check/ subpackage

User Experience

Before:

$ erk objective view 9009
$ erk objective view 9009 --json-output

After:

$ erk objective view 9009                   # unchanged
$ erk json objective view < input.json      # new structured JSON
$ echo '{"identifier":"9009"}' | erk json objective view

Same pattern for check: human commands unchanged, machine commands now accessible via erk json objective check.

Human commands retain --json-output flags with deprecation warnings pointing users to erk json objective view/check.

Human CLI output, error handling, and API contracts remain unchanged. This is a pure architectural refactoring to separate transport concerns from business logic.

original-plan

Plan: Create erk json objective view and erk json objective check machine commands

Part of Objective #9009, Node 3.2

Context

The "best-of-both-worlds" machine command architecture (PR #9132) separates human CLI commands from machine-readable JSON commands. Human commands live at erk <path> with rich output; machine commands live at erk json <path> with structured JSON I/O via @machine_command.

Currently, erk objective view and erk objective check have --json-output flags inline — the legacy pattern. This plan migrates them to the new architecture: extract shared operations into operation.py, create thin json_cli.py machine adapters, and register them under erk json objective view/check.

Implementation

Phase 1: Extract objective view operation

Create src/erk/cli/commands/objective/view/operation.py

Extract from view_cmd.py:

  • ObjectiveViewRequest frozen dataclass: identifier: str | None = None (optional, infers from branch)
  • ObjectiveViewResult frozen dataclass with fields: issue_number, phases (serialized), graph_nodes, unblocked, pending_unblocked, next_node, is_complete, summary. Include to_json_dict() matching the current _display_json output shape.
  • run_objective_view() function: the shared logic from view_objective() that fetches the issue, validates the label, parses the roadmap, builds the graph, and returns ObjectiveViewResult | MachineCommandError.

Refactor view_cmd.pysrc/erk/cli/commands/objective/view/cli.py

Move the human command to the new location. It imports from operation.py and calls run_objective_view(), then renders with Rich tables (all existing _format_* and display functions move here).

Create src/erk/cli/commands/objective/view/json_cli.py

Thin machine adapter following the pr/view pattern:

@machine_command(request_type=ObjectiveViewRequest, output_types=(ObjectiveViewResult,))
@click.command("view")
@click.pass_obj
def json_objective_view(ctx, *, request):
    return run_objective_view(ctx, request, repo_id=...)

No @mcp_exposed yet — that's node 3.3.

Phase 2: Extract objective check operation

Create src/erk/cli/commands/objective/check/operation.py

Extract from check_cmd.py:

  • ObjectiveCheckRequest frozen dataclass: identifier: str (required), allow_legacy: bool = False
  • ObjectiveCheckResult frozen dataclass wrapping ObjectiveValidationSuccess data with to_json_dict() matching current _output_json shape.
  • run_objective_check(): calls existing validate_objective() and wraps the result.

Keep validate_objective(), ObjectiveValidationSuccess, ObjectiveValidationError, and the _check_* helper functions in a shared module (either keep them in check_cmd.py and import, or move to check/validation.py). Prefer moving to check/validation.py since it's pure logic.

Refactor check_cmd.pysrc/erk/cli/commands/objective/check/cli.py

Human command that calls run_objective_check() and renders [PASS]/[FAIL] output.

Create src/erk/cli/commands/objective/check/json_cli.py

Thin machine adapter:

@machine_command(request_type=ObjectiveCheckRequest, output_types=(ObjectiveCheckResult,))
@click.command("check")
@click.pass_obj
def json_objective_check(ctx, *, request):
    return run_objective_check(ctx, request, repo_id=...)

Phase 3: Register in JSON command tree

Create src/erk/cli/commands/json/objective/__init__.py

@click.group("objective")
def json_objective_group():
    """Machine-readable objective commands."""
    pass

json_objective_group.add_command(json_objective_view, name="view")
json_objective_group.add_command(json_objective_check, name="check")

Update src/erk/cli/commands/json/__init__.py

json_group.add_command(json_objective_group, name="objective")

Update src/erk/cli/commands/objective/__init__.py

Update imports from new subpackage locations (view/cli.py, check/cli.py).

Phase 4: Deprecate --json-output flags

Keep the --json-output flags on the human commands for backwards compatibility during transition, but have them emit a deprecation warning pointing to erk json objective view/check. The flags delegate to the same run_* operation.

Key Files

File Action
src/erk/cli/commands/objective/view_cmd.py Delete (split into view/ subpackage)
src/erk/cli/commands/objective/check_cmd.py Delete (split into check/ subpackage)
src/erk/cli/commands/objective/view/__init__.py Create (empty)
src/erk/cli/commands/objective/view/operation.py Create (request/result/run)
src/erk/cli/commands/objective/view/cli.py Create (human command)
src/erk/cli/commands/objective/view/json_cli.py Create (machine adapter)
src/erk/cli/commands/objective/check/__init__.py Create (empty)
src/erk/cli/commands/objective/check/validation.py Create (validation logic)
src/erk/cli/commands/objective/check/operation.py Create (request/result/run)
src/erk/cli/commands/objective/check/cli.py Create (human command)
src/erk/cli/commands/objective/check/json_cli.py Create (machine adapter)
src/erk/cli/commands/json/objective/__init__.py Create (json group)
src/erk/cli/commands/json/__init__.py Update (add objective group)
src/erk/cli/commands/objective/__init__.py Update imports

Patterns to Follow

  • Decorator order: @machine_command > @click.command > @click.pass_obj (no @mcp_exposed — that's node 3.3)
  • Repo resolution: Use resolve_owner_repo(ctx, target_repo=None) in json_cli.py (same as pr/view/json_cli.py)
  • Error returns: Return MachineCommandError(error_type=..., message=...) not exceptions
  • Result serialization: Implement to_json_dict() on result dataclasses
  • Request defaults: Use defaults for optional fields (e.g., identifier: str | None = None)
  • No @mcp_exposed: That's explicitly node 3.3, not this node

Verification

  1. erk json objective view --schema returns valid input/output schema
  2. echo '{"identifier": "9009"}' | erk json objective view returns JSON matching current erk objective view 9009 --json-output
  3. erk json objective check --schema returns valid schema
  4. echo '{"identifier": "9009"}' | erk json objective check returns JSON matching current erk objective check 9009 --json-output
  5. erk objective view 9009 still works (human output unchanged)
  6. erk objective check 9009 still works (human output unchanged)
  7. Run existing tests: pytest tests/ -k objective
  8. Run ty and ruff checks
plan-header
schema_version: '2'
created_at: '2026-03-10T13:28:45.766357+00:00'
created_by: schrockn
plan_comment_id: null
last_dispatched_run_id: '22907526992'
last_dispatched_node_id: WFR_kwLOPxC3hc8AAAAFVWUfUA
last_dispatched_at: '2026-03-10T14:33:19+00:00'
last_local_impl_at: null
last_local_impl_event: null
last_local_impl_session: null
last_local_impl_user: null
last_remote_impl_at: '2026-03-10T14:33:19+00:00'
last_remote_impl_run_id: '22907526992'
last_remote_impl_session_id: a6843f1d-89ea-427e-a407-9182c6af85d8
branch_name: plnd/O9009-objective-json-machi-03-10-1328
objective_issue: 9009
created_from_session: 22c8f695-dac4-412f-981f-b33d83413118
lifecycle_stage: impl
last_session_branch: planned-pr-context/9162
last_session_id: a6843f1d-89ea-427e-a407-9182c6af85d8
last_session_at: '2026-03-10T14:33:15.582849+00:00'
last_session_source: remote
---

To replicate this PR locally, run:

erk pr teleport 9162

@schrockn schrockn added the erk-pr PR created by erk label Mar 10, 2026
@schrockn
Copy link
Member Author

Plan Queued for Implementation

submission-queued
status: queued
queued_at: '2026-03-10T13:29:50.091017+00:00'
submitted_by: schrockn
validation_results:
  pr_is_open: true
  has_erk_pr_title: true
expected_workflow: plan-implement
trigger_mechanism: label-based-webhook
pr_number: 9162

Plan submitted by schrockn at 2026-03-10T13:29:50.091017+00:00.

The plan-implement workflow has been dispatched via direct dispatch.

Workflow run: https://github.com/dagster-io/erk/actions/runs/22904871122

@github-actions
Copy link
Contributor

⚙️ GitHub Action Started

workflow-started
status: started
started_at: '2026-03-10T13:30:23Z'
workflow_run_id: '22904871122'
workflow_run_url: https://github.com/dagster-io/erk/actions/runs/22904871122
branch_name: plnd/O9009-objective-json-machi-03-10-1328
pr_number: 9162

Setup completed successfully.

Branch: plnd/O9009-objective-json-machi-03-10-1328
PR: #9162
Status: Ready for implementation

View workflow run

schrockn added a commit that referenced this pull request Mar 10, 2026
@github-actions github-actions bot changed the title [erk-pr] Plan: Create erk json objective view and erk json objective check machine commands Create erk json objective view and erk json objective check machine commands Mar 10, 2026
…ne commands

This PR implements machine-readable JSON commands for objective operations, separating human CLI commands from structured JSON I/O. Both `objective view` and `objective check` are refactored into modular architectures with shared operations, human CLI renderers, and thin machine adapters registered under `erk json objective`.

## Key Changes

- Extract `objective view` into `view/operation.py` (request/result types, `run_objective_view()` function), `view/cli.py` (human command with Rich output), and `view/json_cli.py` (machine adapter)
- Extract `objective check` into `check/validation.py` (pure validation logic), `check/operation.py` (request/result types, `run_objective_check()` function), `check/cli.py` (human command with [PASS]/[FAIL] output), and `check/json_cli.py` (machine adapter)
- Register `json_objective_view` and `json_objective_check` under new `src/erk/cli/commands/json/objective/` group, accessible as `erk json objective view` and `erk json objective check`
- Keep `--json-output` flags on human commands for backwards compatibility during transition (deprecated but functional)
- Update imports across exec scripts and test files to reference new module locations

<details>
<summary>Files Changed</summary>

### Added (13 files)
- `src/erk/cli/commands/objective/view/__init__.py` - View subpackage
- `src/erk/cli/commands/objective/view/operation.py` - Request/result types and operation
- `src/erk/cli/commands/objective/view/cli.py` - Human command with Rich tables
- `src/erk/cli/commands/objective/view/json_cli.py` - Machine adapter
- `src/erk/cli/commands/objective/check/__init__.py` - Check subpackage
- `src/erk/cli/commands/objective/check/validation.py` - Pure validation logic
- `src/erk/cli/commands/objective/check/operation.py` - Request/result types and operation
- `src/erk/cli/commands/objective/check/cli.py` - Human command with [PASS]/[FAIL] output
- `src/erk/cli/commands/objective/check/json_cli.py` - Machine adapter
- `src/erk/cli/commands/json/objective/__init__.py` - JSON objective group

### Modified (8 files)
- `src/erk/cli/commands/json/__init__.py` - Register json_objective_group
- `src/erk/cli/commands/objective/__init__.py` - Update imports to subpackages
- `src/erk/cli/commands/objective/plan_cmd.py` - Import from check/validation
- `src/erk/cli/commands/exec/scripts/objective_plan_setup.py` - Import from check/validation
- `src/erk/cli/commands/exec/scripts/objective_save_to_issue.py` - Import from check/validation
- `tests/unit/cli/commands/objective/test_check_cmd.py` - Import from new locations
- `tests/unit/cli/commands/objective/test_view_cmd.py` - Import from new locations

### Deleted (2 files)
- `src/erk/cli/commands/objective/view_cmd.py` - Migrated to view/ subpackage
- `src/erk/cli/commands/objective/check_cmd.py` - Migrated to check/ subpackage

</details>

## User Experience

**Before:**
```
$ erk objective view 9009
$ erk objective view 9009 --json-output    # legacy inline JSON output
```

**After:**
```
$ erk objective view 9009                   # unchanged human output
$ erk json objective view                   # new structured JSON from stdin
$ echo '{"identifier": "9009"}' | erk json objective view
```

Same pattern for `check`: human commands unchanged, new machine commands accessible via `erk json objective check`.

Human commands retain deprecated `--json-output` flags that emit warnings directing users to `erk json objective <view|check>`.

Human CLI output, API contracts, and error handling remain unchanged. This is a pure architectural refactoring to support machine command patterns.
@schrockn schrockn force-pushed the plnd/O9009-objective-json-machi-03-10-1328 branch from e08ca50 to 91eeb8b Compare March 10, 2026 13:44
@github-actions github-actions bot marked this pull request as ready for review March 10, 2026 13:45
@github-actions
Copy link
Contributor

✅ Test Coverage Review

Last updated: 2026-03-10 06:48:43 PT

No violations detected. All test coverage requirements met.

Details

Source Files Reviewed

File Type Status Tests
src/erk/cli/commands/objective/check/cli.py Added Click wrapper ✅ Covered by test_check_cmd.py
src/erk/cli/commands/objective/check/json_cli.py Added Machine adapter ✅ Covered by test suite
src/erk/cli/commands/objective/check/operation.py Added Operation layer ✅ Covered by test_check_cmd.py
src/erk/cli/commands/objective/view/cli.py Added Click wrapper ✅ Covered by test_view_cmd.py
src/erk/cli/commands/objective/view/json_cli.py Added Machine adapter ✅ Covered by test suite
src/erk/cli/commands/objective/view/operation.py Added Operation layer ✅ Covered by test_view_cmd.py
src/erk/cli/commands/json/objective/__init__.py Added Command group ✅ Re-export/delegation only

Refactoring Notes

This PR restructures objective commands by splitting monolithic files into modular layers:

  • CLI layer (cli.py): Click command decorators + human-readable output
  • Machine adapter (json_cli.py): JSON input/output for machine consumption
  • Operation layer (operation.py): Transport-independent business logic
  • Validation layer (validation.py): Pure validation functions

Existing comprehensive tests cover all three layers:

  • tests/unit/cli/commands/objective/test_check_cmd.py (1167 lines)
  • tests/unit/cli/commands/objective/test_view_cmd.py (863 lines)

Files Excluded from Analysis

  • __init__.py files (empty or re-exports only)
  • Import path updates in existing modules (not logic changes)

Activity Log

  • 2026-03-10 06:48:43 PT: PR 9162 — Refactoring objective commands into modular layers. All new source files covered by existing test suites. No test coverage gaps detected.

@github-actions
Copy link
Contributor

✅ Dignified Python Review

Last updated: 2026-03-10 06:48:50 PT

No violations found. All Python code in PR 9162 adheres to dignified-python standards.

Details

Patterns Checked

✅ LBYL over EAFP - Proper control flow patterns with isinstance() checks
✅ Frozen dataclasses - All dataclasses use @DataClass(frozen=True)
✅ Absolute imports - All imports use absolute paths
✅ Module-level imports - No inline imports detected
✅ Import aliases - No gratuitous renaming, well-known aliases respected
✅ Path operations - No path safety issues detected
✅ 5+ parameter functions - Click command callbacks properly use keyword-only separator
✅ Error handling - Proper error boundaries at CLI level
✅ Click patterns - Consistent use of click.echo() and SystemExit for exits

Files Reviewed

  • src/erk/cli/commands/exec/scripts/objective_plan_setup.py: 0 violations
  • src/erk/cli/commands/exec/scripts/objective_save_to_issue.py: 0 violations
  • src/erk/cli/commands/json/__init__.py: 0 violations
  • src/erk/cli/commands/json/objective/__init__.py: 0 violations
  • src/erk/cli/commands/objective/__init__.py: 0 violations
  • src/erk/cli/commands/objective/check/__init__.py: 0 violations
  • src/erk/cli/commands/objective/check/cli.py: 0 violations
  • src/erk/cli/commands/objective/check/json_cli.py: 0 violations
  • src/erk/cli/commands/objective/check/operation.py: 0 violations
  • src/erk/cli/commands/objective/check/validation.py: 0 violations
  • src/erk/cli/commands/objective/plan_cmd.py: 0 violations
  • src/erk/cli/commands/objective/view/__init__.py: 0 violations
  • src/erk/cli/commands/objective/view/cli.py: 0 violations
  • src/erk/cli/commands/objective/view/json_cli.py: 0 violations
  • src/erk/cli/commands/objective/view/operation.py: 0 violations

Activity Log

  • 2026-03-10 06:48:50 PT: No violations detected. Code demonstrates excellent adherence to dignified-python standards with proper refactoring of CLI commands into modular operation/cli/json_cli structure.

@github-actions
Copy link
Contributor

✅ Tripwires Review

Last updated: 2026-03-10 06:49:05 PT

No tripwire violations detected. This PR correctly follows the machine command architecture pattern.

Details

Tripwires Triggered

Tier 1 Pattern Matches

✅ All mechanical pattern checks passed

Tier 2 Semantic Matches

adding --json flag to human command - Compliant (deprecated in favor of erk json tree)
result dataclass to_json_dict() - Compliant (both Result classes implement it correctly)
cli.py delegates to operation - Compliant (no duplicate logic)
rich objects in Result - Compliant (carries ObjectiveValidationSuccess, not pre-serialized dicts)

Files Reviewed

  • src/erk/cli/commands/json/__init__.py: 0 violations
  • src/erk/cli/commands/json/objective/__init__.py: 0 violations
  • src/erk/cli/commands/objective/__init__.py: 0 violations
  • src/erk/cli/commands/objective/check/cli.py: 0 violations
  • src/erk/cli/commands/objective/check/json_cli.py: 0 violations
  • src/erk/cli/commands/objective/check/operation.py: 0 violations
  • src/erk/cli/commands/objective/check/validation.py: 0 violations
  • src/erk/cli/commands/objective/view/cli.py: 0 violations
  • src/erk/cli/commands/objective/view/json_cli.py: 0 violations
  • src/erk/cli/commands/objective/view/operation.py: 0 violations
  • tests/unit/cli/commands/objective/test_check_cmd.py: 0 violations
  • tests/unit/cli/commands/objective/test_view_cmd.py: 0 violations

Activity Log

  • 2026-03-10 06:49:05 PT: No violations detected. Clean refactoring following machine command architecture.

@github-actions
Copy link
Contributor

❌ Dignified Code Simplifier Review

Last updated: 2026-03-10 06:49:19 PT

Found 1 violation across 1 file. Inline comment posted.

Details

Patterns Checked

✅ LBYL exception handling - None found
✅ Pathlib usage - None found
✅ Absolute imports - None found
❌ Inline imports - Found in src/erk/cli/commands/objective/check/validation.py:89

Violations Summary

  • validation.py:89: Inline import of rerender_comment_roadmap without justification

Files Reviewed

  • objective_plan_setup.py: 0 violations
  • objective_save_to_issue.py: 0 violations
  • json/__init__.py: 0 violations
  • json/objective/__init__.py: 0 violations
  • objective/__init__.py: 0 violations
  • check/__init__.py: 0 violations
  • check/cli.py: 0 violations
  • check/json_cli.py: 0 violations
  • check/operation.py: 0 violations
  • check/validation.py: 1 violation
  • plan_cmd.py: 0 violations
  • view/__init__.py: 0 violations
  • view/cli.py: 0 violations
  • view/json_cli.py: 0 violations
  • view/operation.py: 0 violations
  • test_check_cmd.py: 0 violations
  • test_view_cmd.py: 0 violations

Activity Log

  • 2026-03-10 06:49:19 PT: Found 1 violation (inline import in validation.py:89)

@github-actions
Copy link
Contributor

PR Review Comments Addressed

This PR was reviewed by an automated process triggered by:

erk pr address-remote 9162

Result

No changes were needed to address the review comments.

This could mean:

  • Comments were already addressed in a previous commit
  • Comments were informational (no action required)
  • Claude determined the existing code already satisfies the feedback

Details


Automated by erk pr-address workflow

Copy link
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@schrockn schrockn merged commit 3ec429d into master Mar 10, 2026
19 checks passed
@schrockn schrockn deleted the plnd/O9009-objective-json-machi-03-10-1328 branch March 10, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

erk-pr PR created by erk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant