Repository navigation
refactor(autoland): have steps describe themselves with a StepView - #70
Merged
Merged
Conversation
## Summary
Phase 2 of the autoland step-abstraction stack. Each step type now says what it has to show, and `display.py` decides how it looks. Before, `display.py` and `status.py` switched on the step's type to pick its status label, color, subject, detail line, prose description, and JSON fields.
- New `steps/view.py` holds the presentation-neutral contract. `StepView` is a frozen dataclass with these fields: `outcome` (a `StepOutcome`: done / active / pending / failed, moved from display's `_Outcome` with the same values), `status` ("Waiting for checks", "Assumed completed", ...), `tone`, `key` ("modular#103", "deploy.yaml", the condition or "(no condition)"), `description` (the one-line prose used in "Next steps" and in replan warnings), `note` (the PR title), `detail` (a tuple of fragments for the second line), and `data` (the type-specific `--status -o json` fields).
- `tone` is a semantic `StepTone` (muted, success, failure, awaiting review/checks/user/workflow, in queue, plain), not a rich style. The active tones say what the step is waiting on, so no color distinction is lost and no style hint is needed. `display.py` maps tone to style in one `_TONE_STYLES` table.
- `AutolandStep.view(stack, *, is_next, assumed_completed)` is a new abstract method, implemented on `LandStep`, `WorkflowStep`, and `ConfirmStep`. Display computes the two plan-position facts it takes (the pointer, and `is_assumed_completed`), so steps never see the plan or the pointer logic.
- The per-type tables move into the step modules: `STATE_LABELS`/`STATE_STYLES`/`_PR_STATE_OUTCOMES` go to `steps/land.py`, and `_WORKFLOW_STEP_STATUS` goes to `steps/workflow.py`. `_step_status`, `_step_target`, `_step_detail`, `_STEP_KINDS` (now `step.label`), `_land_entry`, and the type branches of `_describe_step` and `status._step_json` are gone. `_describe_step` remains as a one-line wrapper that returns `view().description`.
- `display.py` keeps all presentation: row layout, glyphs and icons, column widths, rich versus plain rendering, styles, markup escaping, joining detail fragments with the glyph separator, the headline, and the next-steps lines. `status.py` builds each step's JSON from generic fields plus `view.data`. Neither one branches on step type anymore, and the `assert isinstance(step, ConfirmStep)` shims from phase 1 are removed.
- `PRState` moves to a new `autoland/pr_state.py`, because `steps/land.py` needs it at runtime and `model.py` imports `steps`. `model` re-exports it, so no imports change.
No user-visible change. Console output, status text, and status JSON are byte-for-byte identical, and the order of JSON keys is unchanged.
## Test plan
- [x] Golden autoland scenarios (`tests/test_autoland_golden.py`) pass unchanged, with no golden files regenerated
- [x] New view tests in `tests/test_autoland_steps.py` pin the `StepView` for each step type: land across every `PRState`, with error/retry detail, untitled, and already landed; workflow across every state, plus its error detail; confirm when pending, awaiting confirmation, confirmed, and assumed completed, plus with no condition
- [x] The registered-step-type coverage now also requires every type to be concrete and to return a `StepView` with a status, key, and description
- [x] Full suite passes with `GH_CONFIG_DIR` empty and `GH_TOKEN` unset
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
stack-info: PR: #70, branch: micahjsmith/stack/4
micahjsmith
force-pushed
the
micahjsmith/stack/4
branch
from
October 12, 2026 23:05
c6f89bb to
d4c38a3
Compare
This was referenced Oct 12, 2026
micahjsmith
added this pull request to stack #77
October 12, 2026 23:22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked PRs:
Summary
Phase 2 of the autoland step-abstraction stack. Each step type now says what it has to show, and
display.pydecides how it looks. Before,display.pyandstatus.pyswitched on the step's type to pick its status label, color, subject, detail line, prose description, and JSON fields.steps/view.pyholds the presentation-neutral contract.StepViewis a frozen dataclass with these fields:outcome(aStepOutcome: done / active / pending / failed, moved from display's_Outcomewith the same values),status("Waiting for checks", "Assumed completed", ...),tone,key("Don't generate table of contents for stacks with just 1 PR. modular/stack-pr#103", "deploy.yaml", the condition or "(no condition)"),description(the one-line prose used in "Next steps" and in replan warnings),note(the PR title),detail(a tuple of fragments for the second line), anddata(the type-specific--status -o jsonfields).toneis a semanticStepTone(muted, success, failure, awaiting review/checks/user/workflow, in queue, plain), not a rich style. The active tones say what the step is waiting on, so no color distinction is lost and no style hint is needed.display.pymaps tone to style in one_TONE_STYLEStable.AutolandStep.view(stack, *, is_next, assumed_completed)is a new abstract method, implemented onLandStep,WorkflowStep, andConfirmStep. Display computes the two plan-position facts it takes (the pointer, andis_assumed_completed), so steps never see the plan or the pointer logic.STATE_LABELS/STATE_STYLES/_PR_STATE_OUTCOMESgo tosteps/land.py, and_WORKFLOW_STEP_STATUSgoes tosteps/workflow.py._step_status,_step_target,_step_detail,_STEP_KINDS(nowstep.label),_land_entry, and the type branches of_describe_stepandstatus._step_jsonare gone._describe_stepremains as a one-line wrapper that returnsview().description.display.pykeeps all presentation: row layout, glyphs and icons, column widths, rich versus plain rendering, styles, markup escaping, joining detail fragments with the glyph separator, the headline, and the next-steps lines.status.pybuilds each step's JSON from generic fields plusview.data. Neither one branches on step type anymore, and theassert isinstance(step, ConfirmStep)shims from phase 1 are removed.PRStatemoves to a newautoland/pr_state.py, becausesteps/land.pyneeds it at runtime andmodel.pyimportssteps.modelre-exports it, so no imports change.No user-visible change. Console output, status text, and status JSON are byte-for-byte identical, and the order of JSON keys is unchanged.
Test plan
tests/test_autoland_golden.py) pass unchanged, with no golden files regeneratedtests/test_autoland_steps.pypin theStepViewfor each step type: land across everyPRState, with error/retry detail, untitled, and already landed; workflow across every state, plus its error detail; confirm when pending, awaiting confirmation, confirmed, and assumed completed, plus with no conditionStepViewwith a status, key, and descriptionGH_CONFIG_DIRempty andGH_TOKENunset🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 5.5 (1M context) noreply@anthropic.com