Skip to content

Repair metric tests and qualify native subscription fixtures - #563

Open
HemSoft wants to merge 15 commits into
mainfrom
fix/issue-562-metric-fixture
Open

HemSoft wants to merge 15 commits into
mainfrom
fix/issue-562-metric-fixture

Conversation

@HemSoft

@HemSoft HemSoft commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Repair #562's stale Google catalog test references, retain the bounded #564 watch diagnosis, and qualify the two native-commerce fixture failures tracked by #565. Google support stays removed. All original metric unavailable/setup/custom/empty assertions remain.

Five stale catalog calls use the retained ProviderMetricCatalog. Named SwiftPM tests run through an opt-in manual-only input; strict workflow pins cover that intentional change. Automatic CI workloads, triggers, matrices, retries and timeouts are unchanged. Reset-copy and three billing-window fixture corrections preserve their existing account, cookie and request assertions.

The renewal-metadata test now compares the production mapper with the same verified native payload, preserving every exact-value assertion. The separate source snapshot checks all nine stable fields exactly against the mapper output; its signature timestamp is logged independently. The existing mapper becomes internal for @testable access; its executable code is unchanged. Billing-grace adds bounded native-status and fixture readback diagnostics; it does not claim a grace repair.

Acceptance Criteria

Definition of Done

  • Issue-linked changes, development changelog and retained failure evidence.
  • Fresh independent current-head review completed cleanly.
  • Current-head hosted qualification with every applicable job green.
  • Accurate final evidence, ordinary guarded merge, safe main sync, cleanup and matching claim release.

Validation

Current head: 23232c123c26111c383d9f89b4f82b25528f7bbe. No manual dispatch has run at this head yet; the preceding diagnostic run is being preserved until its actual completion event.

  • Preceding-head diagnostic run 42990740217: dispatched with ui_validation_mode=account-menu-comparison and swiftpm_regressions=true. At 8f61f17, result pending; no current-head hosted pass is claimed. Actual completion is registered to this Laptop thread.
  • R14 review found two P3s: the snapshot coverage gap is corrected at this head, and the stale-body finding was already addressed in the live body. R15 is clean at this exact head, with no P1–P3 findings. Placement constraints exclude unqualified hosted reviewer routes; the eligible fallback is Claude Opus 5.5 xhigh. This is not SFL PASS or a formal approval.
  • Previous clean R13 review applies only to 9b70619. Its Python command passed 58 tests; integration mutations caught missing trace emission, masked exit 65 and ignored trace failure after successful xcodebuild.
  • Run 42983035068, head 2f09173: FAILURE. Ordinary iOS passed 1112/1112, zero skips. Native-commerce passed 23/25 and failed billing grace plus exact renewal metadata. Other four quality jobs passed, including 13 metric tile and 20 Claude reset tests. This failed result is retained and not superseded by ordinary-test successes.
  • Qualify native subscription grace and exact-payload test fixtures #565 original grace evidence: the 75-second wait expired, service status trace was empty, and the native snapshot remained subscribed without retry/grace metadata after expiry. This does not establish scheduler starvation, subscription loss or a production defect. Current diagnostics preserve that distinction.
  • Qualify native subscription grace and exact-payload test fixtures #565 original exact-metadata evidence: signedAt differed between independently read status payloads despite the same printed second. The corrected oracle uses one verified Status for both expected values and mapper input; separate-read epochs are diagnostic, with no tolerance or changed expected values.
  • Diagnose intermittent watch snapshot transition test timeout #564 original trace at f766923: Free state and actor witness within 3.3 ms, publication at 1.66 seconds. Later corrected healthy probes do not explain it. Per-line printing perturbed 3b1628a; those timings are not production-latency evidence. Diagnose intermittent watch snapshot transition test timeout #564 remains open and DIAGNOSIS UNQUALIFIED.
  • A preceding run hit the unchanged 45-minute job limit and lost its artifact to xcresult staging-file ENOENT. The bounded trace emitter preserves watch diagnostics before later phases. Original logs and the complete failed artifact remain on Mini disk.

Risks and remaining gates

Not ready to merge. Current-head hosted qualification is pending; the exact-head R15 review is clean. The grace failure remains diagnostic, not fixed; any current-head red result must be investigated. The accepted #564 diagnostic tranche does not require inventing a watch synchronization repair, and it does not waive red checks.

All 25 native-commerce cases, exact grace/access and renewal-metadata assertions, account/transaction isolation, synthetic product configuration and existing deadlines remain. No skip, retry-for-green, deadline increase, assertion relaxation or production behavior change. No real Mac, live provider/auth/account/reset/purchase, signing, installation, release, TestFlight, ASC or pricing operation. Full Linux cost-suite sw_vers errors remain a platform limit, not passes.

The same Laptop worker retains claim chief-dispatch-0c08ab71-ios562 and every unfinished delivery item. No merge, main sync, cleanup or claim release is claimed.

Council: not required for this bounded test/fixture correction and diagnosis.

Closes #562
Refs #564
Refs #565

@HemSoft HemSoft added the model:gpt-6.1-sol PR authored by gpt-6.1-sol label Nov 11, 2026

@HemSoft HemSoft left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head f0c55bd64105933322c1b9c49cb09865c3e9c566 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67. Model: Claude Opus 5.5 (claude-opus-5-5), requested effort xhigh. This is a placement-constrained Laptop fallback for the connected Codex reviewer. It is neither an SFL PASS nor a formal approval.

Assessment: changes needed (1 finding).

P1: Two other SwiftPM test targets still use the removed GoogleUsageMetricCatalog, so both named swift test commands still fail to compile

Remaining references at this head:

Proof:

  • #557 renamed the type to ProviderMetricCatalog (CodexBarIOS/Models/ProviderMetricCatalog.swift#L4). git grep -n GoogleUsageMetricCatalog f0c55bd returns only the five call sites above. No declaration or typealias exists anywhere in the repository.
  • Package.swift declares both directories as SwiftPM test targets (CodexUsageTests at L48, GrokAuthTests at L96) with no excludes. Neither file is in CodexBarIOS.xcodeproj, so only SwiftPM compiles them. That explains why the Xcode suites passed.
  • swift test --filter filters only when tests run. It still builds every test target in the package, which is the premise of #562.
  • Hosted run 42950113108 (workflow_dispatch at this head) confirms the failure. In the SwiftPM smoke tests job, Run metric tile SwiftPM regressions concluded failure and Run Claude reset SwiftPM regressions was skipped. MetricTileContentTests now compiles, so the fixture change itself works. The build then stops at GrokAuthTests/CursorSpendingRegressionTests.swift:95:32: error: cannot find 'GoogleUsageMetricCatalog' in scope and error: fatalError. The build stopped before compiling CodexUsageTests, so the four references there are a second failure the log does not show yet.

Impact: acceptance criteria 1 and 3 of #562 remain unmet, and so does this PR's criterion "Named metric tile and Claude reset tests execute successfully at this head". The new opt-in steps at .github/workflows/ci.yml L254–L260 fail whenever swiftpm_regressions=true. The changelog entry at CHANGELOG.md L63–L66 ("Restore filtered SwiftPM test compilation") and Closes #562 would record and close a fix that has not landed.

Correction: change the five GoogleUsageMetricCatalog. references to ProviderMetricCatalog.. The signatures of metrics(for:result:missingReason:) and layoutCopyMetricIDs(for:result:) are unchanged (L29–L61), so this restores no Google code. To verify, rerun both named swift test commands at the new head and confirm the selected tests execute with zero failures, not only that they compile.

Verified

  • Fixture: ProviderMetricTileContent.resolve passes .unavailableUsage(reason) through unchanged (CodexBarIOS/Models/ProviderMetricTileContent.swift L18–L19). The synthetic custom string therefore gives the same coverage as the retired "Disabled" value. All four reason categories and the assertion remain.
  • Google removal is preserved. The diff changes only the fixture string, ci.yml and CHANGELOG.md, and restores no Gemini, Antigravity or Google catalog, auth, endpoint or migration code. No SwiftPM target references another symbol #557 removed (verifiedGoogleSubject, linkedGoogleAccountIDs, .gemini, .antigravity, or the Gemini and Antigravity services).
  • Automatic CI is unchanged. The new steps are in the dispatch-only smoke-tests job and are also gated on workflow_dispatch && inputs.swiftpm_regressions (default false). The account-menu-comparison condition change applies only under workflow_dispatch. No trigger, matrix, retry, timeout or automatic job changed. On the PR event, only SwiftLint ran and every other job was skipped (run 42950091348).
  • python -m unittest discover -s scripts/tests -p test_ci_policy.py -v passed 4 tests in a local rerun. SwiftLint concluded SUCCESS for this head in run 42950091348.
  • The six most recent completed manual SwiftPM smoke tests jobs took about 2–3 minutes each, which leaves room for the two added steps within the 15-minute timeout.

Limits

  • This review ran no Swift build or test locally (Windows laptop, no Mac use). The compile failure comes from inspection and the hosted log above.
  • At review time, run 42950113108 was still in progress overall. Only the smoke job had completed, and it failed.
  • No live provider, account, install or release operation took place.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Review finding addressed

The first Laptop cross-model review identified the same five remaining stale catalog calls as the failed hosted compilation. All five are replaced by the retained ProviderMetricCatalog in 89d9956 with every assertion unchanged. The original finding is retained as old-head evidence. Fresh exact-head independent review and hosted run 42951199605 are pending; no clean review or test pass is claimed yet.

@HemSoft HemSoft left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 89d9956ad271941991658238df60703a4796062d against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67. Model: Claude Opus 5.5 (claude-opus-5-5), effort xhigh. This is the placement-constrained Laptop fallback for the connected Codex reviewer. It is neither an SFL PASS nor a formal approval. The review covers the complete diff, not only the second commit.

Assessment: changes needed (1 finding).

P1: The ci.yml edits break two pinned workflow-hash tests, and the release gate runs one of them

Neither test was updated in this PR.

Both hashes include bytes this PR changes. That covers the new input (ci.yml#L20-L23), the two smoke steps (#L254-L260) and the comparison condition (#L524).

The first test is part of the release gate. ci.yml#L295-L297 runs python3 -m unittest scripts.tests.test_ui_release_shards -v in iOS UI journeys (iphone, original-1) before any UI work. The failure fails that shard and then Full iOS UI validation, which AGENTS.md treats as a release blocker. Run 42951199605 can't show this. Comparison mode skips the UI families.

Proof. I ran each command at both SHAs from GitHub tarballs in disposable directories on Mini, with Python 3.14.4:

Command Base 53e5383 Head 89d9956
python3 -m unittest scripts.tests.test_ui_release_shards -v 10 run, OK 10 run, 1 failure: '71e478372406a8e2c475c0d6af314c0084e887550c65ffbd1897d907bfe3eb72' != '0c10b64a…c357'
python3 -m unittest discover -s scripts/tests/manual -p test_automatic_cost.py -k test_removing_only_cost_mode_restores_complete_prior_workflow -v 1 run, OK 1 failure: 'c939b1ed55948e2e0cdaf617bee22124809928b4b3801af7605d0189cdec5156' != 'a42e96c0…df89'
python3 -m unittest discover -s scripts/tests -p test_ci_policy.py -v 4 run, OK 4 run, OK

I also wrote a separate script that reimplements both transforms. Run on the raw ci.yml blobs (LF), it gave the same four hashes.

Correction: make the fix in this PR. One option is to extend both restore transforms so they also strip the swiftpm_regressions input, its two steps and && !inputs.swiftpm_regressions. Then each test still recovers the workflow its comment describes. The other is to re-pin both hashes on purpose and update those comments. To verify, rerun the first two commands above. The release-gate module must pass 10 of 10.

Verified

  • The f0c55bd finding from review 5853228084 is fixed. No Swift source still names GoogleUsageMetricCatalog. No SwiftPM target uses any other declaration that #557 removed. I checked every removed declaration name against all SwiftPM target directories.
  • The five ProviderMetricCatalog calls do the same work as before. #557 renamed the type and dropped the Google cases, setupDescription and missingSourceConfigurations. The .codex and .cursor paths through metrics, cursorMetrics and layoutCopyMetricIDs have identical bodies.
  • Every fixture assertion stays. ProviderMetricTileContent.resolve passes .unavailableUsage(reason) through unchanged (L18-L19). The synthetic reason therefore covers the same path as the retired "Disabled" value. All four reasons are still exercised.
  • Named tests executed at this head. In run 42951199605, job SwiftPM smoke tests (130296721422), the metric tile filter ran 13 tests with 0 failures, including testUnavailableReasonRemainsDistinctFromReportedValues. The ClaudeUsageReset filter ran 20 tests in 4 suites with 0 failures. The log shows no compiler errors or warnings. The two steps took 49 s and 25 s, and the job took 3 min 24 s of its 15-minute limit.
  • Google removal holds. The diff touches three test files, ci.yml and CHANGELOG.md, and restores no Google code.
  • Automatic CI did not change. The swiftlint job block is byte-identical to base. On the PR event (run 42951176401), only SwiftLint ran (success, 52 s) and every other job was skipped. The new steps sit in the dispatch-only smoke job and also require inputs.swiftpm_regressions, which defaults to false.
  • The changelog entry sits under 2.0.1 Unreleased, Developer Experience, and matches the change.

Limits

  • No local Swift build and no Mac use. Swift results come from hosted logs.
  • CodexCreditsPoolTests and CursorSpendingRegressionTests compile at this head, but neither filter runs them. Their equivalence rests on inspection.
  • Run 42951199605 at posting time: SwiftLint, Strict concurrency, watchOS tests and SwiftPM smoke tests succeeded. iOS tests is still in progress.
  • No provider, account, install, release or CI-rerun operations.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Workflow-pin review finding addressed

Commit ffab46d deliberately updates the two complete-workflow SHA-256 pins to include the approved opt-in manual SwiftPM invocation, with comments explaining the new pinned scope. Hash equality assertions remain strict; no checks or assertions were removed. The notifier script hash and automatic-job controls are unchanged.

Exact-head Mini checks: python3 -m unittest scripts.tests.test_ui_release_shards scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow scripts.tests.test_ci_policy -v: 15 passed. Full cost-suite attempt on Mini produced five unsupported-host errors invoking macOS sw_vers; retained log /tmp/ios562-ffab46d-python.log, not claimed passed. Focused passing log /tmp/ios562-ffab46d-focused.log.

Previous-head hosted smoke executed 13 metric tile tests and 20 Claude reset tests successfully. Exact-head replacement validation at ffab46d: https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42952974281, pending. Independent exact-head review pending.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head ffab46d78de7c328b3bb1d2bdad2e03bf7b8dfd1 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, xhigh) reviewed it in place of connected Codex. This request limits placement to Home or Laptop, which rules out SFL and hosted Codex, so this review is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers the full diff.

Didn't find any major issues. 🎉

Verified:

  • Earlier findings are fixed. Review 5853228084 reported stale catalog calls. At this head, git grep GoogleUsageMetricCatalog returns nothing; base had six references in three test files. No production, project or Package.swift file changed. Review 5853357418 reported two stale pins. The release-gate shard module now passes 10 of 10, and the renamed cost pin test passes.
  • The new pins add only Remove stale Google catalog reference from metric tile tests #562. I wrote a separate implementation of both restore transforms and ran it on the raw ci.yml blobs. At base it gives the old pins (a42e96c0…, 0c10b64a…), and at head it gives the new ones (c939b1ed…, 71e47837…). For each transform, the restored text differs between base and head by exactly this PR's three ci.yml hunks. The notifier script hash, the swiftlint job and prepare-automatic-cost.py are unchanged. Both hash checks still assert strict equality. No other test or manifest pins the working-tree ci.yml.
  • Manual wiring matches the PR body. Both steps need a dispatch with swiftpm_regressions=true, which defaults to false, and they sit in the dispatch-only smoke job. In comparison mode, the flag keeps the five quality jobs and skips the UI journeys, the aggregate and the account-menu comparison. Cost mode skips the smoke job. Full mode is unchanged unless the flag is set.
  • Automatic CI is unchanged. At this head, PR-event run 42952925537 ran only SwiftLint, which passed, and skipped every other job. Ruleset 20103668 requires only SwiftLint, with strict freshness.
  • Swift test evidence. Swift sources and ci.yml here are byte-identical to 89d9956. Its smoke job (run 42951199605, job 130296721422) ran 13 metric tile tests and 20 Claude reset tests with 0 failures, including testUnavailableReasonRemainsDistinctFromReportedValues.

Commands ran on Mini (Linux, Python 3.14.4, Node 24.22.1) against GitHub tarballs of both SHAs in a disposable directory:

Command Base 53e5383 Head ffab46d
python3 -m unittest scripts.tests.test_ui_release_shards -v 10 OK 10 OK
python3 -m unittest discover -s scripts/tests -p test_ci_policy.py -v 4 OK 4 OK
python3 -m unittest discover -s scripts/tests/manual -p test_automatic_cost.py -v 16 run, 5 errors 16 run, same 5 errors
Same, with stub sw_vers and sysctl on PATH 16 OK 16 OK
node --test scripts/tests/manual/test_manual_ci_comment.mjs 14 pass 14 pass
The PR's focused 15-test command – 15 OK

The five errors are FileNotFoundError: 'sw_vers' on Linux, and they occur at base too. The stubbed run emulates the environment and is not macOS evidence.

Limits: I didn't build or test Swift locally or use a Mac. The exact-head dispatch, run 42952974281, was still running when I posted. SwiftLint and watchOS tests had passed, and the smoke, iOS and strict-concurrency jobs were in progress. This review claims no exact-head Swift test result. I performed no provider, account, install, release or CI-dispatch operation.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Baseline iOS validation failures and test-only repair

Exact-head run 42952974281 failed iOS tests: 1109 tests executed, three test methods failed with nine assertions. SwiftLint, strict concurrency, watchOS and smoke all passed. The smoke job executed 13 metric tile and 20 Claude reset tests with zero failures at ffab46d. No merge over this failed run.

The failing tests and their production sources are byte-identical to base 53e5383. Reset inventory assertions expected the messages replaced by #561 for non-selected grants. Commit 25d2181 checks the shipped selected-reset limitation for both non-selected rows and retains the two original reason assertions in a separate test selecting an unusable grant. No arbitrary-grant or Fable behavior is changed.

The two billing tests failed at page readiness and session completion; diagnostics show unresponsive WebContent processes. They previously loaded unattached WKWebViews, and the save test selected a synthetic account immediately after starting navigation. 25d2181 attaches these fixtures to a simulator window and waits for each provider document before account selection. Existing timeout bounds, success/account-isolation assertions and production auth remain unchanged. This is a test-only repair under the assigned CI-repair scope, awaiting hosted proof, not a claim the timing issue is already resolved.

Failed diagnostics retained on Mini: /tmp/ios562-ffab46d-ios-diagnostics, /tmp/ios562-ffab46d-failed.log, /tmp/ios562-ffab46d-smoke.log. Replacement exact-head run: https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42955812990. New independent review pending. No test skip or assertion relaxation added; no provider operation or real Mac used.

@HemSoft HemSoft left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 25d218128f6ab938925ed25f77a9bdaca6b7c391 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Model: Claude Opus 5.5 (claude-opus-5-5), effort xhigh, run on Laptop. This request limits placement to Home or Laptop, which rules out SFL and hosted Codex, so this review is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers all 9 changed files, with a focus on the new commit 25d2181.

Assessment: no blocking code findings; one P3. Merge stays blocked until the iOS tests job in run 42955812990 passes.

P3 (plausible, from inspection): the replacement billing session is probably not hosted in the window

CodexBarIOSTests/SubscriptionBillingTests.swift#L871 runs window.rootViewController?.view = replacement.webView. UIKit adds the root view controller's view to the window when rootViewController is set or the window becomes visible. Both already happened in hostBillingWebView at L885-L895. Reassigning view on a controller that is already on screen does not move the new view into the window. If that holds, the replacement web view runs unhosted, as it did at base, and the first session's invalidated web view stays in the window. I couldn't run UIKit here, so this comes from reading the code. Nothing in the test checks replacement.webView.window, so the PR's "including replacement session" claim is unverified.

The impact is limited. In failed run 42952974281 the replacement half passed while unhosted: ffab46d L869-L871 did not fail. L872 failed only because the first half never saved.

Fix: host the replacement through the helper, for example let replacementWindow = try hostBillingWebView(replacement.webView) with its own defer { replacementWindow.isHidden = true }. Also add XCTAssertNotNil(webView.window) at the end of hostBillingWebView. To verify, the next iOS tests run should pass that assertion for both sessions.

Timing hypothesis

  • The billing failures are intermittent, not deterministic. SubscriptionBillingTests.swift, SubscriptionBillingSignInView.swift, and ProviderSettingsViewModel.swift are byte-identical between e00cbd6 and this base. The only change between them is #561's Claude-reset fixture in UITestFixtures.swift. Hosted run 42807023756 at e00cbd6 passed iOS tests with the same unhosted web views. So one green run at this head is consistent with the fix working, and also with the flake simply passing this time. It cannot attribute the fix by itself.
  • The wrong-account failure is a WebContent launch-latency failure. It failed at its existing load wait (base L732 and L736). The retained xcodebuild.log shows the test starting at about 03:43:50. It then logs WebContent process (0x1171280c0) took 13.846741 seconds to launch at 03:44:04, which is beyond the unchanged 10 s bound at L941. Hosting in the window is the only change to this test. WebKit documents that web views outside a window are throttled or suspended (WKPreferences.inactiveSchedulingPolicy), which supports the hypothesis for work after launch. The log does not show that visibility shortens process launch. If launch takes more than 10 s again, this test will still fail.
  • The save test had no ordering race. If chooseSyntheticAccount runs while the page is loading, the manual check returns early. didFinish then calls inspectCookies(manual: false) inside the discovery window, which captures the cookie and completes (SubscriptionBillingSignInView.swift#L110-L114, #L189-L205). The first-half failure lined up with WebContent PID 17314 becoming unresponsive. The new readiness waits at L860 and L874 therefore help with diagnosis: a failure now shows whether the page never loaded or verification never finished. If the change fixes anything, it is the hosting. The waits also stop this test from covering the path where the cookie is set before the first load finishes. Coverage changed there, but no assertion was weakened.

Verified

  • Teardown and isolation hold. Defers run in reverse order: the window is hidden before session.invalidate(), viewModel.cancelSubscriptionBillingSignIn(), and removal of the defaults suite. The window is a local and is released when the test returns. testBillingRecoveryProductionViewHostEvidence (L1032-L1038) already uses the same makeKeyAndVisible plus hide-on-defer pattern. Each session keeps its own non-persistent website data store (SubscriptionBillingSignInView.swift#L47). The MemorySecretStore and UUID defaults suite are unchanged. All save, mismatch, and account-binding assertions remain (L863-L867, L877-L881). No timeout increase and no new skip; the XCTSkipUnless at L856 predates this PR.
  • The reset copy test matches shipped #561 behavior and is not weakened. For a current grant that is not selected, ClaudeUsageResetInventoryView.swift#L110-L116 shows the selected-reset limitation before the requiresLimit branch. The label dump in the failed log has exactly two copies (Limited and Disabled), and the new exact count == 2 at L397 is stricter than the old contains checks. The new testSelectedResetExplainsItsLimitOrDisabledState (L404) selects a grant with isUsableNow: false. In that case redeemableGrant is nil (ClaudeUsageResetInventory.swift#L52-L55), so both original reason strings render and no action button appears. This matches ClaudeUsageUITests.swift#L32-L35. The helper's new parameter defaults to true, so existing callers are unchanged. No Fable or arbitrary-grant behavior changed.
  • The new commit is test-only. git diff ffab46d 25d2181 touches only these two Swift test files. Across the full PR diff, no production, project, Package.swift, widget, or watch source changed. git grep GoogleUsageMetricCatalog -- '*.swift' at head returns nothing.
  • Workflow and pins. ci.yml is unchanged since the clean ffab46d review (comment 6786289193); that review's pin re-derivation still applies. The opt-in steps are gated on workflow_dispatch && inputs.swiftpm_regressions, and account-menu comparison is gated on !inputs.swiftpm_regressions. No automatic trigger, matrix, retry, or timeout changed. The PR run 42955798493 at head ran SwiftLint (success) and skipped every other job.

Commands run

  • Laptop (read-only checkout): git status --short (clean), git diff 53e5383 25d2181, git diff --stat ffab46d 25d2181, git diff --stat e00cbd6 53e5383, git grep GoogleUsageMetricCatalog 25d2181 -- '*.swift', plus source reads.
  • Mini, disposable /tmp/ios562-review-25d2181 from git archive 25d2181: python3 -m unittest scripts.tests.test_ui_release_shards scripts.tests.test_ci_policy scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow -v → 15 passed (log /tmp/ios562-review-25d2181-python.log).
  • Mini gh as HemSoft: PR, issue, review and ruleset reads; job logs for run 42955812990. The retained failure evidence (/tmp/ios562-ffab46d-ios-diagnostics/pro-native/ios/xcodebuild.log, summary.log, /tmp/ios562-ffab46d-failed.log) was read and not modified.

Current-head CI (exact SHA 25d2181, not a merge commit)

  • Required SwiftLint (ruleset, strict): success in 42955798493 and 42955812990.
  • Dispatch 42955812990: Strict concurrency, watchOS tests and SwiftPM smoke tests passed. The smoke job ran swift test --filter MetricTileContentRegressionTests (13 tests, 0 failures) and swift test --filter ClaudeUsageReset (20 tests, 0 failures). iOS tests: still in progress at 04:08Z (started 03:57:38Z). No iOS result is claimed, including for the two billing tests or the new reset test.

Limits

  • I did not run Xcode, the iOS simulator, a real Mac, or live providers. Nothing about UIKit or WebKit runtime behavior was reproduced here.
  • No CI was dispatched. No edits, checkouts, branches, approvals, thread resolutions, or merges were made.
  • The billing fix can only be confirmed by repeated passing iOS runs. A single run has a sample size of one.
  • Council review not required.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

R4 event and current ownership

R4 completed with one P3 (review 5853734292). Commit 65881b4 hosts the replacement billing WKWebView in its own simulator window and asserts non-nil window attachment for every helper-created fixture. No timeout, production auth or assertions were relaxed.

Existing hosted run 42955812990 remains in progress at 25d2181, registered to the same Laptop thread mcp:launch:0f1262cae9ebb5ff9f14d909465d58bc8b5e96c28dff439f3236780b99ff6e83. Preserve its final outcome before further validation. No duplicate run or review has been launched; the next exact-head review/validation will be for the changed head, after this actual run event. The matching central claim is retained; source delivery only, no release or live auth. Next trigger: existing run completion or PR event.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Preceding validation succeeded; exact-head qualification started

Run 42955812990 finished SUCCESS at 25d2181 with all five quality jobs passing. UI journeys, full UI aggregate and account-menu capture were intentionally skipped by the manual comparison-plus-SwiftPM route. This is the first completed green iOS result after the baseline test-only repairs; it does not prove that intermittent WebKit startup failures are eliminated.

Current PR head is 65881b4. Its only later change fixes R4 P3 with a separate window for the replacement view and a window-attachment assertion. Fresh actual hosted run https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42960321799 and one new independent Laptop review qualify this changed head. Neither is a duplicate of the completed preceding run/review. Claim remains chief-dispatch-0c08ab71-ios562. No merge or release yet.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 65881b41a8e250e45595e0cbcb4928bff754a79c against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, xhigh) reviewed it on Laptop in place of connected Codex. This request limits placement to Home or Laptop, which rules out SFL and hosted Codex; the fresh adversarial picker selected Claude. This review is neither an SFL PASS nor a formal approval. It covers the full 9-file diff and every earlier fix.

Didn't find any major issues. 🎉

Verified

  • R4's P3 is fixed. SubscriptionBillingTests.swift#L871-L872 hosts the replacement session through hostBillingWebView with its own defer { replacementWindow.isHidden = true }. The helper now asserts that webView.window is non-nil (L895) for all three hosted web views, and the rootViewController?.view reassignment is gone. Defers unwind in reverse, so both windows are hidden before cancelSubscriptionBillingSignIn() runs and the defaults suite is removed. During the replacement half, the first window still holds the completed first session's web view, and nothing else. No timeout changed (waitUntil stays at 10 s, L943). No skip was added: the XCTSkipUnless at L856 predates this PR. The save, mismatch and account-binding assertions (L864-L867, L879-L882) are unchanged.
  • The readiness waits match the fixture and need no network. The synthetic start() loads an HTML string with base URL https://grok.com/ (SubscriptionBillingSignInView.swift#L58-L68), and SubscriptionBillingSession.host(.grok) is grok.com. The waits at L860 and L875 therefore depend on no live provider.
  • Earlier fixes still hold. R1: git grep GoogleUsageMetricCatalog -- '*.swift' at this head returns nothing; base had six references. R2: both full-workflow pins pass (below), and nothing else references the renamed cost test. R3: ci.yml and both Python test files are byte-identical to ffab46d, so its workflow conclusions still apply.
  • The reset copy matches shipped behavior. ClaudeUsageResetInventoryView.swift#L110-L116 shows the selected-only message for current grants that are not selected. It shows the requiresLimit reason only for the selected grant when it can't be redeemed, because redeemableGrant requires isUsableNow (ClaudeUsageResetInventory.swift L52-L55). Together, the exact count == 2 check and the new two-case test cover every message the old assertions expected. The helper's new isUsableNow parameter defaults to true, so existing callers are unchanged.
  • No production code changed, and Google stays removed. The diff touches nothing under CodexBarIOS/, the widget or watch targets, Package.swift or the Xcode project. The gemini/antigravity strings left at head exist at base too. They belong to the retired-account migration and its fixtures.
  • Automatic CI is unchanged. The swiftlint job block is byte-identical to base, and it still runs only test_ci_policy.py, which this PR does not touch. The new steps sit in the dispatch-only smoke job and require workflow_dispatch && inputs.swiftpm_regressions, which defaults to false. PR-event run 42957503609 at this head ran SwiftLint (success) and skipped every other job. Ruleset 20103668 requires only SwiftLint, with strict freshness. This PR adds no tests to any automatic suite: the new Swift test runs only in the manual iOS job.
  • The named SwiftPM tests pass at this exact head. In run 42960321799, job SwiftPM smoke tests (130325051839) succeeded. --filter MetricTileContentRegressionTests ran 13 tests with 0 failures, including testUnavailableReasonRemainsDistinctFromReportedValues. --filter ClaudeUsageReset ran 20 tests in 4 suites with 0 failures. The log contains no compiler errors.
  • Earlier-head iOS evidence. In run 42955812990 at 25d2181, summary.json reports 1110 of 1110 iOS tests passed and 0 skipped. That includes testSelectedResetExplainsItsLimitOrDisabledState, testResetInventoryRowsExplainEachGrantState and both billing tests. The slowest WebContent launch in its xcodebuild.log took 1.96 s. At that head the replacement web view was not yet hosted, and a single green run does not show that the intermittent startup failure is gone.

Non-blocking note, not a finding: the PR body still calls 42955812990 the "Replacement exact-head run", doesn't mention 65881b4, and lists the named swift test commands as pending. Before readiness, update it with this head and the final results of run 42960321799.

Commands

  • Laptop, read-only checkout (git status clean before and after): git diff 53e5383 65881b4, git diff 25d2181 65881b4, git diff --stat ffab46d 65881b4, git grep for retired symbols and the renamed test, plus reads of the affected view, view model, inventory, probe, Package.swift and ci.yml.
  • Mini, in disposable /tmp/ios562-review-65881b4 from the GitHub tarball of 65881b4: python3 -m unittest scripts.tests.test_ui_release_shards scripts.tests.test_ci_policy scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow -v → 15 passed (python-head.log).
  • Mini gh as HemSoft: reads of the PR, issue, reviews, review threads (0), ruleset and check runs; job logs for 42960321799 (smoke) and 42955812990 (iOS); and the CodexBarIOSTests-42955812990-1 artifact.

Current-head CI (exact SHA, not a merge commit), as of 04:44Z

  • Required SwiftLint: success in PR run 42957503609. The SwiftLint job in dispatch run 42960321799 is still queued.
  • Dispatch run 42960321799: SwiftPM smoke tests passed. iOS tests, Strict concurrency and watchOS tests are still in progress. No iOS result is claimed for this head, including the new window-attachment assertion.

Limits

  • I did not use Xcode, a simulator or a real Mac, dispatch CI, or touch live providers, accounts, installs or releases. UIKit and WebKit behavior at this head comes from reading the code until its iOS job finishes.
  • CodexCreditsPoolTests and CursorSpendingRegressionTests now compile, but no filter runs them. Their equivalence comes from reading the Remove Google providers and migrate saved iOS accounts #557 rename.
  • I made no edits, checkouts, approvals, thread resolutions or merges. Council review is not required.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

New iOS failure diagnosed and fixture hosted

Run 42960321799 at 65881b4 failed only SubscriptionBillingTests.testGrokUsesItsCapturedCookieSetRatherThanClaudeSessionKey: verification completion remained empty after page readiness, with three assertions reported from that method. The reset test and previously repaired billing tests passed. Four other quality jobs passed, including all 13 metric tile and 20 reset SwiftPM tests. Failed logs/artifacts are retained on Mini at /tmp/ios562-65881b4-failed.log and /tmp/ios562-65881b4-ios-diagnostics.

Commit 83e1264 hosts that remaining Grok fixture with the same asserted simulator-window helper, preserving the captured-cookie set, completion and exactly-two-request assertions. It adds failure diagnostics for verification state, request count and synthetic message. No production/auth/timeout/skip change. This is a test-fidelity correction and not a proven elimination of intermittent WebKit stalls.

Exact-head replacement validation https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42961997119 and fresh independent Laptop review are pending. Prior clean review is earlier-head evidence only. No merge over the failed run.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Current-head validation remains blocked

Run 42961997119 at current head 83e1264 completed FAILURE. SwiftLint, strict concurrency, watchOS and SwiftPM smoke passed, including 13 metric tile and 20 Claude reset tests. All billing tests and reset-copy tests passed. iOS executed 1110 tests and failed only DashboardAndSettingsTests.testWatchSnapshotCoordinatorClearsPausedAccountsWhenFreeResolvesBeforeInitialResults: its one-second publication expectation expired, followed by three unchanged snapshot assertions. That test and its production code are unchanged from base. This is not evidence that the issue562 fix regressed watch behavior, nor permission to merge over the failed job.

Retained Mini evidence: /tmp/ios562-83e1264-failed.log and /tmp/ios562-83e1264-ios-diagnostics/pro-native/ios/{xcodebuild.log,summary.log}. No timeout increase, assertion relaxation, skip or retry was made. Existing R6 reviewer has an active continuation for this exact head and has not posted a verdict. Wait for that actual review event before selecting the next bounded repair. No duplicate run/review/timer. Product claim remains owned by this Laptop worker.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 83e126486356b4fc5823169569346e40e359d282 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, xhigh) reviewed it on Laptop in place of connected Codex. This request limits placement to Home or Laptop, which rules out SFL and hosted Codex; the fresh adversarial picker selected Claude. This review is neither an SFL PASS nor a formal approval. It covers the full 9-file diff and the 3-line change since R5.

Didn't find any major issues. 🎉

I found no P1, P2 or P3 code findings. Merge is still blocked on validation. Exact-head run 42961997119 failed iOS tests in a test this PR doesn't touch. Details are below.

Exact-head validation

  • Run 42961997119 at 83e1264: SwiftLint, strict concurrency, watchOS and SwiftPM smoke passed. The smoke job ran 13 metric tile tests and 20 Claude reset tests in four suites, with 0 failures.
  • iOS tests failed: 1110 tests ran with 4 failures, all in DashboardAndSettingsTests.testWatchSnapshotCoordinatorClearsPausedAccountsWhenFreeResolvesBeforeInitialResults. Its 1-second fulfillment timed out (the test took 3.905 s), and the three assertions after it failed (DashboardAndSettingsTests.swift#L2440-L2442).
  • This failure does not come from the PR. That test file and its production code are byte-identical to base. Its suite started at 05:05:52, before the first class this PR changes ran: ProviderUsageCardHiddenSeverityTests started at 05:06:33 and SubscriptionBillingTests at 05:06:41. None of the PR's other changed test files is in the Xcode test bundle. The same test passed in the retained ffab46d and 65881b4 logs. It is a fourth intermittent baseline failure. Remove stale Google catalog reference from metric tile tests #562's DoD says baseline defects must stay explicit, so record it with a passing exact-head rerun or a separate issue.
  • testGrokUsesItsCapturedCookieSetRatherThanClaudeSessionKey passed at this head in 4.746 s. That is one sample. It is consistent with the hosting fix but does not prove it.
  • The PR SwiftLint check passed on pull_request run 42961978485 at this head. It is the only check the ruleset requires (strict). Merge state is CLEAN, and the PR has 0 review threads.

Hosting hypothesis challenge

The R5 failure log (run 42960321799, xcodebuild.log lines 2137-2146) fits the window-hosting hypothesis but does not prove it.

  • The test took 13.992 s: about 0.8 s for load and cookies, the 3.15 s churn loop, then the 10 s wait. Page readiness and both setCookie awaits completed, so WebKit was still responding when verify() ran.
  • No Cookie header assertion in the fixture fired. Either no request reached the fixture, or every request carried both cookies.
  • WebContent[17113], the Grok session's process, logged updateFreezerStatus (suspend preparation) at 04:48:33.75, about 5.4 s after the test started. A healthy 3.4 s connect would have finished before then. Unhosted tests that passed show the same mid-test suspension, for example testFailedCredentialHasFiniteAutomaticBudgetAndManualRetry, so suspension alone does not cause the failure.
  • The cause may not be WebKit at all. The client uses 3 s request and resource timeouts, and the fixture sleeps 1.7 s per request, which leaves 1.3 s of headroom on a loaded runner. A timeout would appear as a non-nil message.

If the failure comes back, the new message tells these cases apart:

  • requests=0, verifying=false: the first cookie read stalled.
  • requests=2, verifying=true: the cookie re-read after connect stalled.
  • A non-nil message: connect failed.

The assertions on exactly two requests, the captured cookie set and completion are unchanged.

Verified

  • SubscriptionBillingTests.swift#L1458-L1459 hosts the Grok web view before its load. Defers unwind in this order: hide window, session.invalidate(), fixture.invalidate(). The private helper is in the same file as the extension that calls it, so the access is valid, and it compiled at this head.
  • No timeouts, skips or production changes anywhere in the PR. All 9 files are tests, workflow, changelog or scripts.
  • Account isolation holds. The Grok fixture uses a non-persistent data store and no configuration store. The completion test still removes its defaults suite, uses MemorySecretStore, and keeps its usage-binding assertions unchanged.
  • Google removal is intact. git grep GoogleUsageMetricCatalog finds nothing at this head; base had 6 references in 3 files. Remove Google providers and migrate saved iOS accounts #557 renamed the catalog to ProviderMetricCatalog. The synthetic reason string is equivalent to the old "Disabled", because ProviderMetricTileContent.resolve returns .unavailableUsage reasons unchanged.
  • The reset test changes match ClaudeUsageResetInventoryView.swift#L101-L115 and redeemableGrant(at:).
  • Workflow:
    • The two new steps are in the manual-only smoke-tests job and are each guarded again. swiftpm_regressions defaults to false.
    • The automatic swiftlint job is unchanged. Automatic CI still runs only test_ci_policy.py.
    • No stale references to the renamed pin test remain.
    • The 15 focused workflow-pin and CI-policy tests pass at this head.

PR body needs a refresh before merge (not a code defect)

  • The Validation section names head 65881b4 and says "iOS tests pending" for run 42960321799, which finished as a failure. The DoD review link points to R5, an earlier head.
  • "Billing fixtures attach their web views to simulator windows" claims too much. Only 3 of the 14 billing tests that load pages are hosted. The other 11 still load in unhosted web views, for example testSlowConnectSurvivesUnrelatedChurnAndDiscoveryTicks. This PR leaves their existing exposure unchanged.
  • Record run 42961997119's failure and how it was handled.

Commands and limits

  • Laptop, read-only:
    • git diff 53e5383...83e1264
    • git diff 65881b4 83e1264
    • git show and git grep at head and base
  • Mini, as HemSoft:
    • Read PR metadata, issue Remove stale Google catalog reference from metric tile tests #562, comments, reviews, GraphQL review threads and the main ruleset.
    • gh run view and gh run watch 42961997119.
    • Downloaded the job logs and the CodexBarIOSTests-42961997119-1 artifact, and read R5's retained logs.
    • Ran python3 -B -m unittest scripts.tests.test_ui_release_shards scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow scripts.tests.test_ci_policy in a disposable git archive copy: 15 passed.
  • Limits:
    • No Mac build, CI dispatch, live provider, auth, account, install or release.
    • I did not run Swift tests myself. The Swift results above come from hosted CI.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Authorized issue564 diagnostic continuation

Same Laptop worker and claim now explicitly own test-only issue564 as the blocker to issue562. Duplicate/evidence check reuses #564. Commit f766923 instruments the original one-second expectation with synchronous access-emission/state evidence and a main-actor task-entry witness. It adds a synchronous explicit-publication control that preserves unresolved retention and free-transition clearing, snapshot counts, force flags, account isolation and secrets. Production behavior and deadlines are unchanged. This is diagnostic instrumentation, not a claimed repair or established scheduler cause.

Hypotheses: missing/superseded access subscription; unexpected fixture state; delayed task execution. Trace [ios564-transition] distinguishes synchronous emission/commit, main-actor entry, sender publication and wait return. Current diagnostic hosted run: https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42965056884. It runs the named method, control and full affected suite through the existing manual runner. Earlier failure and ordinary receipt remain immutable. Independent Laptop exact-head review pending. No sleep/retry-for-green, timeout increase, skip or automatic-CI change.

Issue564 is referenced, not claimed fixed. No merge or release. Same owner retains diagnosis, final validation/review/merge/safe sync/cleanup and matching claim release.

@HemSoft HemSoft changed the title Fix stale Google catalog fixture in metric tile tests Repair stale metric fixtures and diagnose watch validation blocker Nov 11, 2026
@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

First diagnostic result and matched control

Run42965056884 at f766923 failed only the original watch transition expectation. The trace shows one resolved-Free emission at3.16ms, committed state at3.17ms, main-actor witness at3.28ms, then sender publication at1.6606s and wait return at1.6606s. The other snapshot assertions passed once publication arrived. The synchronous direct-publication control passed. Four other quality jobs and the named SwiftPM suites passed.

This rules out a missing transition or incorrect Free state in this sample. It does not distinguish timer wakeup from later actor interference, and no repair is claimed. Original diagnostics retained at /tmp/ios564-f766923-diagnostics and /tmp/ios564-f766923-failed.log on Mini.

Commit1e97206a5d03d323d211786ea1c836ed02bcaed2 adds a matched zero-coalescing-delay control. Both named tests call the same helper with the same one-second wait, event trace, startup retention, account/secret isolation, snapshot count and force assertions. The original5ms case remains intact. Only the injected fixture coalescing duration differs. This is a counter-control experiment, not a retry-for-green or claimed test fix.

Existing manual hosted run https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42966503947 qualifies both cases and full affected suite. Old-head review was stopped as superseded; fresh current-head independent Laptop review is pending. No production behavior/deadline/automatic-CI change.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 1e97206a5d03d323d211786ea1c836ed02bcaed2 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex. The placement limit rules out SFL and hosted Codex, so this is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers the full 10-file diff, with a focus on the #564 diagnostics in f766923 and 1e97206. I reviewed this head as diagnostic work; it claims no fix for #564, and this review doesn't either.

Assessment: no P1 or P2. One P3 about what the new control can show. Merge stays blocked by #564.

P3: One zero-delay pass can't show that the stall comes from the timer

DashboardAndSettingsTests.swift#L2420-L2423 runs the shared helper with coalescingDelay: .zero. The access sink calls scheduleSnapshotPublish() with no delay argument (WatchSnapshotCoordinator.swift#L405-L407). So .zero reaches Task.sleep(for: .zero) at L468. It does not reach the Task.yield() branch at L465-L466, which only entitlement changes use (L427-L429). Both variants still go through the clock-sleep enqueue and the hop back to the main actor. The only difference is the 5 ms duration. That matches comment 6787993517 ("Only the injected fixture coalescing duration differs"), so it is a valid control for the duration alone.

Only one outcome is informative:

  • Both methods fail in the same run: the 5 ms timer is not the cause.
  • 5 ms fails and zero passes: nothing follows. The zero method runs five tests later in alphabetical order, and the stall is intermittent. The original test passed in three earlier hosted runs (ffab46d, 25d2181, 65881b4) and failed in the last two (83e1264, f766923).

The f766923 evidence already points beyond the fixture timer:

  • wait returned at 1.6606 s is 0.66 s past the 1 s deadline. XCTest's own continuation was late as well, not just the coordinator's 5 ms sleep.
  • Mini /tmp/ios564-f766923-diagnostics/pro-native/ios/xcodebuild.log L857-L863 shows two publications through a real WCSession on tid 57475, the thread that also logs UIKit's scene warnings. They are at 05:27:44.277 and 05:27:44.293 ([WC] WCSession has not been activated, Snapshot context publish failed ... code=7004). The test's RecordingWatchSnapshotSender doesn't log these, so another coordinator using PhoneWatchConnectivityCoordinator.shared produced them.
  • XCTest's durations put the failing test at about 05:27:43.0–44.7. It took 1.669 s, and 11 later tests totaling 0.124 s ran before the suite failed at 05:27:44.825. So these publications landed about 1.25–1.3 s into the test, after the deadline and before the 1.66 s publication.
  • The trace records nothing between 3.28 ms and 1.66 s, and neither variant separates this other work.

Risk: a single green zero run invites switching the fixture to .zero. That timing change would hide the stall, which #564 forbids ("No deadline increase, skip, retry-for-green or product behavior change is used to conceal the failure").

Fix (test-only, same deadline, no sleeps or retries):

Instrumentation order and lifetime (verified)

  • @Published emits in willSet, so access emitted (3.16 ms) before committed (3.17 ms) is expected. The trace prints the emitted value, so the record is correct.
  • The sink creates the coalescing Task before the witness Task, and both go to the main actor in FIFO order. The witness entering at 3.28 ms shows the coalescing task had already started and was suspended in its sleep. Adding taskEntered to the same wait cannot add a failure mode, because the witness always runs before the publication.
  • Defers run sender.onPublish = nil, then accessSubscription.cancel(), then removePersistentDomain. withExtendedLifetime (L2481) keeps the coordinator alive until then. Unlike base, a publication that arrives after a timeout can no longer fulfill an expired expectation.
  • XCTAssertEqual(accessEvents, 1) is new and stricter. Every base assertion is unchanged: retention count, pausedAccountIDs, snapshot count, empty accounts, [false, false] forces, two configurations and their secrets. Each caller passes its own #function, so the defaults suites stay separate. 1e97206 changes only this file (+15/−5).
  • The direct control (L2484-L2506) publishes synchronously with the default 250 ms delay. Its pending task captures [weak self], and deinit cancels it after the synchronous test returns, so no late publication reaches the sender. It rules out fixture state and snapshot content. It says nothing about scheduling.

Earlier #562 repairs (re-verified at this head)

  • Google removal: git grep GoogleUsageMetricCatalog 1e97206 -- '*.swift' finds nothing, and no added line restores Google, Gemini or Antigravity. ProviderMetricCatalog is the old catalog minus the Google definitions, and its Codex and Cursor metrics and layoutCopyMetricIDs are identical. ProviderMetricTileContent.resolve passes .unavailableUsage reasons through unchanged, so the synthetic reason covers the same case as "Disabled".
  • Reset copy: the tests match ClaudeUsageResetInventoryView.swift#L110-L115 and redeemableGrant(at:). Both unusable-reason strings are still asserted, now through the selected grant.
  • Billing window hosting: it follows the pattern already in the file at L1034-L1040. The replacement session has its own window, and hostBillingWebView asserts webView.window. No timeouts changed: waitUntil is still 10 s, and the skip predates this PR.
  • Workflow: the two new steps are gated on workflow_dispatch && inputs.swiftpm_regressions, and account-menu comparison adds !inputs.swiftpm_regressions. No trigger, matrix, retry or timeout changed, and the automatic swiftlint job is unchanged.

Delivery state (read once, no polling)

  • Ruleset: the only required check is SwiftLint (strict). PR run 42966486984 at 1e97206 passed SwiftLint and skipped the other jobs. Merge state is CLEAN, with 0 review threads.

  • Manual run 42966503947 at 1e97206: SwiftLint, strict concurrency, watchOS tests and SwiftPM smoke tests passed. iOS tests were still in progress when I read it at about 05:45Z, so I claim no result for either watch variant.

  • PR body is stale:

    • The Validation section names 83e1264.
    • The checked DoD review box links the 83e1264 review.
    • The continuation section names f766923 and run 42965056884.

    Refresh it before merge. This is not a code defect.

Commands and limits

  • Laptop, read-only checkout (clean before and after): git diff 53e5383...1e97206, git diff f766923 1e97206, git log, git show 0eee783^:CodexBarIOS/Models/GoogleUsageMetricCatalog.swift, git grep at head, and source reads.
  • Laptop, disposable copy (git -c core.autocrlf=false archive 1e97206 into scratch):
    • python -m unittest on test_manual_notifier_permission_and_full_workflow_scope, test_removing_only_cost_mode_preserves_pinned_remaining_workflow and test_branch_dispatch_pins_only_cost_checkout_to_immutable_sha: 3 passed.
    • scripts.tests.test_ci_policy: 4 passed.
  • Mini, as HemSoft: read the PR, issues Remove stale Google catalog reference from metric tile tests #562 and Diagnose intermittent watch snapshot transition test timeout #564, reviews, comments, GraphQL review threads, main rules, and the runs for the head SHA. Read the retained f766923 xcodebuild.log without changing it.
  • Limits:
    • No Xcode, simulator, real Mac, live provider, auth, account, signing, install, release or CI dispatch.
    • I ran no Swift tests myself. Swift results come from hosted CI.
    • The timing above is inferred from log timestamps and XCTest durations. No instrumentation measured it.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

R8 diagnostic limitation accepted

R8 comment6788179991 is accepted: the zero-delay test is a duration-only control, not a separate scheduler path. One zero pass with one timed failure cannot establish causation for an intermittent stall. No such result will be treated as a fix. The f766923 wait itself returned after its deadline, and real shared WatchConnectivity sender logs are possible interference evidence, not a proved cause.

Run42966503947 is still executing iOS tests; all four other quality jobs passed. Preserve that actual outcome before another diagnostic dispatch. If the trace remains insufficient, add global-executor and main-queue witnesses to distinguish the unobserved interval, keeping all original deadlines/assertions intact. No source edit, duplicate run or new review started in response to R8.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Executor-witness diagnostic after the duration control

Run42966503947 at1e97206 completed FAILURE. Both watch transition cases passed:5ms published at12.22ms; zero published at3.91ms. This supplies no causal contrast and is not a fix. The ordinary iOS suite passed all1112 tests. Failure occurred later in native-commerce:22 of25 tests failed with StoreKit verification/purchase errors. This distinct unchanged commerce failure is retained, not bypassed or repaired under the test-only564 allocation. Four other quality jobs passed.

Artifacts retained on Mini: /tmp/ios564-1e97206-diagnostics and /tmp/ios564-1e97206-failed.log. R8 limitation accepted: duration-only passes do not identify a cause; earlier real shared WatchConnectivity publications are possible interference, not proof.

Head3b1628a64b734badef2e5d5c34108b2840ea1215 adds thread-safe immediate trace records for a detached global-executor entry, a main-queue callback, one synthetic5ms timer on the global executor, and its return to MainActor. The timer is a one-shot witness, not a wait/retry used to satisfy an assertion. The original one-second expectations,5ms case,zero-duration control and every snapshot/account/force assertion remain. No production source or automatic CI changes.

New evidence run https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42970337329 uses the existing manual runner. Root cause and safe test-only repair are still unestablished; neither a passing sample nor added probes close564. Fresh independent exact-head Laptop review pending. Same claim/owner; no merge or release.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 3b1628a64b734badef2e5d5c34108b2840ea1215 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex. The placement limit (Home or Laptop) rules out SFL and hosted Codex, and the fresh picker selected Opus 5.5, so this is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers the full 10-file diff, with a focus on the 1e97206→3b1628a delta (one file, +35/−7). This head is diagnostic work. It claims no fix for #564, and this review doesn't either.

Assessment: no P1 or P2. One P3: in the 3 ms–1.66 s gap, the new witnesses can't tell the main thread from the global executor. #564's root cause is not established, and merge stays blocked.

P3: A late global timer leaves the main thread unobserved, and the timer runs at a different priority

DashboardAndSettingsTests.swift#L2462-L2474

  • Three of the four witnesses fire before the gap. The main-queue block (L2462), the detached task's entry (L2464) and the main-actor witness (L2471-L2474) are all enqueued right after the commit at about 3 ms. In the retained traces, the main-actor witness entered at 3.28 ms (f766923) and 5.68 ms (1e97206, 5 ms case). That is before the coordinator's sleep ends (about 8.2 ms and 10.5 ms). The f766923 gap runs from 3.28 ms to 1.66 s, so these three witnesses fire at its start and say nothing about the rest.
  • The only main-thread sample in the gap waits on the global timer. It is the MainActor.run at L2468, which runs only after the detached 5 ms sleep at L2466 returns. If that timer is late, the trace has no main-thread sample in the gap, so a stalled global executor and a stalled process look the same.
  • The witness timer doesn't run at the coordinator's priority. The coordinator's coalescing work is Task { @MainActor [weak self] in … } (WatchSnapshotCoordinator.swift#L460-L475). The access sink creates it synchronously inside applyPlanAccess at L2460, so it inherits the test task's priority. Task.detached doesn't: per SE-0304, a detached task "does not inherit priority, task-local values, or the actor context". Unless the test task happens to run at the detached default, an on-time witness doesn't clear the coordinator's sleep, and a late one doesn't implicate it.

Risk: the next failing sample may still not show which executor stalled, and an on-time witness at the other priority could be misread as "timer path healthy". This is the R8 P3 reading risk, narrowed. It is not a product defect.

Fix (test-only, same 1 s deadline and assertions, nothing awaited, no retries):

  • Capture let priority = Task.currentPriority before L2463, pass it to Task.detached(priority:), and record it in the trace.
  • Add a main-thread timer that doesn't depend on the Swift global executor, for example DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { record("main-queue timer returned") }.
  • Optional: record one wall-clock Date() when the trace starts, so the relative times line up with the timestamped [WC] lines that R8 cited. Also record "cancelled" in the catch at L2466, so a missing timer event can't be confused with a test that finished first.

Reading one sample at this head

Trace before wait returned Supports Doesn't rule out
Timer and main hop on time, publication late Stall specific to the coordinator task A stall at the coordinator's priority only (P3)
Timer on time, main hop late Main thread blocked after the timer —
Timer late or missing Global timer or pool late at the witness's priority A main-thread or whole-process stall
Any passing sample Nothing causal —

In a passing zero-delay sample, the timer events will usually be missing. The test finishes at about 4.6 ms, and the defer at L2470 cancels the 5 ms sleep, which then returns without recording anything. That absence is not a stall.

Verified in the delta

  • Sendability: in WatchTransitionTrace, label and started are immutable lets. events is touched only under the NSLock, and summary takes the same lock, so the post-wait read can't race a late global record. @unchecked Sendable is justified. record is @Sendable and captures only trace. At this head, Strict concurrency passed in run 42970337329. It builds for testing with SWIFT_STRICT_CONCURRENCY=complete, and check-strict-concurrency.sh#L30-L38 fails on any warning or error in DashboardAndSettingsTests.swift.
  • Timestamps: each one is taken before the lock (L4653), so lock waits don't skew it. Across threads, list order can differ from time order by microseconds, so read entries by timestamp.
  • Lifetimes:
    • Defers unwind in this order: executorWitness.cancel(), then onPublish = nil, then the subscription cancel, then the defaults removal. withExtendedLifetime(coordinator) holds the coordinator through all of them.
    • The main-queue block is queued ahead of the awaited main-actor witness, so it always runs before the wait returns.
    • The detached task holds only record and trace, and no expectation. A record after the test can't fulfill or fail anything; it only prints with its case label.
  • Perturbation: the head adds one main-queue block, one detached task with one 5 ms timer, and one main-actor hop, none of them awaited. Each event now prints immediately (L4657), under the lock, on the main thread inside the window. Before, there was one print after the wait. Timestamps are unaffected, but a blocked stdout would delay the next probe step. I didn't measure this.
  • Deadline and assertions: timeout: 1 is unchanged (L2475). Every assertion at L2479-L2485 is unchanged from 1e97206 and base, and the stricter accessEvents == 1 (L2477) remains. Both cases still share the helper (L2417, L2422). The direct control (L2490-L2511) is unchanged since 1e97206. WatchSnapshotCoordinator.swift is byte-identical to base.
  • Supersession: the trace observes only $accountAccess, but the coordinator reschedules from eight store and refresh publishers (L402-L425); the entitlement stream at L427 is absent in this fixture. In this fixture, applyPlanAccess writes only accountAccess, synchronously (ProviderConfigurationStore.swift#L2709-L2805). The store has no NotificationCenter, Timer or sink observers, and the refresh service has no providers. So a reschedule from another publisher is unlikely here.

Earlier #562 work (re-verified at this head)

  • Google removal: git grep GoogleUsageMetricCatalog 3b1628a finds nothing. Base had 6 references in 3 test files. ProviderMetricCatalog.metrics and layoutCopyMetricIDs exist. Only two non-test paths changed, ci.yml and CHANGELOG.md; no production, Package.swift or project file changed.
  • Workflow pins: ci.yml and scripts/ are unchanged since ffab46d. In a disposable git archive 3b1628a copy, 16 Python tests passed: release shards (10), the two cost-control pin tests, and test_ci_policy (4).
  • Manual wiring: both SwiftPM steps require workflow_dispatch && inputs.swiftpm_regressions (L254-L260) and sit in the dispatch-only smoke job. Account-menu comparison adds !inputs.swiftpm_regressions (L524). The swiftlint job, triggers, matrices, retries and timeouts are unchanged.
  • Reset copy: the tests match ClaudeUsageResetInventoryView.swift#L110-L115.
  • Billing hosting: hostBillingWebView (L886-L897) asserts webView.window for all three hosted views. It is unchanged since 83e1264.

Delivery state (read once, no polling)

  • Ruleset: the only required check is SwiftLint (strict). PR run 42970312748 at 3b1628a passed SwiftLint and skipped the other jobs. Merge state is CLEAN, with 0 review threads.
  • Manual run 42970337329 at 3b1628a: SwiftLint, strict concurrency, watchOS tests and SwiftPM smoke tests passed. The smoke job ran 13 metric tile tests and 20 Claude reset tests (9+5+4+2), with 0 failures. iOS tests were still in progress when I read it, so I claim no result for the watch cases, the ordinary suite or native-commerce.
  • Native-commerce: the 22-of-25 StoreKit failure from run 42966503947 at 1e97206 is still a red result. This review doesn't assess or excuse it.
  • PR body: it still lists run 42970337329 and this review as pending, so it needs a refresh once both land. This is not a code defect.

Commands and limits

  • Laptop, read-only checkout (clean before and after): git diff 53e5383...3b1628a, git diff 1e97206 3b1628a, git diff ffab46d 3b1628a -- .github scripts, git grep at both revisions, and source reads.
  • Laptop, disposable copy (git -c core.autocrlf=false archive 3b1628a into scratch): python -I runner on scripts.tests.test_ui_release_shards, the two CostControls pin tests and scripts.tests.test_ci_policy. 16 passed.
  • Mini, as HemSoft (checked with gh api user): read the PR, Remove stale Google catalog reference from metric tile tests #562, Diagnose intermittent watch snapshot transition test timeout #564, comments, reviews, GraphQL review threads, main rules, and the check runs and runs for the head SHA. Read the smoke job log once, saved at /tmp/review-563-3b1628a-smoke.log. Grepped the retained f766923 and 1e97206 xcodebuild.log files without changing them.
  • Docs: SE-0304, for Task.detached priority inheritance.
  • Limits:
    • No Xcode, simulator, real Mac, live provider, auth, account, signing, install, release, App Store Connect, pricing or CI dispatch.
    • I ran no Swift tests myself. Swift results come from hosted CI.
    • I didn't observe the test task's actual priority. The P3 rests on documented inheritance rules, not a measured mismatch.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

R9 probe correction, awaiting existing run

R9 P3 is addressed in e4ee11e: the detached witness now uses Task.currentPriority, and an independent DispatchQueue.main.asyncAfter 5ms sample no longer depends on that global timer. The trace includes a wall-clock anchor, priority and timer cancellation. Records are buffered under the existing lock and printed after the original expectation returns, reducing output in the measured interval. No deadline/assertion/production change; no established564 repair.

Run42970337329 at preceding head3b1628a remains in progress with iOS pending and the other four quality jobs passed. Preserve its actual completion evidence before dispatching the corrected diagnostic. No replacement run or review started yet. Fresh exact-head hosted qualification and independent review will be required for e4ee11e; R9 is earlier-head provenance only. Same Laptop claim/owner retained.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Preserved prior outcome; corrected probes now qualifying

Run42970337329 at3b1628a completed FAILURE in native-commerce StoreKitProSubscriptionTests.testBillingGraceMapsVerifiedStatusAndKeepsAccess. Ordinary iOS and both watch cases passed. The5ms case published at74.79ms; zero at37.96ms. These passing samples do not establish cause or repair564. Prior R9 limitations remain: witnesses had unmatched priority and no independent delayed main-queue sample. Logs/artifacts retained at /tmp/ios564-3b1628a-failed.log and /tmp/ios564-3b1628a-diagnostics.

Currenthead e4ee11e fixes that probe design only: inherited test priority, independent main-queue5ms timer, buffered trace, wall anchor and cancellation record. Newactualrun https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42974788943 now qualifies that changed head and gathers the corrected evidence. This is not an unchanged rerun for green. Same original1s deadline and assertions. Native-commerce behavior remains unmodified and its failures cannot be bypassed.

Fresh independent Laptop exact-head review pending. Same claim/owner; no merge, release or established564 fix.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head e4ee11e98973d07370c2e1d6230dc7f5caaa71ba against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex. The placement limit rules out SFL and hosted Codex, and the fresh picker selected Opus 5.5, so this is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers the full 10-file diff, with a focus on the 3b1628a→e4ee11e delta (one file, +11/−4). This head is diagnostic work. It claims no fix for #564, and this review doesn't either.

Assessment: no P1 or P2. One P3: the new cancellation record never reaches the output. The R9 P3 is addressed. A failing sample at this head can now tell a blocked main thread from a late cooperative-pool timer and from a delay specific to the coordinator. #564's root cause is not established, the exact-head run is still in progress, and merge stays blocked.

P3: The cancellation record is written after the trace is printed

DashboardAndSettingsTests.swift#L2470-L2485

  • The catch at L2470-L2473 records global-executor timer cancelled. The only thing that cancels the detached task is defer { executorWitness.cancel() } at L2477. A detached task doesn't inherit cancellation, and nothing else cancels it.
  • That defer runs when the helper returns. The trace is last read at L2484 (the print) and L2485 (the assertion message), and both run before any defer.
  • So the cancellation record always goes into a trace that is never read again. The same is true of any probe that fires after wait returned, such as the main-queue timer in a zero-delay pass that ends in about 4 ms.
  • Comments 6788735081 and 6789114618 say the trace includes the timer cancellation. It never will.

Impact: low. A failing sample is unaffected, because cancellation can only happen after the print. "Timer not due yet" and "timer late" can still be told apart by comparing wait returned with the 5 ms due time.

Fix (test-only): remove the cancellation record and correct the two claims, or note in #564 that a missing timer is read against the wait returned timestamp. Don't await the detached task before printing. In the failure that matters most, a starved cooperative pool, that await would block for the whole stall.

R9 fix (verified)

  • Main-thread timer: DispatchQueue.main.asyncAfter at L2464-L2466 fires 5 ms after the commit without using the Swift cooperative pool. On Darwin, MainActor jobs run through the same main queue. So a late timer means the main thread, or the whole process, was busy.
  • Priority: L2467 passes Task.currentPriority to Task.detached(priority:), and L2448 records it.
    • The coordinator's Task { @MainActor … } (WatchSnapshotCoordinator.swift#L462) is created synchronously inside applyPlanAccess. It inherits the test task's base priority, and a userInteractive base drops to userInitiated (Task.cpp#L1718-L1731).
    • Task.currentPriority returns the stored priority, which includes any escalation (Task.swift#L1051-L1053, Task.cpp#L1476-L1480).
    • XCTest calls an async test through its Objective-C completion-handler thunk, which starts a plain Task(operation:) (Task.swift#L1499-L1501). Called from the main thread, that task's base should be userInitiated (25).
    • So the priorities match when the trace logs 25 and the test task wasn't escalated. A logged 33 shows a mismatch. Escalation wouldn't show. Optional: also record Task.basePriority.
  • Wall anchor: L2448 records Date().timeIntervalSince1970 at the start of the trace, so relative times line up with timestamped log lines.
  • Buffered output: record (L4660-L4665) now only appends under the lock, and the single print at L2484 runs after the 1 s wait. The retained 3b1628a log shows why this matters:
    • In the zero case, global-executor timer returned is stamped 8.47 ms but printed after the 37.77 ms main-actor witness entered line. The global thread stamped its entry and then couldn't print it until after that line.
    • Most likely the main thread held the trace lock from its 2.99 ms main-queue record, since the only slow step inside the lock was that record's print. The 5 ms case shows the same main-thread gap, from 3.45 ms to 74.6 ms.
    • So those two passes measured the probe as well as the coordinator. Their 74.79 ms and 37.96 ms publication times aren't comparable with 12.22 ms and 3.91 ms at 1e97206.
    • f766923 and 1e97206 printed once after the wait, so this doesn't explain the original 1.66 s failure. A main-thread write stall is one candidate the new main-queue timer can expose. It is not a proven cause.
    • In the 3b1628a 5 ms case, the default-priority witness timer never recorded before cancellation (after 75 ms), even though the coordinator had published by 74.79 ms. That fits R9's priority concern. It is one passing sample and shows nothing causal.

Reading one failing sample at this head

"On time" means within a few ms of commit + 5 ms. A probe missing before wait returned was late by at least the whole wait.

Main-queue timer Global timer, then main hop Publication Points to
On time On time, on time Late Something specific to the coordinator task (or its priority, see above)
Late On time, late Late Main thread blocked
On time Late or missing Late Cooperative pool or timer late at the test priority
Late Late or missing Late Whole-process stall
Any Any On time Nothing causal

Lifetimes and sendability (inspection)

  • WatchTransitionTrace: label is now internal and is still an immutable let, as is started. events is touched only under the NSLock, so @unchecked Sendable still holds.
  • record is @Sendable and captures only trace. The main-queue block, the asyncAfter block and the detached task capture only record. None of them holds an expectation, the store, the sender or the coordinator.
  • A probe that fires after the helper returns, like the zero-case timer or a pending MainActor.run, appends to an unread trace. It costs one lock and one string append on the main thread during a later test.
  • Defers unwind in this order: executorWitness.cancel(), sender.onPublish = nil, subscription cancel, defaults removal. withExtendedLifetime(coordinator) (L2494) covers them.
  • Nothing new is awaited. The only wait is still fulfillment(of: [taskEntered, snapshotPublished], timeout: 1) at L2482.
  • Strict concurrency at this head was still running when I read the run, so this section is inspection only. 3b1628a passed with the same capture pattern. The new pieces are one asyncAfter block and an explicit detached priority.

Earlier work (re-verified at this head)

  • Scope: only DashboardAndSettingsTests.swift changed since 83e1264 (R6). Production code, Package.swift and the Xcode project match the base exactly, and so does WatchSnapshotCoordinator.swift.
  • Diagnose intermittent watch snapshot transition test timeout #564 fixture:
    • timeout: 1 is unchanged at L2482.
    • The assertions at L2487-L2493 match the base, and the stricter accessEvents == 1 check is still at L2485.
    • Both cases share the helper with separate suites (L2417, L2422).
    • The direct control (L2497-L2519) is unchanged since 1e97206.
  • Remove stale Google catalog reference from metric tile tests #562:
    • git grep GoogleUsageMetricCatalog e4ee11e -- '*.swift' finds nothing.
    • The other changed test files are unchanged since 1e97206, and ci.yml and scripts/ are unchanged since ffab46d.
    • In a disposable archive, 16 Python tests passed: 10 release shards, 2 cost pins and 4 CI policy.
  • Exact-head SwiftPM: the smoke job in run 42974788943 passed. swift test --filter MetricTileContentRegressionTests ran 13 tests with 0 failures, and --filter ClaudeUsageReset ran 20 (9+5+4+2) with 0 failures.
  • Native-commerce: no StoreKit test or production file is in the diff.
    • It passed in run 42955812990 at 25d2181.
    • It failed 22 of 25 at 1e97206 and 1 of 25 at 3b1628a. The one failure was testBillingGraceMapsVerifiedStatusAndKeepsAccess: "Real StoreKit billing grace did not reach the entitlement service."
    • This review doesn't assess or excuse it. It remains a red result.

Delivery state (read once at about 06:53Z, no polling)

  • Ruleset: the only required check is SwiftLint (strict). PR run 42972013382 at e4ee11e passed SwiftLint and skipped the other jobs. Merge state is CLEAN, with 0 review threads and no formal review at this head. GitHub would allow a merge, so the blockers are Diagnose intermittent watch snapshot transition test timeout #564 and the unchecked DoD items, not a GitHub control.

  • Manual run 42974788943 at e4ee11e: SwiftPM smoke passed and strict concurrency was in progress. SwiftLint, watchOS tests and iOS tests were queued. I claim no result for the watch cases, the ordinary suite or native-commerce.

  • PR body: mostly current, with two stale points (not code defects):

    • "Previous run 42966503947" is the 1e97206 run. The run for the head just before this one, 3b1628a (42970337329), appears only in a trailing paragraph after Refs #564.
    • Its 3b1628a timings carry the probe stall described above.

    Refresh it when the run lands. The body says 15 Python checks passed; this review ran 16 across the same three modules.

Commands and limits

  • Laptop, read-only checkout (clean before and after, HEAD e4ee11e): git diff 53e5383..e4ee11e, git diff 3b1628a e4ee11e, git diff --stat 83e1264 e4ee11e, git diff --quiet against base, ffab46d and 1e97206, git grep, git show of the print placement at f766923 and 1e97206, and source reads.
  • Laptop, disposable copy (git -c core.autocrlf=false archive e4ee11e into scratch): ran scripts.tests.test_ui_release_shards, the two CostControls pin tests and scripts.tests.test_ci_policy through a python -I runner on Python 3.14.7. 16 passed.
  • Mini, as HemSoft (checked with gh api user): read the PR, comments, GraphQL threads and reviews, Remove stale Google catalog reference from metric tile tests #562, Diagnose intermittent watch snapshot transition test timeout #564, the main rules and the e4ee11e check runs. Read run 42974788943 once and saved its smoke job log to /tmp/review-563-e4ee11e-smoke.log. Read the retained /tmp/ios564-3b1628a-diagnostics iOS and native-commerce logs without changing them.
  • Docs: swiftlang/swift at d0819aa: Task.cpp (task-creation priority, swift_task_currentPriority) and Task.swift (currentPriority, basePriority, _runTaskForBridgedAsyncMethod). I didn't match the runner's toolchain version against this commit.
  • Limits:
    • No Xcode, simulator, real Mac, live provider, auth, account, signing, install, release, App Store Connect, pricing or CI dispatch.
    • I ran no Swift tests myself.
    • The priority match and the 3b1628a lock-wait reading are inferred from source and log order. Nothing measured them.
    • I didn't wait for or poll run 42974788943.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

R10 trace limitation accepted

R10 comment6789390070 is accepted. The timer-cancellation record cannot appear in the printed trace because cancellation occurs in teardown after printing. Earlier descriptions in comments6788735081 and6789114618 overstated that output. Cancellation is not an available diagnostic signal at this head and is not used in any cause inference. Remove that unused record during diagnostic cleanup; do not await the witness before printing.

The per-line printing in3b1628a also perturbed main-thread timing. Its74.79ms and37.96ms values are probe-affected passing samples, not evidence of production latency or a causal comparison. The earlier f7669231.66s failure used one print after the wait and remains original failure evidence. Current e4ee11e buffers records until after the unchanged wait.

Run42974788943 remains in progress with only iOS pending; the other four jobs passed. Preserve its actual completion before another source revision or hosted diagnostic. No duplicate run/review or readiness claim.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Diagnostic retention repaired after cancellation

Run 42974788943 at e4ee11e was cancelled during native-germany after the ordinary iOS phase. Diagnostics upload then failed with ENOENT on the in-progress xcresult Staging testmanagerd.log. No iOS artifact was published. The compact corrected watch trace is therefore unavailable; no exact-head watch pass, cause or repair is inferred. Other four quality jobs passed. Retained Mini job log: /tmp/ios564-e4ee11e-ios-job.log.

Head 2f09173 repairs this evidence path only: the existing manual runner emits bounded synthetic ios564 trace lines immediately after ordinary iOS xcodebuild returns, before later commerce phases and artifact upload. Full test/gate execution, deadlines, cancellation, failure handling and automatic workloads remain intact. Three counter-controls prove later failure cannot erase emitted output, unrelated log lines are excluded, and oversize/missing diagnostics are rejected. All 54 affected manual-runner, CI-policy and shard Python tests passed on Mini. R10's unobservable cancellation record is removed; nothing waits for the witness before printing.

Current diagnostic run: https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42983035068. Fresh independent Laptop review pending. The underlying #564 cause and any safe test-only synchronization repair remain unestablished. No deadline increase, skip, relaxed assertion, protected commerce modification or unchanged retry-for-green.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 2f09173570e95fa083a9113a6f9a39b49ef33cf1 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex. The placement limit (Home or Laptop) rules out SFL and hosted Codex, and the fresh picker selected Opus 5.5, so this is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers the full 12-file diff, with a focus on the e4ee11e→2f09173 delta (3 files, +48/−4). This head is diagnostic work for #564. It claims no fix, and this review doesn't either.

Assessment: no P1 or P2. One P3: no test runs the new trace hook inside Runner.phase, so the three new tests can't show what the PR claims for it. R10's P3 is fixed. #564's root cause is not established, the exact-head iOS tests job was still running when I read it, and merge stays blocked.

P3: The trace hook in Runner.phase has no test

pro_release_runner.py#L608-L613 is what keeps the trace when later phases fail. It runs only for ios, right after xcodebuild returns, and failure = failure or PhaseFailure(…) keeps an earlier xcodebuild exit. No test reaches that block.

  • The three new tests (test_pro_release_validation.py#L43-L66) call emit_watch_transition_trace directly. In the first one, the "later phase failure" is a RuntimeError the test raises after the helper returns. That only shows a Python exception doesn't take back text already written to a StringIO. It doesn't run a later phase or touch Runner.phase.
  • No test calls phase('ios', …). The existing RunnerCommandFailureTests (L185-L208) drives the real phase, command and receipt flow with only process calls mocked, but only for native and UI phases.
  • Mutation check in disposable copies on Mini:
    • M1 deletes the six-line hook.
    • M2 changes failure = failure or PhaseFailure(…) to failure = PhaseFailure(…), so a trace error would replace an xcodebuild exit 65 with exit 1.
    • Both mutants pass all 54 tests in test_pro_release_validation, test_ui_release_shards and test_ci_policy.

Impact: low. The code at this head is correct by inspection, and this is a manual-only diagnostic. But comment 6790409873 says the counter-controls "prove later failure cannot erase emitted output" and that failure handling stays intact. Both rest on inspection, not on these tests. A later edit that drops the hook, or lets a trace error override exit 65, would still pass the manual fixture step.

Fix (test-only, in the manual-only module):

  • Add a RunnerCommandFailureTests case that runs phase('ios', …) on an ordinary build. Follow the pattern at L241-L252, with discovery_tests patched to return the manifest's shared_pro_tests.
  • Have the mocked xcodebuild write one [ios564-transition …] line and return 65. Assert that:
    • stdout has the trace line and the count line;
    • the phase raises with code 65;
    • the receipt has exit 65 and no watchTransitionTraceError.
  • In a second case, write nine trace lines and return 0. Assert exit 1 and a recorded watchTransitionTraceError.

This module runs only in the dispatch-only iOS tests job, so automatic CI is unaffected.

Runner change (verified)

  • Placement: the hook runs after receipt['ended'] (L607), so it doesn't widen the result-timestamp window that validate_results checks (L625). It runs before result validation, attachment export and every later native phase. command() creates xcodebuild.log with mode 'x' before starting xcodebuild (L45-L48), so the log exists whenever xcodebuild ran.
  • Retention: run-pro-release-validation.sh execs the runner, so flushed stdout goes straight into the Run iOS unit tests step log. The retained e4ee11e job log (/tmp/ios564-e4ee11e-ios-job.log) shows that step's output up to the cancellation, so a cancelled job keeps lines already printed. In that run, the hook point (ordinary xcodebuild returned at 07:18:13) came 29 minutes before the cancel.
  • Error preservation: failure or … keeps an earlier xcodebuild failure and its code (inspection only; see P3). The trace error goes into receipt['watchTransitionTraceError'].
  • Bounds: at most 8 lines of at most 8192 characters, so about 64 KiB. Each check runs before its line is printed. A 9th match or an oversized line raises after the earlier lines are out. Lines that don't start with [ios564-transition are never printed.
  • One gate nuance: a trace error now fails an otherwise-passing ordinary phase with exit 1. That fails closed, so it can't bypass anything, and it isn't reachable at this head:
    • The two cases print one line each.
    • The last labeled-format log (1e97206) had 2 matches at column 0, the longest 355 characters.
    • All six retained ordinary xcodebuild.log files (ffab46d through 3b1628a) decode as strict UTF-8, and none has a line over 1100 bytes.
    • observer_runner_records (L327-L331) already reads xcodebuild.log with the same strict decoding.
  • Prefix match: the head prints [ios564-transition delay=… seconds] … (DashboardAndSettingsTests.swift L2481), and that labeled form matched at 1e97206. The f766923 failure line used the older unlabeled [ios564-transition] form, which this prefix wouldn't match.
  • Content: the lines carry only time offsets, a wall-clock anchor, the task priority, booleans and snapshot counts. No account or secret data. They can't start with ::, so Actions won't read them as workflow commands.
  • Automatic CI: the swiftlint job still runs only test_ci_policy.py. test_pro_release_validation runs only in the dispatch-only iOS tests job and in the FUNCTION-RISK.md commands.

#564 probes (re-verified)

  • R10 is fixed: at L2467-L2474, the catch now just returns, so nothing records a cancellation that could never be printed. Nothing awaits the witness before the print at L2481.

  • Unchanged since R10:

    • timeout: 1 (L2479);
    • the base assertions (L2484-L2490) and the stricter accessEvents == 1 (L2482);
    • both cases through one helper with separate suites (L2416-L2423);
    • the direct control (L2494-L2516).

    Records are still buffered under the lock and printed once, after the wait.

  • R10's table for reading one failing sample still applies. A probe that fires after wait returned is still never printed (for example, both timers in a zero-delay pass that publishes in about 4 ms). That doesn't affect a failing sample.

#562 and earlier fixes (re-verified at this head)

  • git grep GoogleUsageMetricCatalog finds nothing at head; base has 6 matches. Compared with base, no production file, Package.swift, Xcode project or WatchSnapshotCoordinator.swift changed.
  • ci.yml, both workflow pin tests, the cost test and CHANGELOG.md are unchanged since ffab46d. Earlier reviews verified their content.
  • Since 83e1264, only the three Diagnose intermittent watch snapshot transition test timeout #564 files changed, so the reset-copy and billing-window fixes are as previously reviewed.
  • Exact-head SwiftPM (smoke job):
    • --filter MetricTileContentRegressionTests ran 13 tests with 0 failures.
    • --filter ClaudeUsageReset ran 20 (9+5+4+2) with 0 failures.

Delivery state (read once at 08:03Z; no polling)

  • Ruleset: the only required check is SwiftLint (strict), which passed at this head in PR run 42983000561. There's no branch protection, 0 review threads and no formal review at this head.
  • Manual run 42983035068 at this head: SwiftLint, strict concurrency, watchOS tests and SwiftPM smoke passed, and iOS tests was in progress. I claim no result for the watch cases, the ordinary suite or native-commerce.
  • Why e4ee11e was cancelled: run 42974788943's iOS tests annotation says "The job has exceeded the maximum execution time of 45m0s". It hit the job's timeout-minutes: 45; nobody cancelled it.
    • The job started at 07:01:41 and was still in native-germany when it was cut off at 07:47:05.
    • The one earlier iOS job that ran every phase (42955812990, success) took 35m39s.
    • The PR correctly doesn't touch the timeout. Comment 6790409873 says only that the run was cancelled during native-germany.
    • If the current run times out the same way, the new hook still keeps the trace.
  • PR body: still lists this review as pending and doesn't give the e4ee11e cancellation cause. The parent owns that refresh; I don't count it as a code defect. Native-commerce remains a separate red result, which this review doesn't assess or excuse.

Commands and limits

  • Laptop, read-only checkout (clean before and after, HEAD 2f09173): git diff base...head (all 12 files), git diff e4ee11e 2f09173, git diff --stat 83e1264 2f09173, git diff --quiet against base and ffab46d, git grep, and source reads.
  • Mini, as HemSoft (checked with gh api user):
  • Mini, disposable copy (git -c core.autocrlf=false archive 2f09173, extracted to /tmp/review-563-2f09173-1LaNCG, Python 3.14.4):
    • python3 -m unittest scripts.tests.test_pro_release_validation scripts.tests.test_ui_release_shards scripts.tests.test_ci_policy plus the two CostControls pin tests: 56 passed (/tmp/review-563-2f09173-python.log).
    • Mutants M1 and M2, each in its own copy: 54 of 54 passed each (/tmp/review-563-2f09173-M1.log, /tmp/review-563-2f09173-M2.log).
  • Limits:
    • No Xcode, simulator, real Mac, live provider, auth, account, signing, install, release, App Store Connect, pricing or CI dispatch.
    • I ran no Swift tests.
    • I didn't wait for or poll the iOS tests job, so I have no exact-head trace.
    • That the step log keeps the trace through a cancellation comes from the e4ee11e log, not from a run at this head.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

R11 integration finding addressed with mutation proof

Commit06aab1b043ae1eb4ed37fb626f3f1b92d29b0540 adds two RunnerCommandFailureTests that execute phase('ios') with OS process calls mocked. They assert trace output before result inspection/later failure and preserve xcodebuild65 when trace emission fails. On Mini the full affected Python suite passes. Deleting the phase hook makes the first new test fail; changing its error precedence to overwrite exit65 makes the second fail. Exact-head source, both mutations and logs retained at /tmp/ios564-06aab1b-proof-4m613gg5. This corrects the earlier helper-only proof limitation.

Run42983035068 at preceding2f09173 remains the registered hosted attempt. Preserve its actual completion before a new dispatch. Currenthead changes only test coverage. Fresh independent exact-head Laptop review is requested; no current-head hosted pass is claimed.

The e4ee11e job reached its existing45-minute limit, then upload failed on the changingxcresult stagingfile. That was timeout cancellation, not a human cancellation. No deadline increase, automaticwork change, skippedgate or retry-forgreen.

Issue564 cause and repair remain unestablished. Sameowner/claim, no merge/release.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 06aab1b043ae1eb4ed37fb626f3f1b92d29b0540 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex. The placement limit (Home or Laptop) rules out SFL and hosted Codex, and the fresh picker selected Opus 5.5, so this is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers the full 12-file diff, with a focus on the 2f09173→06aab1b delta (1 file, +35/−1). This head is diagnostic work for #564. It claims no fix, and this review doesn't either.

Assessment: no P1 or P2. One P3: a test gap. R11's P3 is fixed for both seams it named: hook removal and exit-65 masking. #564's root cause is not established. No hosted run has qualified this head, and merge stays blocked.

P3: No test covers a trace error failing an ordinary phase that otherwise passed

At pro_release_runner.py#L608-L613, suppose xcodebuild passes but the emitter raises: a 9th matching line, a line over 8192 characters, or a missing or undecodable log. Then failure = failure or PhaseFailure(…) fails the ios phase with exit 1. Neither new test reaches that branch:

  • L260-L270 uses a valid trace and exit 0.
  • L272-L282 uses an oversized trace and exit 65, so a primary failure already exists.

Proof: mutant M3 replaces L613 with pass. The error is still written to the receipt, but the phase no longer fails. All 57 tests pass. R11 recommended this exact case ("write nine trace lines and return 0. Assert exit 1 and a recorded watchTransitionTraceError"). 06aab1b added the oversized + 65 case instead.

Impact: low. The code at this head is correct by inspection, and stdout stays bounded under M3 because the emitter raises before printing a 9th line. What's unguarded is the fail-closed outcome. A later edit could let a missing, unreadable or over-bound diagnostic pass the ordinary iOS phase, and the error would show only in receipt.json.

Fix (test-only, in the manual-only module): add a third RunnerCommandFailureTests case.

  • Set self.ordinary_trace = '[ios564-transition fixture] event\n' * 9 and keep exit 0.
  • Assert PhaseFailure with code 1, receipt exit 1, diagnostic bound in watchTransitionTraceError, and exactly one -resultBundlePath call.
  • Re-run M3; it should now fail.

06aab1b integration tests (verified)

  • Real phase path: both tests call the real Runner.phase('ios', …). Only subprocess.run, device_record and unchanged are patched (setUp L203-L205). The tests leave discovery_tests, validate_results and validate_receipt unpatched, so the ordinary discovery gate (runner L585-L589) and result validation actually run.
  • Fixture scope: the fixture returns shared_pro_tests only for unfiltered CodexBarIOS discovery (L289-L290) and serves results for phase ios (L307). Mutant M8 inverts the shared-Pro discovery gate, and both new tests then error. The gate is live, not bypassed.
  • First test: asserts receipt exit 0 inside the block, the trace on stdout, and the trace printed before summary: (result inspection). An AssertionError or PhaseFailure would escape assertRaisesRegex(RuntimeError), so neither can be hidden. The "later failure" is still a synthetic RuntimeError. What this test actually proves is that the line reaches stdout during the phase, before it returns.
  • Second test: asserts exit 65 is kept, the receipt has exit 65, watchTransitionTraceError is recorded, and xcodebuild runs exactly one test invocation (no retry).
  • Fixture default: setUp leaves self.ordinary_trace unset. A future ios-phase test that omits it raises AttributeError instead of passing.

Each mutant ran in its own copy of the exact head, under the full 57-test command:

Mutant Edit Result
M1 delete the 6-line hook new test 1 FAIL, new test 2 ERROR
M2 failure = PhaseFailure(…) (trace error overwrites 65) new test 2 FAIL
M3 trace error no longer fails the phase 57/57 pass (the P3)
M4 hook moved after result inspection new test 1 FAIL
M5 trace printed to stderr new test 1 and helper test FAIL
M6 hook only when xcodebuild failed new test 1 FAIL
M7 hook only when xcodebuild passed new test 2 ERROR
M8 invert the ios shared-Pro discovery gate both new tests ERROR
M9 line bound 8192 → 9000 new test 2 ERROR, helper FAIL
M10 count bound 8 → 9 helper FAIL

Parent proof (/tmp/ios564-06aab1b-proof-4m613gg5): source/scripts matches my exact-head copy apart from __pycache__. Its two mutant diffs are exactly M1 and M2, and its logs show the expected targeted failures. My full-suite runs reproduce both.

Unchanged since R11 (re-checked at this head)

  • Delta scope: git diff --stat 2f09173 06aab1b touches only scripts/tests/test_pro_release_validation.py. The runner hook, workflow, CHANGELOG and Swift tests are as R11 reviewed them.
  • Watch case: DashboardAndSettingsTests.swift#L2479-L2490 keeps timeout: 1. The seven post-wait assertions are textually identical to base L2438-L2444, and the stricter accessEvents == 1 remains. There is no skip, retry or deadline change.
  • Remove stale Google catalog reference from metric tile tests #562:
    • git grep GoogleUsageMetricCatalog finds 0 matches at head and 6 at base.
    • Compared with base, no production source, Package.swift or Xcode project changed.
    • The reset-copy test follows the precedence in the unchanged ClaudeUsageResetInventoryView.swift (L110-L115).
  • Automatic CI: the swiftlint job still runs only test_ci_policy.py. The new Python tests run only in the dispatch-only iOS tests job.

Delivery state (read once at about 08:14Z; no polling)

  • Ruleset: the only required check is SwiftLint (strict), which passed at 06aab1b in PR run 42984807250. Other jobs were skipped on pull_request. There is no formal review at this head and there are 0 review threads.
  • Manual run 42983035068 at the preceding 2f09173: SwiftLint, strict concurrency, watchOS tests and SwiftPM smoke passed. iOS tests has been in progress since 07:59:05Z. I claim no result for it. No hosted run exists at 06aab1b, so this head needs fresh qualification later.
  • PR body: still names 2f09173 as the current diagnostic head and cites "all 54 affected Python tests". That refresh is the parent's to make; I don't count it as a code defect. The native-commerce red results are separate, and this review doesn't assess or excuse them.

Commands and limits

  • Laptop, read-only shared checkout (clean before and after, HEAD 06aab1b): git diff base...head (12 files), git diff 2f09173 06aab1b, git grep and git show source reads.
  • Mini, as HemSoft (checked with gh api user): read the PR, all comments, reviews, GraphQL threads, Remove stale Google catalog reference from metric tile tests #562, Diagnose intermittent watch snapshot transition test timeout #564, the main rules, the head check runs and run 42983035068, once each.
  • Mini, disposable copies:
    • git -c core.autocrlf=false archive of head into /tmp/review-563-06aab1b-FhqXcZ/src. The runner and test file SHA-256 values match the git blobs. Python 3.14.4.
    • python3 -m unittest scripts.tests.test_pro_release_validation scripts.tests.test_ui_release_shards scripts.tests.test_ci_policy scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow: 57 OK. At base, the three modules give 51 OK.
    • M1–M10 via mutate.py in that directory, one log per mutant.
    • The full manual cost module gives 16 tests with 5 sw_vers errors at both head and base. This is a Linux limitation, not a pass.
  • Limits:
    • No Xcode, Swift tests, simulator, real Mac, auth, provider, account, signing, install, release, App Store Connect, pricing or CI dispatch.
    • I didn't wait for or poll iOS tests, so I have no exact-head trace or watch result.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

R12 missing success-path failure control addressed

Head9b7061966c2f38991b90a7153ffdb34e804c2873 adds the requested realphase('ios') case: successful xcodebuild plus nine trace lines must produce PhaseFailure1,receipt exit1,recordedwatchTransitionTraceError and exactlyone test invocation. It also asserts the original xcodecommand exited0. The full affected Python suite passes on Mini. All three mutations now fail their matching integration tests: deletedhook, maskedprimary65, and recorded-but-suppressedtracefailure. Source/mutations/logs retained at /tmp/ios564-9b70619-proof-i_dft55y.

Fresh independent current-head review is running. No new hosted attempt was dispatched: run42983035068 atpreceding2f09173 remains the actual bound attempt. Preserve its final trace/outcome first. Currenthead only changes Python test coverage; no qualified564cause or repair is claimed, and it still needs applicable fresh hosted qualification after that outcome. No timeout/gate/assertion/production/automaticCI change.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 9b7061966c2f38991b90a7153ffdb34e804c2873 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex. The placement limit (Home or Laptop) rules out SFL and hosted Codex, and the fresh picker selected Opus 5.5, so this is the Laptop fallback. It is neither an SFL PASS nor a formal approval. It covers the full 12-file diff, with a focus on the 06aab1b→9b70619 delta (1 file, +11/−0). This head is diagnostic work for #564. It claims no fix, and this review doesn't either.

Didn't find any major issues. 🎉

Assessment: no P1, P2 or P3. R12's P3 is fixed, and every earlier finding is still fixed or still an accepted limitation. #564's root cause is not established. No hosted run has qualified this head, so I make no readiness claim and merge stays blocked.

R12 fix (verified)

test_ios_phase_trace_failure_cannot_pass_successful_xcode is the case R12 asked for. It runs the real Runner.phase('ios', …) with nine trace lines and xcodebuild exit 0. It asserts PhaseFailure code 1, receipt exit 1, diagnostic bound in watchTransitionTraceError, xcodebuild.command.json exit 0, and exactly one -resultBundlePath call.

The setup still patches only subprocess.run, device_record and unchanged, so discovery and result validation run for real. The sibling test drives the same fixture with one valid trace line and asserts exit 0. So in the new test, exit 1 can only come from the trace error at runner L608-L613. M3 below confirms it.

I ran each mutant in a fresh copy of the exact head under the full 58-test command. Numbering follows R12's table.

Mutant Edit to pro_release_runner.py Result
M1 delete the 6-line ios hook all 3 integration tests fail or error
M2 trace error overwrites the primary (failure = PhaseFailure(…)) …preserves_original_xcode_exit FAIL
M3 trace error recorded, phase not failed (pass) …cannot_pass_successful_xcode FAIL (57/57 passed at R12)
M4 hook moved after result inspection …emits_trace_before_result_inspection… FAIL
M6 hook only when xcodebuild failed trace-emission test and new test FAIL
M7 hook only when xcodebuild passed …preserves_original_xcode_exit ERROR
M11 watchTransitionTraceError not recorded both trace-failure tests ERROR
M12 emitter called without try/except both trace-failure tests ERROR
M14 count bound 8 → 9 new test and test_trace_output_is_bounded FAIL

The parent's proof at /tmp/ios564-9b70619-proof-i_dft55y holds up. Its source/scripts matches my exact-head copy apart from __pycache__. Its three mutant diffs are exactly M1 (deleted-hook), M2 (masked-primary) and M3 (dropped-trace-failure), and each log fails the matching test. Its baseline.log shows 57 OK, which is the three affected modules without the cost pin test.

Earlier findings (re-checked at this head)

  • R1 P1, stale catalog calls: git grep GoogleUsageMetricCatalog finds 0 matches at head and 6 at base.
  • R2 P1, workflow hash pins: both pin tests pass in the 58-test run.
  • R4 P3, replacement billing session: replacementWindow hosts it, and hostBillingWebView asserts a non-nil webView.window.
  • Reset copy: the unchanged ClaudeUsageResetInventoryView.swift L102-L116 gives the limited and disabled grants the "only the reset selected by Claude" text, because neither is selected. The expected count of 2 is correct.
  • R7 P3, zero-delay control: accepted as a limitation in comment 6788203991. Nothing to change.
  • R9 P3, priority and main-thread sampling: the detached witness uses Task.currentPriority, and an independent DispatchQueue.main.asyncAfter 5 ms sample is in place (L2464-L2474).
  • R10 P3, unprinted cancellation record: the catch just returns (L2470).
  • R11 P3, untested hook: the two 06aab1b integration tests cover it. M1, M2, M4 and M7 show they bite.
  • The watch case keeps timeout: 1 (L2479), the original post-wait assertions and the stricter accessEvents == 1 (L2482). No skip, retry or deadline change.
  • Compared with base, no production source, Package.swift or Xcode project changed. Since 06aab1b, only scripts/tests/test_pro_release_validation.py changed.
  • Automatic CI is unchanged. The swiftlint job still runs only test_ci_policy.py, and test_pro_release_validation runs only in the dispatch-only iOS tests job (ci.yml L107, L123).

Delivery state (read once at 08:25Z, no polling)

  • The main ruleset requires only SwiftLint (strict). It passed at this head in PR run 42986134559, and the other jobs skipped on pull_request. There is no branch protection, no formal review and there are 0 review threads.
  • Manual run 42983035068 is at the preceding 2f09173. SwiftLint, strict concurrency, watchOS tests and SwiftPM smoke passed, and iOS tests was still in progress. I claim no result from it: no trace, no watch result and no native-commerce result. This head changed only a manual-only Python test, so I dispatched nothing.
  • Earlier native-commerce failures are separate and unmodified. This review doesn't assess or excuse them.
  • The PR body still names 2f09173 as the current head and cites 54 Python tests. The parent owns that refresh. It is not a code defect.

Commands and limits

  • Laptop, read-only shared checkout, clean with HEAD at 9b70619 before and after: git diff base..head (12 files), git diff 06aab1b 9b70619, git diff --stat 83e1264 9b70619 -- '*.swift', git grep and git show reads.
  • Mini as HemSoft (checked with gh api user): read the PR, all comments and reviews, GraphQL threads, head check runs, run 42983035068 and the main rules, once each.
  • Mini disposable copy /tmp/review-563-9b70619-lbiRmb, from git -c core.autocrlf=false archive of head and base. Runner and test-file SHA-256 match the git blobs. Python 3.14.4.
    • python3 -m unittest scripts.tests.test_pro_release_validation scripts.tests.test_ui_release_shards scripts.tests.test_ci_policy scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow gives 58 OK at head. The three modules give 51 OK at base.
    • The nine mutants above, applied by mutate563.py one at a time, with one diff and one log each.
  • Limits: no Xcode, Swift tests, simulator, real Mac, live provider, auth, account or reset, signing, install, release, App Store Connect, pricing or CI dispatch. I didn't rerun the full manual cost module, which has Linux sw_vers errors at both base and head. I didn't wait for or poll iOS tests.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Bounded diagnosis outcome: repair not established

Same Laptop worker retains claim chief-dispatch-0c08ab71-ios562 and PR563 at9b7061966c2f38991b90a7153ffdb34e804c2873. R13 independent Laptop Claude Opus5.5 xhigh review is clean (comment6790980500), and the reviewed Python command passed58 tests. Required SwiftLint passed. No hosted run exists for this current test-only-coverage head.

The completed diagnostic run42983035068 at2f09173 is FAILURE. Its ordinary iOS phase passed1112of1112 tests with0skips. The corrected watch probes are now retained in the job log:5ms case state committed2.57ms, independentmain timer8.90ms, globaltimer8.95ms, snapshot9.03ms; zero case snapshot4.22ms. These are healthy samples and do not explain the original f766923 publication at1.66seconds. There is no evidence-backed synchronization correction to apply under the no-deadline/no-assertion-relaxation/test-only limits. #564 remains open and is not claimed repaired.

The job failed later in unchanged native-commerce:23of25 passed, two failed (StoreKit billinggrace and exactrenewalmetadata). Protected commerce/release behavior was not modified. No red failure was bypassed and no unchanged retry-forgreen was dispatched. Current-head hosted qualification remains pending rather than inferred from older source-equivalent Swift.

The156MBartifact download first failed on Mini tmpfsquota; retry used a newly created owner-specific Mini disk directory, not another validation run. Complete evidence now retained at /home/franz/ios562-0c08ab71-evidence-RzPKpy/run42983035068. Earlier failures, controls and mutationproofs remain retained under their recorded Mini paths. Cancellation42974788943 was the existing45minute joblimit followed by xcresultuploadENOENT, not human cancellation.

No merge/main delivery/sync/cleanup/claimrelease/absence proof is claimed. The branch/worktree remain clean and retained; all original release/auth/account/device/production/automaticCI prohibitions hold. The same Laptop worker remains owner of every unfinished item. No run or reviewer is pending, no newtimer or replacementowner. Next useful trigger requires a qualified failing watch sample with the corrected probes or a scoped decision about the independent protected native-commerce blocker. Further identical validation solely for green is prohibited. Report this evidence/scope boundary to both requesting sweeps; neither is asked to take over PR/CI.

@HemSoft HemSoft changed the title Repair stale metric fixtures and diagnose watch validation blocker Repair metric test compilation and retain watch diagnostics Nov 11, 2026
@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Scoped commerce continuation

Reused original claim and ownership; new defining issue #565 passed the evidence/duplicate check. #564 is accepted as DIAGNOSIS UNQUALIFIED and remains OPEN. A watch synchronization fix is not a requirement of this diagnostic tranche; all actual validation failures still block merge.

Head 8f61f17 addresses the renewal test's oracle seam: the existing production mapper is exposed internally by removing only its private modifier, with no executable mapping change. The test feeds it the same verified Apple Status used for every exact metadata assertion, rather than comparing two separate SDK reads. A separate source.snapshot integration check remains. Epoch diagnostics distinguish the exact-payload mapping from an independently signed read; no tolerance or expected-value substitution is added.

Grace remains diagnostic: native SDK status updates and fixture retry/grace/rate readbacks are captured before purchase and at timeout, alongside the existing service trace. All original 75-second bounds, grace/access assertions, 25 cases, product configuration and account/transaction isolation remain. This separates SDK generation/fixture state from mapping/delivery before any grace repair claim.

Actual hosted manual run https://github.com/hemsoft-dev/codexbar-ios/actions/runs/42990740217 is registered to this Laptop thread. Fresh independent Laptop review pending. No production purchase/entitlement/billing-grace/renewal behavior, live operation, release or automatic CI change.

@HemSoft HemSoft changed the title Repair metric test compilation and retain watch diagnostics Repair metric tests and qualify native subscription fixtures Nov 11, 2026
@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Reviewed head 8f61f17d59b3c6eb52f236d9847014dc044caea2 against base and merge-base 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex. The placement limit (Home or Laptop) rules out SFL and hosted Codex, and the fresh picker selected Opus 5.5, so this is the Laptop fallback. It is neither an SFL PASS nor a formal approval. Council was not required. The review covers the full 14-file diff and focuses on the #565 delta 9b70619..8f61f17 (2 files, +60/−4).

Assessment: no P1 or P2, two P3s. The renewal-metadata change keeps the intended exact-mapping contract. The grace change is diagnosis only and repairs nothing. Run 42990740217 has not finished, so this head is not qualified and merge stays blocked.

P3-1. The renewal test no longer checks metadata through snapshot()

At base, all ten field assertions ran on the output of the real source.snapshot() (base L23-L34). At head they run on the mapper alone (L25-L35). The separate snapshot read at L36-L39 asserts only that the transaction is present, renewalVerified, and .xcode.

As a result, no native test checks renewalPrice, renewalCurrency, nextRenewalProduct or expiresAt through StoreKitProStoreSource.snapshot(). A grep of CodexBarIOSTests/StoreKitPro*.swift finds this test was the only one. Those fields drive the renewal line in ProCommercePolicy.swift L58-L60 and ProCommerceContentView.swift L20-L24.

Today's code has no defect, because snapshot() appends Self.subscription(status) unchanged (L86-L88). The gap is regression detection. A later edit to that loop that rebuilds or drops these fields would now pass the native suite.

Bounded fix, with no tolerance and no change to signedAt handling. Keep signedAt as logged-only for the separate read, and assert exact equality between separateRead and mapped on the fields that two reads of this realTime yearly purchase cannot change. Those are transactionID, state, expiresAt, willAutoRenew, nextRenewalProduct, renewalPrice, renewalCurrency, graceEndsAt and isInBillingRetry. To verify, have L86-L88 append evidence with renewalPrice: nil. That mutant should fail at base and with the fix. By inspection it passes at this head.

P3-2. The PR body does not describe this head

The body still names 9b70619 as the current head and says "No production source, Package.swift or Xcode project changes". It links only #562 and #564. At 8f61f17, StoreKitProStoreSource.swift L282 drops private in a production file, and the body never mentions #565. The change itself is acceptable, since it only changes visibility and no executable logic. To fix, refresh the head, add Refs #565, state the visibility-only production edit, and add the #565 validation boundary.

#565 contract checks (verified by inspection)

  • Exact-payload oracle. L16-L35 reads one subscription.status payload and binds transaction and renewal from it. It passes that same native value to the unchanged mapper (L282-L306) and compares every field exactly. Expected values and operators match base, with no tolerance and no substitution. The test reads the payload fields itself, so the check is not circular. Mutating willAutoRenew, isInBillingRetry, graceEndsAt or renewalPrice in the mapper would fail an assertion. The original event recorded price and currency as present.
  • Why the old line failed. The preserved native-commerce/xcodebuild.log line 134 fails base L28 (signedAt), and both dates print as 08:31:50. Base read status twice, once at test L18 and again in snapshot() L80. Qualify native subscription grace and exact-payload test fixtures #565 cites Apple's RenewalInfo.signedDate as the date of the signed JWS, so two reads can differ by less than a second. The new epoch fields at L54-L56 will show the difference. Log line 135 also shows the old event hard-coded matchesVerifiedApplePayload: "true" while line 28 failed. The computed value at L40-L49 fixes that false record.
  • Grace diagnosis. configureBillingRenewal, the 75 s bound and every post-wait grace and access assertion are unchanged. The context retains the new native Status.updates recorder (capped at 16 entries) and close() cancels it. The settings readbacks before and after purchase add assertions and remove none. The direct status read at timeout runs inside the existing nested do/catch (L151-L168), so the primary error is still the one thrown. The new data separates fixture settings, SDK generation, stream delivery (native trace against debugSubscriptionObservation) and mapping (sourceStatuses). The original event shows renewing=true, issue=false with expiry 08:28:27 and no renewal transaction by 08:29:15. That points toward the SDK never running the renewal, not toward mapping. This head's fields are needed to confirm it.
  • Scope. The 25 -only-testing methods, the .storekit product configuration, account and transaction isolation, deadlines and automatic CI are unchanged. Nothing is skipped, retried or disabled.

Unverified risks, not findings. Check them in run 42990740217.

  • The new recorder at L122-L132 is the only retained listener that iterates a raw StoreKit sequence. All others iterate an AsyncStream, which ends on cancellation. close() cancels each listener and then awaits it (StoreKitProTestSupport.swift L110-L113). Apple does not document whether Product.SubscriptionInfo.Status.Statuses ends on cancellation. If it does not, teardown waits for the next status. The only limit then is the runner's test-time allowance: a 90 s default, which XCTest rounds up to whole minutes, under a 120 s cap. Confirm that the grace case stays near its earlier 87 s and has no allowance or restart message. If teardown hangs, bridge the recorder through an AsyncStream, the way production subscriptionUpdates() does at L122-L139.
  • The fixture readbacks read properties on the SKTestSession object. A true there may not prove that the StoreKit daemon applied the setting.

Earlier work, re-checked at this head

git grep GoogleUsageMetricCatalog finds no matches. The workflow pins, reset copy, billing-window hosting, and #564 trace retention and integration controls are byte-identical to 9b70619, which R13 reviewed in comment 6790980500. git diff --stat 9b70619 8f61f17 lists only the two Swift files. #564 stays open as DIAGNOSIS UNQUALIFIED, and this review waives no actual red gate.

Delivery state, read once with no polling

  • The main ruleset requires only SwiftLint, in strict mode, with no review count. mergeStateStatus is CLEAN.
  • SwiftLint passed at this head in PR run 42990644471 and dispatch 42990740217. In 42990740217, SwiftPM smoke tests passed. iOS tests, watchOS tests and Strict concurrency were in progress. I claim no native-commerce or iOS result for this head.

Commands and limits

  • Laptop, read-only shared checkout, clean at 8f61f17 before and after. Read-only git diff, git show and git grep.
  • Mini as HemSoft, confirmed with gh api user. I read the PR, Qualify native subscription grace and exact-payload test fixtures #565, comments, reviews, threads, head check runs, run 42990740217 and the main rules once each. I read the preserved /home/franz/ios562-0c08ab71-evidence-RzPKpy/run42983035068/pro-native/native-commerce/xcodebuild.log with grep only.
  • Mini owner-disk disposable copy /home/franz/pr563-claude-review-8f61f17-KtjGuN, from git -c core.autocrlf=false archive of the head. SHA-256 of the runner, workflow and both changed Swift files matches the git blobs. I removed the copy afterwards. With Python 3.14.4, the R13 command python3 -m unittest scripts.tests.test_pro_release_validation scripts.tests.test_ui_release_shards scripts.tests.test_ci_policy scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow gave 58 OK. The full manual cost module ran 16 tests with 5 Linux sw_vers errors. That is a platform limit, not a pass.
  • Context7 was unavailable. I used Apple's Product.SubscriptionInfo.Status.updates reference, and the signedDate and SKTestSession grace pages that Qualify native subscription grace and exact-payload test fixtures #565 links.
  • I ran no Xcode, Swift compile, native test, Swift mutation or simulator, and used no real Mac. I did no live StoreKit, provider, auth, account or reset work, and no signing, install, release, TestFlight, ASC, pricing or CI dispatch. I did not wait for or poll CI.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

R14 findings addressed at 23232c1

P3-1 is fixed in 23232c123c26111c383d9f89b4f82b25528f7bbe: the real source.snapshot() output now has exact checks for transaction ID, state, expiry, auto-renew, next product, price, currency, grace end and billing retry against the same-payload mapper output. The real-time yearly fixture cannot renew between these reads. The original exact signedAt assertion remains on the same payload; the separate signed timestamp remains diagnostic. No tolerance or expected value changed.

P3-2 had already been corrected while R14 was running: the live body names #565 and the visibility-only production-file edit. It is refreshed again for this head and makes no exact-head qualification claim.

git diff --check passes. Swift integration assertions and the suggested dropped-price mutant have not been executed at this head; no mutation pass/fail is claimed. All 25 native cases, grace deadline/assertions and product configuration remain unchanged. R14's listener-cancellation concern remains an explicit observation to check in run 42990740217.

Fresh exact-head Laptop review R15 is requested. The existing run 42990740217 at 8f61f17 is left to finish; no duplicate dispatch or rerun is started. Its actual completion event will drive the next diagnosis or changed-head qualification. #564 remains OPEN, DIAGNOSIS UNQUALIFIED. Merge remains blocked.

@HemSoft

HemSoft commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Cross-model review (Claude Opus 5.5 via T3)

Review R15 of head 23232c123c26111c383d9f89b4f82b25528f7bbe. Base and merge-base are both 53e53837981121ab3a13986ed5630b9ce52d0b67, which is still the main tip. Claude Opus 5.5 (claude-opus-5-5, requested effort xhigh) ran on Laptop in place of connected Codex, whose eligible placement could not be established. Routing excluded SFL and hosted Codex. This comment is neither an SFL PASS nor a formal approval. It covers the full 14-file diff and focuses on 8f61f17..23232c1, which changes one file by 11 added lines.

Assessment: no P1, P2 or P3 findings at this head. R14 P3-1 is fixed. The grace change still only diagnoses and repairs nothing. No hosted manual run has qualified this head, so merge stays blocked.

R14 P3-1 fix (verified by inspection)

  • L36-L50 reads source.snapshot() and checks exact equality with the same-payload mapper output on all nine fields R14 named: transactionID, state, expiresAt, willAutoRenew, nextRenewalProduct, renewalPrice, renewalCurrency, graceEndsAt and isInBillingRetry. renewalVerified and .xcode are still asserted on the separate read. No tolerance was added, and git diff 8f61f17 23232c1 contains only additions.
  • The same-payload oracle at L16-L35 is unchanged. signedAt == renewal.signedDate is still exact there. The separate read's signature date is only logged (L65-L67).
  • The nine fields cannot change between the two reads in this fixture:
    • configuredSession sets timeRate = .realTime (StoreKitProTestSupport.swift L66).
    • The yearly product is P1Y with a P1W free introductory offer (CodexBarPro.storekit L78-L87), so the earliest expiry is a week away.
    • Nothing touches the subscription between the reads. Both reads go through the same mapper, and the snapshot row is selected by the same transaction ID.
  • Mutant reasoning, by inspection only. Suppose snapshot() L86-L88 appended evidence with renewalPrice: nil. That mutant fails at base and passes at 8f61f17. At this head, L47 fails, because the preserved run 42983035068 event (native-commerce xcodebuild.log line 135, runtime 26.2) recorded renewalPricePresent: true. Mutants that drop state, expiry, auto-renew, next product or currency in that loop fail the same way. No Swift mutant was compiled or run, so this is not a mutation result.
  • Residual gap, not a finding: this test no longer catches a mutant that changes only signedAt inside the snapshot() loop. That follows from Qualify native subscription grace and exact-payload test fixtures #565's contract, since two independently signed reads can differ. The epoch is logged for diagnosis.

#565 delta at 8f61f17 (re-checked)

  • StoreKitProStoreSource.subscription changes from private to internal (L282) and its body is unchanged. No other production file is in the PR.
  • The grace test keeps its 75-second bound and every post-wait grace and access assertion (L158-L189). The native status recorder is capped at 16 entries, and the context retains it.
  • These are unchanged from base: the 25-test native-commerce manifest in scripts/pro-release-phases.json, both .storekit files, schemes, the Xcode project and Package.swift.

Observations, not findings

  • The raw Status.updates cancellation question from R14 is still UNVERIFIED. close() cancels each listener and then awaits it (StoreKitProTestSupport.swift L110-L113). Nobody has established whether Product.SubscriptionInfo.Status.updates ends when its task is cancelled. I did no new doc lookup, and R14 found no documentation. If the sequence does not end, teardown waits for a status that may never arrive. The native phase runs with -test-timeouts-enabled YES, a 90-second default allowance and a 120-second maximum (pro_release_runner.py L591-L592). A hang would therefore surface as a failed test, never as a false pass. Check this in run 42990740217.
  • The grace timeout path at L162-L179 calls storeKitProducts() and reads subscription.status before it emits storeKitEvent. If either throws, the whole billing-grace-timeout event is lost, including traces that were already collected. The primary error is still thrown. No trigger has been observed: at the earlier timeout, storeKitState loaded products and statuses without error. Emitting the event first, or catching those two reads separately, would keep the traces.

Earlier work at this head

Delivery state (each item read once, no polling)

  • The main ruleset 20103668 requires only SwiftLint, in strict mode. There is no classic branch protection. mergeStateStatus is CLEAN, and the PR has no review threads.
  • At this head, SwiftLint passed in PR run 42992743512. No workflow_dispatch run exists for this head.
  • Run 42990740217 is at 8f61f17, not this head. Its SwiftLint, watchOS tests, SwiftPM smoke tests and Strict concurrency jobs passed. The smoke job log shows MetricTileContentRegressionTests at 13/13 and ClaudeUsageReset at 20/20. iOS tests was still in progress when I read it at 09:13 UTC. I claim no native-commerce or iOS result for any head.
  • The PR body matches this head and makes no claim that it has passed hosted qualification.

Commands and limits

  • Laptop: I used the shared checkout read-only, and it was clean at 23232c1 before and after. I ran only git diff, git show and git grep.
  • Mini, as HemSoft (confirmed with gh api user): I read the PR, Qualify native subscription grace and exact-payload test fixtures #565, comments, reviews, threads, head check runs, the run 42990740217 jobs, the log of smoke job 130422722449 and the main rules, once each. I read the preserved /home/franz/ios562-0c08ab71-evidence-RzPKpy/run42983035068/pro-native/native-commerce/xcodebuild.log with grep only.
  • Mini disposable copy on owner disk: /home/franz/pr563-claude-r15-23232c1-OMdf4k, made with git -c core.autocrlf=false archive 23232c1. The SHA-256 of the runner, the workflow, both StoreKit Swift files and the runner test matched the git blobs. With Python 3.14.4, python3 -m unittest scripts.tests.test_pro_release_validation scripts.tests.test_ui_release_shards scripts.tests.test_ci_policy scripts.tests.manual.test_automatic_cost.CostControls.test_removing_only_cost_mode_preserves_pinned_remaining_workflow ran 58 tests, all OK. I removed the copy before posting.
  • Not run: Xcode, Swift compilation, native tests, Swift mutation, simulators or a real Mac. The new assertions at 23232c1 have not been compiled. I did no live StoreKit, provider, auth, account, reset or purchase work, and no signing, install, release, TestFlight, ASC or pricing work. I did not dispatch, rerun or cancel CI. Context7 was unavailable.

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

Labels

model:gpt-6.1-sol PR authored by gpt-6.1-sol

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove stale Google catalog reference from metric tile tests

1 participant