Skip to content

Cover the updates section's assisted-update safety routing - #968

Merged
selfcontained merged 2 commits into
mainfrom
agt_603f9484438a/job-test-enforcer-b07bb5ca
Aug 17, 2026
Merged

Cover the updates section's assisted-update safety routing#968
selfcontained merged 2 commits into
mainfrom
agt_603f9484438a/job-test-enforcer-b07bb5ca

Conversation

@selfcontained

Copy link
Copy Markdown
Owner

Recurring Test Enforcer run. The local suite was green on the first pass (29th consecutive), so the whole run went to the queued next_focus: the five subcomponents PR #949 split out of updates-section.tsx, none of which anything mounted.

Why here

release-utils.test.ts already covers the pure predicates — isForceRequired, isAssistedPreferred, describeForceTriggers — but not which UI they drive. The contract that actually protects an install is that a release requiring the agent-assisted flow must demote the standard updater into a dropdown and route it through a force confirmation instead of running it on one click, and that decision spans UpdatesCheckPanelUpdateActionsUpdatesForceConfirmDialog. No single component owns it, so the test stubs useReleaseUpdates and mounts the real UpdatesSection, exercising all five subcomponents plus the assisted and pending-migration gates. forceConfirmOpen is held in real state inside the stub, so click → dialog → confirm is one real interaction rather than an assertion that a setter fired.

What it pins

  • A required release and a migration-gated release both demote the standard update into the menu and open the confirmation instead of running it; confirming then forces the update, cancelling runs nothing.
  • A migration evaluation error warns but leaves the one-click path open — a checker that could not run is not evidence of a risky update.
  • A recommended release promotes the assisted flow without forcing a confirmation, which is what separates assistedPreferred from forceRequired.
  • The wiring a component split is most likely to scramble: the { force: true } flag on the confirmed update, plain vs cache-clearing reload, and the channel / auto-update handlers.
  • Both in-flight-update takeovers hide the controls, so a second update cannot be launched on top of the first.

Validation

check, format, finalize:web, test (server 163 files/2783, web 65/926, ext 60, scripts 4), test:e2e (180 passed / 12 skipped) and test:e2e:live (11 passed, 23.9s, no stray e2e-* tmux sessions) all green.

Mutation battery: 27 mutants, 23 killed. The survivors are not test gaps — three are one root cause (channelSaving / autoUpdateSaving exist only as a Tailwind opacity-50 pointer-events-none class with no disabled or aria-busy, so jsdom cannot observe them and asserting would mean class-name coupling), and the fourth is OperationTakeover's phasesOrder, which belongs to that component's own coverage. Both are recorded in the Brain backlog.

One review round; both findings applied — restore Element.prototype.scrollIntoView in afterEach so the stub cannot leak into later files sharing the worker, and narrow the preferences test's name to the wiring it actually covers.

🤖 Generated with Claude Code

selfcontainedand others added 2 commits August 16, 2026 20:20
PR #949 split updates-section.tsx into five subcomponents, and nothing
mounted any of them: release-utils.test.ts covers the pure predicates
(isForceRequired, isAssistedPreferred, describeForceTriggers) but not
which UI they drive. The decision that a gated release must route the
standard updater through a confirmation spans UpdatesCheckPanel ->
UpdateActions -> UpdatesForceConfirmDialog, so the test mounts
UpdatesSection with useReleaseUpdates stubbed and drives the real
subcomponents.
Covers the paths where a regression breaks an install: a required or
migration-gated release demotes the standard update into the menu and
opens the force confirmation instead of running it, confirming forces
the update, and a migration-evaluation error warns without gating. Also
pins the wiring a component split is most likely to scramble — the
force flag on the confirmed update, plain vs cache-clearing reload, the
channel and auto-update handlers — and the two in-flight-update
takeovers that hide the controls.
Mutation battery: 27 mutants, 23 killed. The survivors are the two
saving-state affordances, which exist only as a Tailwind class with no
disabled/aria-busy for jsdom to observe, and OperationTakeover's phase
order, which belongs to that component's own coverage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deleting the scrollIntoView stub in afterEach keeps the mock out of any
later test file sharing the worker — jsdom does not define it at all, so
restoring means removing it. The preferences test was named as
persistence coverage but useReleaseUpdates is stubbed, so it only pins
that each control reports the value it was clicked with; renamed to say
that. Kept rather than deleted: it is what kills the mutants hardcoding
onChannelChange("stable") and onAutoUpdateModeChange("check").
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@selfcontained
selfcontained merged commit 7d4e2a4 into mainAug 17, 2026
1 check passed
@selfcontained
selfcontained deleted the agt_603f9484438a/job-test-enforcer-b07bb5ca branch August 17, 2026 02:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@selfcontained