Skip to content

Point the contributor at the next action in the post-init view - #256

Merged
juanmaguitar merged 1 commit into
juanmaguitar/nothing-points-the-contributor-at-the-next-thingfrom
juanmaguitar/next-action-post-init-panels
Aug 11, 2026
Merged

juanmaguitar merged 1 commit into
juanmaguitar/nothing-points-the-contributor-at-the-next-thingfrom
juanmaguitar/next-action-post-init-panels

Conversation

@juanmaguitar

Copy link
Copy Markdown
Collaborator

Why

PR #254 pointed the contributor at the next action in the setup checklist, but the resolver already decides a next action for the post-init view too — and nothing carried those ids onto the screen yet, so the cue silently did nothing once the wizard was skipped. This finishes #252: the same "start here" ring and scroll now reach every panel, and the cue gains a spoken equivalent for screen-reader users.

What changes

  • Wires the post-init panels as cue targets. A tiny cueProps(id) helper spreads data-next-action + the .next-action-cue class onto: the update-incomplete banner (retry-install-build), the stale-trunk banner (update-trunk), the update-in-progress tracker (updating), the Start dev server and Review & submit changes buttons (each wrapped so the ring hugs the button), and the Trac ticket panel (link-ticket). No resolver change — it already returned these ids; this just gives them a home in the DOM.
  • Adds the accessibility equivalent flagged in PR Point the contributor at the next setup step #254's review. The "Start here" pill is a decorative CSS ::before; this adds a visually-hidden role="status" aria-live="polite" region that announces Next step: <reason> as the cue moves, using the reason the resolver already returns. Gated on the active row so only the visible site speaks, cleared to nothing when there's no next action. .sr-only added to index.html.

Not in this PR: no new resolver logic or ids — the ladder and its tests shipped in PR #254. The changes here are JSX markup plus one trivial props helper, so no new *.cjs decision module was introduced (consistent with the §1 invariant: the only decision, deriveNextAction, is already a tested module).

How to test this

Platforms: any (renderer-only). Stacked on PR #254 — test this branch's Buildkite artifact and you exercise both PRs at once.

Starting state: an initialized site with the wizard skipped (the main dev view, not the checklist).

  1. Open a site that is not running and has no changes. → The Start dev server button wears the ring + "Start here", and the view scrolls to it.
  2. Start the server. → The cue clears from Start dev. Make an edit in src/ so the tree is dirty. → The cue moves to Review & submit changes.
  3. Discard/submit so the tree is clean, with no ticket linked. → The cue moves to the Trac ticket panel ("Link a ticket"). Link one. → With a running, clean, linked site, no cue shows (nothing pending).
  4. Force an Update incomplete state (update, interrupt before build). → The red Retry install & build banner is ringed, outranking everything.
  5. Trigger Update to latest trunk on a stale site. → The stale banner is ringed; while the update runs, the cue sits on the progress tracker.
  6. Screen reader (VoiceOver / NVDA): as each of the above transitions happens, hear "Next step: …" announced once. Nothing is announced for a background (inactive) site.
  7. Reduce Motion on: rings are static, scrolling is instant (unchanged from PR Point the contributor at the next setup step #254).

What must not have happened:

  • The two wrapped action-bar buttons must still line up in the row (the <span> wrappers are inline-flex; they must not collapse or misalign the buttons).
  • A linked ticket panel must not be ringed — link-ticket only activates when unlinked, even though the shared wrapper carries the data-next-action.
  • No background/inactive site scrolls or announces.
  • The live region must not chatter on every render — it speaks only when the step actually changes.

Risks and limitations

  • The Start-dev / Review-changes buttons are now each wrapped in an inline-flex span so the ring can hug them. This is the one layout-affecting change; visually confirm the row is unchanged when no cue is active.
  • Not hand-verified by the author: node_modules couldn't be installed here (the repo's EBADENGINE constraint), so no local eslint/full suite/app run. The resolver test passes directly and the renderer transpiles clean; the Buildkite artifact is the path to drive the steps above.

Related

Closes #252. Stacked on #254 (base branch juanmaguitar/nothing-points-the-contributor-at-the-next-thing) — merge #254 first.


Design decisions and alternatives considered
  • cueProps spread over per-block className expressions. One helper keeps the tag and the class in lockstep and reads the same at every call site.
  • One section-level live region, not per-block. A single polite region announcing the resolver's reason gives the "what's next" signal without threading a visually-hidden node into six panels, and it naturally follows the cue as it moves.
  • Tag the shared ticket-panel wrapper. data-next-action="link-ticket" sits on the wrapper used for both linked and unlinked states; harmless because the .next-action-cue class only activates when the resolver returns link-ticket, which it only does when no ticket is linked.
Review outcome (required — see AGENTS.md)

No findings across the five dimensions (0 [fix here] · 0 [follow-up]). The judgement pass ran in a fresh Explore subagent given only the diff and the review standard, per /self-review.

Verified: cueProps and the live region add no new decision to index.jsx (the only decision, deriveNextAction, is already a tested module — matches the §1 invariant); the six cue ids match the resolver's returned ids exactly; span-wrapping the two action-bar buttons leaves the rendered layout unchanged (each inline-flex span becomes the stretched flex child, the button fills it); data-next-action="link-ticket" on the shared ticket panel is inert when a ticket is linked (the class only activates when the resolver returns link-ticket).

One improvement applied from the review (a non-dimension robustness note, not a finding): the live region is now mounted unconditionally with only its text toggling, rather than the element being conditionally rendered — a role="status" region that appears already holding text isn't reliably announced, whereas a text change to one already in the DOM is.

Deterministic layer: npm run lint and full npm test could not run (no node_modules — EBADENGINE); the resolver test passes directly (11/11) and the renderer transpiles clean via esbuild.

@juanmaguitar
juanmaguitar force-pushed the juanmaguitar/next-action-post-init-panels branch from 5834b20 to ec039ab Compare August 11, 2026 08:03
@juanmaguitar
juanmaguitar force-pushed the juanmaguitar/next-action-post-init-panels branch from ec039ab to d47927f Compare August 11, 2026 08:11
@juanmaguitar
juanmaguitar force-pushed the juanmaguitar/next-action-post-init-panels branch from d47927f to 4ebe3f4 Compare August 11, 2026 08:13
@juanmaguitar
juanmaguitar force-pushed the juanmaguitar/next-action-post-init-panels branch from 4ebe3f4 to 28293be Compare August 11, 2026 08:45
@juanmaguitar
juanmaguitar requested a balanced review from Copilot August 11, 2026 08:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends PR #254’s next-action cue to post-initialization panels and adds screen-reader announcements.

Changes:

  • Adds cue targets to six post-init actions.
  • Adds a visually hidden live status region.
  • Review found 2 [fix here] accessibility/testing issues.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/renderer/index.jsx Wires cue targets and live announcements.
src/renderer/index.html Adds visually hidden utility styling.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/renderer/index.jsx
Comment on lines +3539 to +3540
<div className="sr-only" role="status" aria-live="polite">
{isActive && nextAction ? `Next step: ${nextAction.reason}` : ''}
Comment thread src/renderer/index.jsx
holds text, so only the visible site speaks; it clears to nothing when
there is no next action. */}
<div className="sr-only" role="status" aria-live="polite">
{isActive && nextAction ? `Next step: ${nextAction.reason}` : ''}
@juanmaguitar
juanmaguitar force-pushed the juanmaguitar/next-action-post-init-panels branch from 28293be to 37ce9ac Compare August 11, 2026 09:10
@juanmaguitar juanmaguitar added the highlight-next-action The #252 stack: pointing the contributor at the next pending action label Aug 11, 2026
PR #254 wired the setup checklist; the resolver already decided a next action
for the post-init view too, but no block carried its id, so the cue did
nothing once the wizard was skipped (#252).

Add a `cueProps(id)` helper and spread it onto the post-init blocks — the
update-incomplete and stale banners, the update tracker, the Start-dev and
Review-changes buttons, and the Trac ticket panel — so the ring and scroll
reach every panel the resolver can name.

Add the accessibility equivalent flagged in #254's review: the "Start here"
ring is a decorative CSS pseudo-element, so a visually-hidden polite live
region now announces the next step as the cue moves, using the reason the
resolver already returns. It is mounted unconditionally with only its text
toggling — a live region that appears already holding text is not reliably
read — and only the active row ever holds text, so only the visible site
speaks.

Closes #252.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@juanmaguitar
juanmaguitar force-pushed the juanmaguitar/next-action-post-init-panels branch from 37ce9ac to 7942ef1 Compare August 11, 2026 10:47
@juanmaguitar
juanmaguitar merged commit 1e9a0f6 into trunk Aug 11, 2026
3 checks passed
juanmaguitar added a commit that referenced this pull request Aug 11, 2026
## Why

The initial setup checklist labelled the **current** step "In progress"
the moment it became the next actionable step — before the contributor
clicked anything and before any work was happening. On a fresh site,
*Install npm dependencies* announced itself as "In progress" while
nothing was installing (#257), which reads as "the app is already doing
this" and invites a contributor to wait instead of clicking. The
next-action ring (#252) made it worse by drawing the eye straight to the
wrong word.

## What changes

Root cause: the checklist's `current` status — "first ready, not-done
step" — always mapped to the "In progress" label, conflating *next* with
*running*. The running signal (`installing` / `building` / `starting`,
and `isPending` for the clone) already existed for the buttons but the
label ignored it.

- New pure `setupStepLabel(status, isRunning)` in
`src/renderer/setup-steps.cjs`: a current step reads **"Ready"** until
its action is under way, and **"In progress"** only once it is. Other
statuses keep their fixed words.
- The render passes each step's own running flag (download↔`isPending`,
install↔`installing`, build↔`building`, dev↔`starting`).
- Removed the now-dead `label` keys from the `checklistVisuals` object
so the status word has one source of truth (the object keeps only
colours/indicator).

## How to test this

**Platforms:** any (renderer-only). Stacked on #254 + #256 — test this
branch's Buildkite artifact to exercise all three.

**Starting state:** a freshly created site, on the **Initial setup
checklist**, clone finished.

1. Look at the current step (*Install npm dependencies*) before
clicking. → It reads **"Ready"**, not "In progress".
2. Click **Install npm dependencies**. → While it runs, the status reads
**"In progress"**.
3. Let it finish. → Install reads **"Completed"**; **Run full build**
becomes current and reads **"Ready"** (not "In progress"). Repeat for
build → dev.
4. While the initial clone is still running (a brand-new site), the
**Download** step reads **"In progress"** — because it genuinely is.

**What must not have happened:**

- No step reads "In progress" while its button is idle and clickable.
- Completed / pending / locked steps are unchanged ("Completed" /
"Pending" / "Locked").
- The next-action ring and scroll (from #254/#256) still behave exactly
as before — only the status word changed.

## Risks and limitations

- Renderer-only; no behavioural change beyond the status word. Not
hand-verified by the author (no local app run — the repo's EBADENGINE
install constraint); `eslint .` is clean and `npm test` passes (**794**,
incl. 3 new for `setupStepLabel`). The Buildkite artifact is the
hand-test path.

## Related

Closes #257. Stacked on #256 (base branch
`juanmaguitar/next-action-post-init-panels`), which is stacked on #254.
Merge #254 → #256 → this.

---

<details>
<summary>Review outcome (required — see AGENTS.md)</summary>

**No findings across the five dimensions** (0 [fix here] · 0
[follow-up]). Judgement pass ran in a fresh `Explore` subagent given
only the diff and the review standard, per `/self-review`.

Verified: only `complete`/`current`/`pending`/`locked` reach the switch,
so `default` is exclusively `current`; each step's `running` flag
matches the flag that drives its own button (and `isPending` for the
clone, which is the only time download is `current`); no other reader of
the removed `visuals.label` remains; the `running` spread never collides
with `computeSetupStepState`'s keys; and the new tests fail on the old
hard-coded "In progress" behaviour.

Deterministic layer: `eslint .` clean; `npm test` **794 pass** (3 new).
Both run in the worktree with node_modules symlinked.

</details>

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
juanmaguitar added a commit that referenced this pull request Aug 11, 2026
## Why

The next-action cue (#252) points at the block a contributor should act
on next. A block that is *doing* work — a trunk update, a patch apply or
revert — is where attention belongs too: it should scroll into view and
glow like any other cue target, so a contributor who looked away can see
where the work is and that it isn't stuck. The trunk-update tracker was
already highlighted; this completes the idea so **any block displaying
work in progress** gets the same treatment.

## What changes

- `next-action.cjs`: a new high-priority rule — when `isApplying` is
true, point at the `applying-patch` block (the patch apply/revert step
tracker). It sits above the stale-state warnings: a live operation
outranks a warning. One flag covers both operations — a revert runs
through the same apply machinery (`runApply({ reverse: true })`), so
there is no separate "reverting" id.
- Reordered the ladder so the two in-flight, tree-owning operations
(`isApplying`, `isUpdating`) are checked before `updateIncomplete`, and
dropped the now-redundant `!isUpdating` guard. Behaviour-preserving for
the pre-existing cases.
- `index.jsx`: passes `isApplying` to the resolver and spreads
`cueProps('applying-patch')` onto the tracker (the block that only
renders while the operation runs).

After this, every in-progress block in the site view is a cue target:
the trunk-update tracker (wired earlier in the stack) and the patch
apply/revert tracker (here). Each glows and — with the assertive scroll
from the base PR — is centered in view when it starts, and the cue
clears when it ends.

## How to test this

**Platforms:** any (renderer-only). Stacked on #254 + #256 + #258 — test
this branch's Buildkite artifact to exercise the whole stack.

**Starting state:** an initialized site, wizard skipped, with the *Apply
a patch or PR* panel visible.

1. Apply a PR or a `.diff`/`.patch` file (paste a PR URL → **Apply PR**
→ **Apply and rebuild**). → While it runs, the **step tracker block
glows** (soft amber) and the view scrolls to center it.
2. Let it finish. → The glow clears from the tracker (the block
unmounts); the cue moves on to the next pending action.
3. With a patch applied, click **Revert this patch**. → The same tracker
block glows while the revert runs.
4. Trigger **Update to latest trunk** (on a stale site). → Its progress
tracker glows and scrolls into view the same way — confirming the
treatment is general, not apply-only.

**What must not have happened:**

- The glow must not linger after an operation ends (the tracker unmounts
when `isApplying` is false).
- While applying/reverting, the cue must be on the tracker — not on
start-dev / review-changes / link-ticket.
- The screen-reader live region announces "Next step: A patch is being
applied or reverted."

## Risks and limitations

- `isApplying` and `isUpdating` own the working tree and never run at
once (each disables the other's controls), so the ordering between them
is not observable in practice; `isApplying` is placed first regardless.
- Renderer-only. Not hand-verified by the author (no local app run — the
EBADENGINE install constraint); `eslint .` clean, `npm test` **796** (2
new). Buildkite artifact is the hand-test path.

## Related

Part of #252. Stacked on #258 (base branch
`juanmaguitar/setup-step-ready-label`) → #256 → #254. Merge bottom-up.

---

<details>
<summary>Review outcome (required — see AGENTS.md)</summary>

**No findings across the five dimensions** (0 [fix here] · 0
[follow-up]). Judgement pass ran in a fresh `Explore` subagent given
only the diff and the review standard, per `/self-review`.

Verified: `isApplying` is true for both apply and revert (`runApply`
sets `applyState` unconditionally, revert calls
`runApply({reverse:true})`); the ladder reorder is behaviour-preserving
across all `updateIncomplete`/`isUpdating` combinations, and dropping
the `!isUpdating` guard is safe now that `isUpdating` is checked first;
the two tree-owning operations are mutually exclusive (both bail on the
shared `running` flag), so `isApplying` winning is moot in practice and
reasonable otherwise; and the `applying-patch` id and its DOM block
appear/disappear together (`cueProps` in scope).

**One change applied from the review** (a style note, not a finding):
removed a second test that fed the resolver the same input as the first
— the apply/revert distinction lives in `runApply`, not the resolver, so
a resolver test can't prove it. Kept one test documenting the
shared-flag behaviour.

Deterministic layer: `eslint .` clean; `npm test` **796 pass** (2 new).

_Note: reviewed as the apply/revert-specific change; the reframing to
"any block showing work in progress" is a title/description change only
— no code changed since review._

</details>

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
juanmaguitar added a commit that referenced this pull request Aug 11, 2026
## Why

When a contributor pastes a PR or picks a `.diff`/`.patch` file, the app
shows a **staged preview** — "<label> changes N files" with **Apply and
rebuild** / **Cancel**. That block is a clear next action: they set it
up and the decision to apply is theirs. But it was not a cue target, so
nothing pointed at it. This highlights it like the other next-action
blocks.

## What changes

- `next-action.cjs`: a new rule — when `applyPreview` is staged, point
at the `apply-preview` block. Placed **after** the in-flight operations
(`isApplying`, `isUpdating`) and **before** the stale-state warnings and
routine steps: an explicit, waiting choice beats a standing suggestion,
but a live operation still wins. The ordering matters — the preview
block renders only under `applyPreview && !isApplying`, so once applying
starts the cue must move to the progress tracker; checking `isApplying`
first guarantees the id follows the visible block.
- `index.jsx`: passes `applyPreview` to the resolver and spreads
`cueProps('apply-preview')` onto the preview block.

## How to test this

**Platforms:** any (renderer-only). Stacked on #254 + #256 + #258 + #260
— test this branch's Buildkite artifact to exercise the whole stack.

**Starting state:** an initialized site, wizard skipped, *Apply a patch
or PR* panel visible.

1. Paste a PR URL (or number) and click **Apply PR** — or choose a
`.diff`/`.patch` file. → The **preview block** (with *Apply and rebuild*
/ *Cancel*) glows and the view scrolls to center it.
2. Click **Apply and rebuild**. → The preview unmounts and the glow
follows to the **progress tracker** (from #260).
3. Instead, click **Cancel**. → The preview clears and the cue moves on
to the next pending action.

**What must not have happened:**

- The glow must not stay on the preview once applying begins (the
preview block is gone by then).
- No glow on the preview when there is nothing staged.
- Screen reader announces "Next step: A patch is staged — apply and
rebuild, or cancel."

## Risks and limitations

- Renderer-only. Not hand-verified by the author (no local app run — the
EBADENGINE install constraint); `eslint .` clean, `npm test` **799** (3
new). Buildkite artifact is the hand-test path.

## Related

Part of #252. Stacked on #260 (base branch
`juanmaguitar/highlight-patch-progress`) → #258 → #256 → #254. Merge
bottom-up.

---

<details>
<summary>Review outcome (required — see AGENTS.md)</summary>

**No findings across the five dimensions** (0 [fix here] · 0
[follow-up]). Judgement pass ran in a fresh `Explore` subagent given
only the diff and the review standard, per `/self-review`.

Verified: the resolver checks `isApplying` before `apply-preview`, so
when applying starts the preview block unmounts and the cue follows to
the progress tracker in the same render (locked by a new test);
`Boolean(applyPreview)` is the right null-vs-object coercion, and the id
is returned only when the preview block is actually rendered (no
id-without-block window); `cueProps` in scope; full suite green. The
reviewer also confirmed a staged preview + in-flight trunk update can
coexist and `updating` correctly wins — placing `apply-preview` below
`isUpdating` is intentional, and the cue still lands on a rendered
block.

Deterministic layer: `eslint .` clean; `npm test` **799 pass** (3 new).

</details>

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@juanmaguitar
juanmaguitar deleted the juanmaguitar/next-action-post-init-panels branch August 21, 2026 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

highlight-next-action The #252 stack: pointing the contributor at the next pending action

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nothing points the contributor at the next thing to do

2 participants