Skip to content

refactor(ios): give each snapshot capture step its penalty authority - #3947

Merged
thymikee merged 5 commits into
mainfrom
refactor/snapshot-step-penalty-authority
Oct 25, 2026
Merged

thymikee merged 5 commits into
mainfrom
refactor/snapshot-step-penalty-authority

Conversation

@thymikee

@thymikee thymikee commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Summary

Refactor #3 from the iOS snapshot-performance plan, following #3896 and #3916. Each capture step now carries its own penalty authority: a new SnapshotCaptureStep (an enum over .ordinary(backend) / .boundedRecovery(backend, claimsPeriodicProbe:)) is built once by the plan factory and travels inside SnapshotBackendAttempt. The penalty recorder reads the attempt's step instead of a re-derived plan position plus a defaulted .normal channel-state argument — the seam that made #3896's probe self-renewal reachable is now unrepresentable. All XCTest-channel policy owners (penalty value, plan reorder, verdict reasons, evidence gates) move to one file, RunnerTests+SnapshotChannelPolicy.swift, whose plan construction is a single site. Behavior-preserving.

Touched files: 6 (3 production, 3 tests). No scope expansion, no interface added.

Production lines (three files, wc -l): 1057 → 1053 vs main. SnapshotCapturePlan.swift 872→630, SnapshotTiming.swift 185→143, new SnapshotChannelPolicy.swift 280. Comments net −13; the 5-line code delta replaces the removed defaulted argument and duplicated evidence gate with the step enum's payload.

Validation

Tested SHA 1e39504de9 (prior SHAs 0ae929c0ea carried the full validation below; the later commits only consolidate plan construction and trim comments). pnpm check:affected --run --head passed (receipt .tmp/validation/run-GPl9i8/receipt.json; iOS/macOS Swift lanes GitHub-authoritative). Full macOS host unit lane: 356 passed, TEST EXECUTE SUCCEEDED. 13 focused iOS-lane tests passed on a dedicated simulator. Historical red/green: the new regression test fails at 1e7092c09a^ (tree_backend_timeout renewal) and passes at head. Four mutations (drop recovery mode, omit probe claim, sweep-added step, post-response probe) each fail an existing test; the guards remain structurally present after the consolidation. pnpm check:xctest-selection and check:packaged-runner-swift pass.

Live on an owned simulator with the production runner: hostile captures armed PENALIZED, deferred plans stamped DEFERRED, the due capture claimed RECOVERY_PROBE inside the plan and did not renew the penalty, and the next ordinary probe re-armed only from its own hostile evidence (queries_backend_timeout).

Independent review of the full range found no blockers; findings were fixed across f873db3629..1e39504de9 (probe claim bound to XCTest-backed steps at construction, single plan-construction site, constraint-only comments).

CI obligations: Apple Smoke / iOS Conformance / Runner Full XCTest lanes are authoritative for the Swift suites.

🤖 Generated with Claude Code

EffectiveSnapshotCapturePlan carried a backend array, the channel state, a
tree-slice budget override, and a pin in parallel. Whether the first step was
the periodic probe was inferred from position, and the penalty recorder
accepted a channel state defaulted to .normal, so a caller that ran a bounded
recovery attempt but forgot its state could re-arm the penalty it was
recovering from - the #3896 bug class, still reachable at the interface.

A factory-built SnapshotCaptureStep now carries the backend together with its
execution mode. captureWithBackend returns the attempt carrying that step; the
recorder takes the attempt plus the warmup exemption and no other policy
arguments. Only an ordinary attempt has an arming branch; only an accepted
completed bounded recovery can clear the penalty early. Plan shapes, the
selection-cause slots from #3916, the in-band claim after the occupied skip,
and SnapshotXCTestChannelPenalty as the sole expiry owner are unchanged. The
channel policy and its verdict helpers move to a named policy file.
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.58 MB 5.58 MB +60 B
Package (unpacked) 5.58 MB 5.58 MB +60 B
Package (download) 1.69 MB 1.69 MB +252 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 41.3 ms 40.6 ms -0.7 ms
CLI --help 87.4 ms 83.2 ms -4.2 ms

@cubic-dev-ai cubic-dev-ai Bot 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.

No issues found across 6 files

View guided diff | Turn on auto-fix | Re-trigger cubic

Bind the periodic-probe claim to an XCTest-backed leading step at the factory,
so the mode cannot hand claim authority to a non-XCTest step. Align the
warmup-exemption and claim-ordering comments with the code, and drop repeated
narration of the step-carries-authority constraint.
Collapse the step's execution mode into the step itself, drop the accessors the
step made redundant, and trim the moved policy's comments to constraints the
code cannot show. Net production lines vs main now shrink.
Merge the plan construction into one site, drop the step factory and the
plan accessor the steps made redundant, and cut narration to the
constraints the code cannot show.

@cubic-dev-ai cubic-dev-ai Bot 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.

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift">

<violation number="1" location="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift:55">
P3: This comment incorrectly implies that every pin avoids arming the penalty; a tree pin remains an ordinary attempt and can arm it. Qualify the statement to private-AX pins and document the tree-pin exception.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment on lines +55 to +56
/// if any — that narrowed it. A pin shares the deferred ordering with a breaker deferral without
/// arming the penalty, so the selection cause stays separate from each step's mode.

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.

P3: This comment incorrectly implies that every pin avoids arming the penalty; a tree pin remains an ordinary attempt and can arm it. Qualify the statement to private-AX pins and document the tree-pin exception.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift, line 55:

<comment>This comment incorrectly implies that every pin avoids arming the penalty; a tree pin remains an ordinary attempt and can arm it. Qualify the statement to private-AX pins and document the tree-pin exception.</comment>

<file context>
@@ -52,9 +52,8 @@ enum SnapshotTierOutcome: Equatable {
-/// if any — that narrowed it. The selection cause stays separate from the step's execution mode:
-/// an explicit pin, a custom-action pin, and a breaker deferral can share the deferred ordering
-/// while their verdict explanations differ, and a pin is never an armed penalty.
+/// if any — that narrowed it. A pin shares the deferred ordering with a breaker deferral without
+/// arming the penalty, so the selection cause stays separate from each step's mode.
 struct EffectiveSnapshotCapturePlan {
</file context>
Suggested change
/// if any — that narrowed it. A pin shares the deferred ordering with a breaker deferral without
/// arming the penalty, so the selection cause stays separate from each step's mode.
/// if any — that narrowed it. A private-AX pin shares the deferred ordering with a breaker deferral
/// without arming the penalty; a tree pin remains an ordinary attempt. Selection cause stays separate
/// from each step's mode.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Valid — a tree pin stays an ordinary attempt and can arm the penalty; only the private-AX pin takes the deferred route. Removed the sentence instead of qualifying it: the step mode (SnapshotCaptureStep.ordinary vs .boundedRecovery) is what decides arming authority, so the struct doc should not restate it. Fixed in 1e39504.

A tree pin stays an ordinary attempt and can arm the penalty; only the
private-AX pin defers. The step mode owns that, so the struct says nothing.
@thymikee

Copy link
Copy Markdown
Member Author

This PR is ready at 1e39504. I found no problems in the code, and all 16 checks pass at this commit. The Apple Smoke, iOS Conformance and Runner Full XCTest lanes exercise the changed Swift files. There are no conflicts.

Not blocking, and you can take or leave these: the && kind.usesXCTestAccessibilityChannel conjunct at RunnerTests+SnapshotChannelPolicy.swift#L236 cannot change the result, so you could drop it or make the claim unrepresentable for non-XCTest kinds. The two swapped planVerdictReason inputs in RunnerTests+SnapshotCapturePlanTests.swift#L545 are a test correction, so a line in the PR body would help. The defaulted xCTestRecoveryProbeDue and availableBackends on the step factory (L197) could lose their defaults, so claim authority has no implicit input.

I did not rerun the Swift tests or mutations, and the red/green and 356/13 counts come from the PR body. The live simulator run was at 0ae929c, not at head. I judged head equivalent by reading the diff from 0ae929c to 1e39504, not by rerunning it.

The outdated Cubic thread no longer applies, because 1e39504 deletes the sentence it questioned (CapturePlan.swift:54-55). It can be resolved: #3947 (comment)

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 25, 2026
@thymikee
thymikee merged commit 45125d7 into main Oct 25, 2026
16 checks passed
@thymikee
thymikee deleted the refactor/snapshot-step-penalty-authority branch October 25, 2026 14:20
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-25 14:22 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant