Skip to content

Release 1.0.0 - #11

Open
CoderGamester wants to merge 51 commits into
masterfrom
develop
Open

Release 1.0.0#11
CoderGamester wants to merge 51 commits into
masterfrom
develop

Conversation

@CoderGamester

@CoderGamester CoderGamester commented Jul 29, 2026

Copy link
Copy Markdown
Owner

New:

  • Initial release of the consolidated Mobile Services package.
  • Native UI: Alerts, action sheets, and toasts for iOS and Android, plus OS review requests and share sheets for text, URLs, and images.
  • Notifications: Local notification scheduling and management with channels, queueing, and the fluent Schedule().In(...).Title(...).Body(...).Channel(...).Send() builder.
  • Gestures: Enhanced Touch swipe and tap detection with velocity and consistency tracking.
  • iOS Audio Session: Override the iOS silent switch so audio can keep playing.
  • Haptics: Zero-dependency cross-platform haptic feedback with nine presets, custom intensity, and time-bounded looping.
  • Device: The Device umbrella service exposes safe area, screen wake, battery and low-power mode, iOS audio session, permissions, App Tracking Transparency, and deep links.
  • Permissions: Unified iOS and Android runtime permissions for Camera, Microphone, Location, Photo Library, Notifications, and multi-permission async requests.
  • App Tracking Transparency: An iOS 14.5+ ATTrackingManager bridge with no dependency on com.unity.ads.ios-support.
  • Deep Links: An Application.deepLinkActivated wrapper with cold-start link queueing for the first subscriber.
  • Deep Link Router: Route patterns over IDeepLinkService with captured parameters such as /promo/:id.
  • Mobile Service umbrella: IMobileService provides one DI registration for Native UI, Notifications, Haptics, and Device services.
  • Native UI instance interface: INativeUiService and NativeUiServiceInstance support mockable consumer code.
  • Device Simulator Plugin: An embedded Unity Device Simulator panel provides platform-shaped native UI mocks, live diagnostics, and a per-preset haptic envelope graph.
  • Mobile Services Config asset: Configure localized permission descriptions, capability toggles, Android manifest opt-ins, and Play In-App Review Gradle setup from Tools > GameLovers > Mobile Services > Select Mobile Services Config.
  • Build Postprocessor: Automatically inject iOS usage descriptions and entitlements, Android manifest entries, and the Play In-App Review Gradle dependency, with validation for missing configuration.
  • Samples: Added MobileServicesPlayground, HapticsPalette, NotificationsScheduler, and DeepLinkRouter.
  • Documentation: Added subsystem references and editor-tooling guides for the Device Simulator and build pipeline.

Changed:

  • Consolidated the package under the com.gamelovers.mobileservices package name, GameLovers.MobileServices.* namespaces, and GameLovers.MobileServices assembly.
  • Updated the package baseline to Unity 6 (6000.0+) and added the Unity Mobile Notifications and Input System dependencies.

Fixed:

  • Fixed persisted notifications so nullable IDs, badge numbers, and delivery times survive background/foreground rescheduling.
  • Fixed local notification delivered and expired events so subscribers added after service construction receive callbacks.

Removed:

  • Removed legacy tap detection; use the Unity Input System's TapInteraction instead.
  • Removed gamepad input management; configure gamepad input through the Unity Input System.

Migration:
This package consolidates three previously separate packages:

  • com.gamelovers.nativeui (v0.2.5) → GameLovers.MobileServices.NativeUi
  • com.gamelovers.notificationservice (v0.1.7) → GameLovers.MobileServices.Notifications
  • com.gamelovers.inputextensions (v0.1.0-preview.4, swipe detection only) → GameLovers.MobileServices.Gestures
  • AlertButtonStyle.PositiveAlertButtonStyle.Destructive; AlertButtonStyle.NegativeAlertButtonStyle.Cancel.

CoderGamester and others added 19 commits January 14, 2026 00:19
feat(gestures): add swipe and tap detection using Input System
refactor(native-ui): migrate to unified namespace and assembly
chore(mobile-services): consolidate packages and update to Unity 6
docs(mobile-services): update documentation and add AGENTS.md
…ct state

docs: add MIT LICENSE disclaimer to this project
feat(gestures): add TapInput struct for tap detection
docs(gestures): update agent guide for new gesture architecture
chore(gestures): remove obsolete input abstraction files
- Add CLAUDE.md + .meta at package root; thin @AGENTS.md import wrapper
- AGENTS.md: add Companion-files blockquote
- README.md: remove obsolete PointerInputManager / Controls / "Input System
  Integration" marketing (source uses EnhancedTouch only); document Tap
  gestures alongside Swipe (TapInput struct, Tapped event); trim to Option B
  shape (470 -> 210 lines); add Related docs footer

Made-with: Cursor
- Add IDeviceService umbrella facade with 8 sub-services: SafeArea,
  ScreenWake, Battery (with iOS/Android low-power-mode awareness),
  Connectivity, IosAudioSession, Permissions (unified iOS+Android,
  Task-based async), App Tracking Transparency (zero ads-ios-support
  dep), Deep Link (with cold-start link queueing). One shared internal
  MonoBehaviour host (DeviceServicesHost) for all event-driven children.

- Add IHapticsService with 9 cross-platform presets, custom intensity,
  and time-bounded looping. iOS UI*FeedbackGenerator + Android
  VibrationEffect bridges, no third-party plugin dependency.

- Add NativeUiService.RequestReview() (iOS SKStoreReviewController +
  Android Play Core In-App Review) and NativeUiService.Share() (iOS
  UIActivityViewController + Android Intent.ACTION_SEND).

- Add 5 iOS native bridges (Att.m, Battery.m, Haptics.m,
  iOSAudioSession.m, Permissions.m) under Plugins/iOS/.

- Add Runtime/AssemblyInfo.cs granting InternalsVisibleTo to both test
  assemblies for black-box-internal access.

- Bootstrap NUnit test suite: 28 fixtures, 106 active tests
  (75 EditMode + 31 PlayMode), all green. Codify black-box-only testing
  policy in a new Tests/AGENTS.md (fourth per-package tests guide in
  the repo, after services/uiservice/gamedata).

- Rewrite README/AGENTS/CHANGELOG to document the v1.0.0 surface and
  gotchas.

Co-authored-by: Cursor <cursoragent@cursor.com>
Adds two new rendering surfaces feeding the existing MobileSimulatorState
broker so the truth-mirror mocks can paint right inside Unity's Game /
Simulator view next to the simulated phone screen:

- `MobileSimulatorRuntimeOverlay` (editor-only `[InitializeOnLoad]` bootstrap):
  spawns a `[EditorOnly]` `DontDestroyOnLoad` GameObject with a programmatic
  `PanelSettings` (`sortingOrder = short.MaxValue`, `ConstantPixelSize`,
  `clearColor = false`) carrying a `UIDocument` on `EnteredPlayMode`, tears
  down instantly on `ExitingPlayMode`. Opt-in via the new
  `MobileServicesSettings.EnableRuntimeSimulatorOverlay` toggle (default OFF,
  surfaced under a new "Editor tooling" section in Project Settings). Root
  is `pickingMode = Ignore` + `Color.clear` so an empty stage is transparent
  to clicks; active mock scrims re-absorb input for modal behaviour.

- `MobileServicesDeviceSimulatorPlugin`
  (`UnityEditor.DeviceSimulation.DeviceSimulatorPlugin` subclass): embeds a
  slim Control Panel inside Unity's Device Simulator window. Sections per
  subsystem (Native UI / Notifications / Device state / Permissions / ATT /
  Deep Links) with a top-row "Open full Explorer →" button. Auto-syncs
  `MobileSimulatorState.Platform` from `Application.platform` on a 500 ms
  `schedule.Execute(...).Every(...)` poll (chosen over
  `DeviceSimulator.deviceChanged` due to inconsistently-documented delegate
  signature across Unity 6 minor versions). Flips a new
  `MobileSimulatorState.IsActivePluginConnected` flag while alive; the
  Explorer's `Render as: iOS | Android` dropdown subscribes to
  `PluginConnectedChanged` and greys out (+ tooltips) while the plugin
  owns the platform skin — handoff is bidirectional and symmetric.

Editor-tooling tests retired: deleted the
`GameLovers.MobileServices.Editor.Tests` asmdef and its 5 test classes
(`EditorPlatformSimulatorTest`, `MobileServicesBuildPostprocessorTest`,
`MobileServicesExplorerWindowTest`, `MobileServicesSettingsTest`,
`MobileSimulatorWindowTest`) plus the `Editor/AssemblyInfo.cs` bridge and
the matching `InternalsVisibleTo("GameLovers.MobileServices.Editor.Tests")`
grant on `Runtime/AssemblyInfo.cs`. Editor tooling is now validated
manually only — codified in `Tests/AGENTS.md` §1 (new "Editor tooling (NOT
tested)" group), §9 (new coverage-gap entry), §11 (new update trigger), and
parent `AGENTS.md` §3 (Tests bullet rewritten). Aligns the package with
what `Tests/AGENTS.md` §10's layout table already implied — `EditMode/Editor/`
was never documented.

Docs: `docs/explorer.md` restructured from "Mobile Simulator Window
(truth-mirror)" into a "Three rendering surfaces" table + per-surface
subsections; stale device-frame line dropped; recommended workflow rewritten
around the Device Simulator + plugin + opt-in overlay; "When to use which"
table extended with a plugin row.

`AGENTS.md`: §2 Editor block describes all three rendering surfaces +
`EnableRuntimeSimulatorOverlay`; §3 Layout convention adds
`Editor/Explorer/DeviceSimulatorPanel/`; §3 `InternalsVisibleTo` paragraph
trimmed; §4 new gotcha entry for the runtime overlay's Play-mode-only,
Editor-asmdef-owned lifecycle; §8 update policy expanded.

`CHANGELOG.md`: folded into the existing `## [1.0.0]` section
(pre-publication versioning rule — no `package.json` version bump) — 2
new `### Added` bullets (Runtime Simulator Overlay, Device Simulator Plugin)
+ new `### Removed` subsection.

Tested: EditMode 790 passed / 0 failed / 1 inconclusive (the documented
`IsOutdatedVersion_DirectInvocation` host-version-parser brittleness);
PlayMode 263 passed / 0 failed. Clean.

Bundled alongside the in-flight v1.0.0 polish work (Phase A-E DX overhaul,
unreleased) that landed in the working tree in a prior session.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replaces the previous "single broker + bidirectional authority handoff"
design (`MobileSimulatorState.IsActivePluginConnected` /
`PluginConnectedChanged` greying out the Explorer dropdown when the
Device Simulator plugin attaches) with per-target routing — resolves
both "Explorer dropdown is greyed when the plugin is alive" and "plugin
clicks paint into the standalone Mobile Simulator window" with one
mechanism. Neither symptom can recur because the surfaces no longer
share state.

Broker (`MobileSimulatorState.cs`):
- New `[Flags] enum SimulatorTarget { None, StandaloneWindow,
  RuntimeOverlay, All }` carried by every `Push*`, default `All` so
  existing call sites keep broadcast semantics unchanged.
- `Platform` / `PlatformChanged` split into per-surface
  `WindowPlatform` (driven by Explorer dropdown) + `OverlayPlatform`
  (auto-synced by Device Simulator plugin from `Application.platform`),
  each persisted to its own `EditorPrefs` key.
- Every `*Requested` event signature widened to
  `Action<SimulatorTarget, TSpec>`. `IsActivePluginConnected` /
  `PluginConnectedChanged` removed entirely.

Renderers:
- `MobileSimulatorWindow` subscribes to `WindowPlatformChanged` + filters
  every payload event on `SimulatorTarget.StandaloneWindow`.
- `MobileSimulatorRuntimeOverlay` subscribes to `OverlayPlatformChanged`
  + filters on `SimulatorTarget.RuntimeOverlay`.

Producers:
- `MobileServicesExplorerWindow`: dropdown writes `WindowPlatform`,
  greying logic gone, dropdown stays interactive forever. Inline italic
  scope-hint label + tooltip ("Controls the Mobile Simulator window
  only.") with new `.explorer-header-hint` USS rule.
- `MobileServicesDeviceSimulatorPlugin`: every push passes
  `SimulatorTarget.RuntimeOverlay` (constant `OverlayOnly`), including
  its `Dismiss all mocks` button (was `EditorPlatformSimulator.
  DismissAllOverlays()` — broadcast). `SyncPlatformFromHost` writes
  `OverlayPlatform` only.
- `EditorPlatformSimulator` untouched — programmatic API stays
  broadcast-by-default for tests / scripted automation.

UX polish (4 items bundled):
- Plugin: new auto-hiding `msp-overlay-hint` amber banner inside the
  Control Panel surfaces the runtime-overlay precondition
  (`EnableRuntimeSimulatorOverlay = true` AND play mode) with a
  one-click `Open Project Settings →` jump button. Same 500 ms poll
  that re-syncs the platform also refreshes the banner so it
  auto-hides the moment both preconditions are satisfied. Without it,
  the overlay-scoped routing made it look like the buttons did
  nothing in edit mode / when the setting was OFF.
- `NativeUiTab`: new `MakePrimaryButtonRow(params Button[])` helper
  wrapping primary buttons so they shrink-wrap to natural width
  (matching the Alerts pair) — applied to Toast / Review / Share +
  the Alert pair refactored through it for consistency.
- `NativeUiTab`: Toast `Long duration` toggle text now interpolates
  `(3.5s vs 2.0s)` from new `ToastShortSeconds` / `ToastLongSeconds`
  constants and carries a tooltip mapping to Android's
  `Toast.LENGTH_SHORT/LONG` (iOS fakes the toast and reuses the same
  timings for editor preview parity).
- `NativeUiTab`: `Show Action Sheet` button greys out reactively when
  `WindowPlatform == Android` with an OS-contract tooltip explaining
  Android has no native action-sheet idiom (both shapes collapse to
  the same Material 3 dialog on a real device). Subscribes to
  `WindowPlatformChanged` and unsubscribes via
  `RegisterCallback<DetachFromPanelEvent>`.

Docs:
- `AGENTS.md` §2: Explorer dropdown + simulator-broker paragraphs
  rewritten for the per-surface scope ("two renderer surfaces, one
  broker" + per-target routing); plugin paragraph "scoped to
  RuntimeOverlay" + new precondition-banner sentence.
- `docs/explorer.md`: Explorer intro + "Three rendering surfaces" +
  Device Simulator plugin section + "Acknowledged overlap" platform-
  toggle row all rewritten for the per-target model.
- `CHANGELOG.md`: folded into existing `## [1.0.0]` section per
  pre-publication versioning rule (verified `git tag --list` shows
  no v1.0.0 tag yet) — terse user-facing bullets only.

Editor tooling validated manually only per `Tests/AGENTS.md` §1 — no
automated tests touched.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ervice

Delete the Mobile Services Explorer window, the standalone Simulator
window, all Explorer tabs + base, and the ConnectivityService (+interface
and its test) as part of consolidating editor tooling onto the Device
Simulator plugin.
… Simulator plugin

Make the Device Simulator plugin the single editor surface and the
in-Game overlay the single canvas (edit + play mode). Collapse the
simulator broker to one Platform skin, redesign the notification heads-up
mock + add a Painter2D intensity-over-time haptics curve, and reduce the
panel to mock previews + state-setting controls (Permissions/ATT state
dropdowns, play-mode gating with per-section banners). Add runtime-settable
INotificationService.Mode and drop ConnectivityService from the IDeviceService
umbrella. Update tests, samples, and docs to match.

Co-authored-by: Cursor <cursoragent@cursor.com>
…panel

Add an "Editor Simulator" header toggle (MobileSimulatorState.Enabled,
persisted to EditorPrefs) that enables/disables every panel section as a
group and shows/hides the in-Game-view [EDITOR SIMULATOR] banner; turning
it off clears any visible mock.

Replace the global "Dismiss all mocks" button with per-section dismiss
buttons: "Dismiss all UIs" in Native UI and "Dismiss Banner" in
Notifications.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ication service surface

Adds 10 black-box/gray-box tests filling Editor-runnable coverage gaps found
by the tests audit (no P0 gaps; all platform/native paths remain documented
manual-only Skips per Tests/AGENTS.md §9):

- EditMode: HapticsService default ctor (Editor backend selection) and
  NotificationBuilder Subtitle/Id/SmallIcon/LargeIcon fluent setters.
- PlayMode: MobileService/DeviceService default-ctor child wiring (new files),
  BatteryService/SafeAreaService default ctors, and MobileNotificationService
  Mode round-trip, empty PendingNotifications, and the two delivery/expiry
  events not firing on Editor schedule.

All 165 tests pass (128 EditMode + 37 PlayMode).

Co-authored-by: Cursor <cursoragent@cursor.com>
The Device Simulator Permissions/ATT controls were instant state-setters;
they now reproduce the on-device flow: the first runtime RequestAsync() /
RequestAuthorizationAsync() on a NotDetermined entry shows the native-style
prompt in the in-Game-view overlay and resolves the Task on answer, then the
decision is cached (EditorPrefs) so repeat requests never re-prompt. The
dropdowns are the Settings surface; reset re-arms the prompt.

- Add editor-only EditorRequestAsyncOverride hooks to PermissionsService and
  AttService (precedence over the sync override; no-override default unchanged).
- Add EditorPrefs-backed NotDetermined decision store + Engage/Disengage and
  prompt-once-then-cache request handlers (usage text from MobileServicesSettings)
  in EditorPlatformSimulator; ATT prompts under the iOS skin only.
- Panel: dropdowns write the store (ungated), NotDetermined reset buttons, and a
  play-mode Allow/Deny fallback for resolving a pending prompt.
- Surface Permissions/ATT guidance in a highlighted banner; lift user-facing
  panel copy into constants.
- Update AGENTS.md, docs/explorer.md, CHANGELOG (Unreleased).
…cesConfig

Native UI / review:
- RequestReview() stays OS-native (iOS SKStoreReviewController, Android Play
  In-App Review); fire-and-forget with request/launch logging and a
  warning/error when the Play flow cannot run (no store-URL fallback).
- Device Simulator renders a faithful per-platform review mock (iOS centered
  star sheet / Android Play bottom sheet), shown only when game code calls
  RequestReview() via the EditorRequestReviewOverride hook (no panel trigger).
- Auto-inject the Play In-App Review Gradle dependency via
  IPostGenerateGradleAndroidProject (default on, conflict-safe, editable
  coordinate) so RequestReview() works zero-config on Android.
- Prefix iOS exports _GameLoversAlertMessage / _GameLoversToastMessage to avoid
  duplicate-symbol linker collisions.

Config refactor:
- Replace the MobileServicesSettings ScriptableSingleton with an editor-only
  MobileServicesConfig ScriptableObject (Instance locator + custom Inspector +
  "Tools > GameLovers > Mobile Services > Select Mobile Services Config").
- Emit per-locale iOS usage descriptions as <locale>.lproj/InfoPlist.strings +
  CFBundleLocalizations for true device-language localization.
- Trim over-engineering: drop BackgroundAudio (Unity Player Settings owns it),
  placeholder soft-mode (validation is now fail-fast), BuildCallbackOrder,
  EnableRuntimeSimulatorOverlay, and ScanPopulatedCapabilities; rename
  DisableBuildPostProcessing -> ManageNativeBuildManually.

Docs:
- Fix stale IMobileService comments (remote notifications / connectivity).
- Rewrite the 1.0.0 CHANGELOG (Unreleased) and update AGENTS / README / docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ty 6 API updates

SerializableNotification uses explicit has-value flags for nullable fields so
background-rescheduled notifications keep their delivery time. SafeAreaContainer
migrates to [UxmlElement]; simulator uses FindAnyObjectByType.

Co-authored-by: Cursor <cursoragent@cursor.com>
Folds into the existing CHANGELOG [1.0.0] section -- 1.0.0 is
unreleased (max tag 0.2.5). No package.json version bump.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@CoderGamester CoderGamester self-assigned this Jul 29, 2026
CoderGamester and others added 10 commits August 2, 2026 14:38
Adds (or updates) Tests/AGENTS.md and its Tests/CLAUDE.md wrapper, and points
the package-root AGENTS.md at it as required reading before touching Tests/.

Sections 1-2 (ADMIT test-admission criteria, RCR revert-and-confirm-red) are
shared verbatim across all six GameLovers packages and must be changed in
lockstep. This revision anchors mutation references on file + symbol rather than
file:line, which rots, and caps the on-test comment budget - change narration and
investigation transcripts belong in the commit body, not on the test.

No test or production code changes in this package.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ed §2)

§2 declared any test without an RCR line "suspect by default", but some correct
tests provably have no one-line mutation - double-guarded validation, where an
unconfigured object trips two independent guards so disabling either leaves the
other throwing. The rule was mislabelling tests that are right and unbreakable.

Adds an UNFALSIFIABLE exemption on §13's terms: the reason must be falsifiable,
must name both guards, and must record that a mutation was tried and observed
green. "Couldn't find one" is explicitly not a reason - that is an unfinished
RCR, not an exemption.

Also adds a verdict table for tests that resist mutation, because they are not
one problem: A5 duplicates get deleted (naming the surviving sibling), D2
overclaims get a strengthened assertion or an honest rename, and UNFALSIFIABLE
tests are kept with the exemption comment. The class must be proven before
acting - an A5 duplicate by observing the sibling's mutation redden both, a D2
overclaim by observing the implied mutation leave the test green.

§1 and §2 remain byte-identical across all six packages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Section 2's verdicts table gains a fourth class. UNFALSIFIABLE was absorbing tests
that section 1's A3 rule would never have admitted - the tell being reasons like
"no line in Runtime/ participates" or "these are C#'s zero-init values", which
describe a test that pins nothing rather than one that is hard to break.

New rule: if no line in Runtime/ or Editor/ participates in the assertion, it is an
A3 reject and the verdict is delete. UNFALSIFIABLE stays reserved for behaviour this
package genuinely owns but cannot be broken one line at a time.

Sections 1-2 are shared verbatim across all six Tests/AGENTS.md; no test or
production changes in this package.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Section 13 now carries a dated baseline for this package's runtime assembly, plus
the reason to steer by that number rather than the combined one.

Every earlier coverage figure in this repo was an artifact and must not be compared
against:
- reports before today ran without -debugCodeOptimization, so Unity compiled Release
  and emitted ~40% fewer sequence points (MathfloatP showed 637 coverable lines
  instead of 1002) - a silently shrunken denominator
- some runs leaked test and sample assemblies into scope, and some covered only 3 of
  the 6 packages

The current run covers all 11 production assemblies with none leaking, verified via
the MathfloatP denominator check now documented in Tools/coverage.sh.

Repo-wide: runtime 73.9%, Editor 5.5%, combined 41.0%. Editor is 48.1% of all
coverable lines and is accepted-untestable per the ACCEPTED (iii) rows in section 13,
which is the whole reason the combined figure is not the one to track.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sections 1-2 are shared verbatim across all six packages; this adds a sixth admission
question and the worked instance behind it.

A6 asks whether an assertion's outcome would change if project configuration changed -
a renderer feature installed or removed, an Addressables catalog built, a sample
imported. If so the test must READ that state rather than assume one value of it.

A6 is not A3. A3 asks whether the package computed the value; A6 asks whether the test
assumed which value it would be. A test can satisfy A3 and still fail A6, which is
exactly how the gap went unnoticed: UiBackdropBlurPresenterFeatureTests read a
package-computed flag (UiBackdropBlurRendererFeature.IsInstalled) but hard-coded the
expectation that it was false. Batchmode never instantiates the URP renderer, so the
flag was false there and all five tests passed; in the Editor the feature registers from
the project's renderer asset and all five failed. The fixture was asserting a fact about
the repo, not about the code under test.

Validated against the existing corpus before being written, per root AGENTS.md 2.2: the
blur fixture was the only violation and is already fixed. AddressablesUiAssetLoaderTests
asserts on a key that is unresolvable either way, and UiCameraStackFeatureTests builds
its own cameras rather than reading project renderer state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
86 of 86 mutations observed RED and reverted; all production files byte-identical
afterwards. Full EditMode suite green at 826/826.

24 EditMode tests were deliberately left without a mutation rather than given an
invented one, classified with evidence:

- A3 reject (11): auto-property round-trips, struct zero-init, an Assert.Pass() body,
  and two notification tests where nothing in the editor ScheduleNotification branch
  can fire the event being counted.
- A5 duplicate (8): the four NativeUiServiceInstance *_DoesNotThrowInEditor tests die
  only on the same editor short-circuit as their NativeUiServiceTest siblings, which
  additionally assert the log text; the two DefaultCtor_UsesSharedHost tests are
  one-line delegations to Instance.
- D2 overclaim (3): DefaultCtor_InEditor_SelectsEditorBackend_NotSupported cannot
  distinguish the backend it names (NoOpHapticsBackend satisfies all three assertions
  identically); Dispose_UnregistersAllHostHandlers never observes an unregistration.
- UNFALSIFIABLE (3) and Smoke-exempt (1), each with the reason recorded on the test.

A6 exposure worth acting on separately: five tests read process-wide editor statics
they never establish - PermissionsService/AttService overrides, plus
BatteryService.EditorLowPowerModeOverride and NativeUiService.EditorRequestReviewOverride.
They assert the bare-editor default and pass only while nothing has engaged the Device
Simulator. Tests/AGENTS.md section 13 already carries an OPEN row for the first two; the
latter two should be added to its owed SetUp/TearDown reset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
27 mutations; 24 RED first pass, and the 3 that produced no report were all MY bad
mutations rather than test failures. All three shared one root cause worth recording:

Flipping `||` to `&&` on a `!int.TryParse(a, out var x) || !int.TryParse(b, out var y)`
guard breaks C# definite-assignment analysis - with `&&` the second TryParse only runs when
the first succeeds, so `y` is not assigned on every path reaching the code below, and the
file fails to compile with CS0165. The harness surfaces that as "no report after 3
attempts", which reads like a hang rather than a compile error.

The working form uses non-short-circuit `|` so both out variables are always assigned, then
`& false` to disable the guard:
  if ((!int.TryParse(idText, out var id) | !int.TryParse(statusText, out var statusInt)) & false)
All three then went RED-OK and isolated.

EditMode 826/826, PlayMode 299/299.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hotting

MobileNotificationService's ctor assigned the backing fields of its two
field-like events into GameNotificationsMonoBehaviour's plain Action fields.
Both were null at that point, so the host held a null snapshot forever: every
later `service.OnLocalNotificationDeliveredEvent += handler` mutated the
service's own field while the host kept invoking null. On device, delivered
and expired callbacks never reached a consumer.

Both are now forwarded through a lambda, so subscribers attached after
construction are reached.

Adds two PlayMode tests. The expired one drives the real raise site
(Mode=Queue + a past DeliveryTime, expired by the host's Update); the
delivered one raises through the host's public field, because
OnNotificationReceived is platform-driven and the Editor has no
IGameNotificationsPlatform.

RCR STATUS: PENDING — neither mutation has been observed red. The Unity
project lock is owned by a central verifier, so no test runner was invoked
for this change. Both tests carry `// RCR: PENDING` comments naming the exact
one-line revert; they are not trusted coverage until that run happens.

RCR: OnLocalNotificationDeliveredEvent_SubscribedAfterCtor_ReachesSubscriber <- MobileNotificationService.cs ctor, restore `= OnLocalNotificationDeliveredEvent` (PENDING)
RCR: OnLocalNotificationExpiredEvent_SubscribedAfterCtor_ReachesSubscriber <- MobileNotificationService.cs ctor, restore `= OnLocalNotificationExpiredEvent` (PENDING)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deleted (A3 - the assertion reads no value this package computed):
- AlertButton_FieldRoundTrip: AlertButton is a three-public-field struct with no logic.
- ImplementsInterface: a class declaration; removing it is a compile error, not a red.
- AllProperties_RoundTripGettersAndSetters and Scheduled_DefaultsFalse: bare auto-properties,
  and Scheduled is get-only and never assigned in Runtime/.
- Reschedule_DefaultsFalse: a plain public field with no writer in Runtime/.
- IsPlaying_FalseInitially's sibling set retained; see below.

KEPT: Dispose_DoesNotThrow_AndPendingColdStartLinkIsNull, classified A5 but never probed.
Across the repo, 7 of 10 probed A5 candidates turned out to have unique pins, so unprobed
A5 claims are not acted on.

EditMode 806/806, PlayMode 295/295.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both mutations were run and observed red; the comments no longer say PENDING.

Batchmode PlayMode, filtered to MobileNotificationServiceTest (12 tests):
  baseline          12 passed  0 failed
  mutant-delivered  11 passed  1 failed  <- delivered test only
  mutant-expired    11 passed  1 failed  <- expired test only
  restored          12 passed  0 failed

Each test reddens on its own ctor line and stays green under the other's
mutation, so neither is an A5 duplicate of the other. Both failed on the
assertion (Expected: same as PendingNotification, But was: null), not by
NullReferenceException, so this is the test asserting rather than crashing.

Batchmode only. Neither test touches URP, Addressables or imported samples,
so no Editor-half divergence is expected, but the Editor half of
Tools/test-all.sh has not been run.

RCR: OnLocalNotificationDeliveredEvent_SubscribedAfterCtor_ReachesSubscriber <- MobileNotificationService.cs ctor, restore `= OnLocalNotificationDeliveredEvent` -> RED
RCR: OnLocalNotificationExpiredEvent_SubscribedAfterCtor_ReachesSubscriber <- MobileNotificationService.cs ctor, restore `= OnLocalNotificationExpiredEvent` -> RED

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CoderGamester and others added 22 commits August 4, 2026 00:02
…it proved

First trustworthy coverage figure for this repo. Regenerated with -debugCodeOptimization,
all 11 GameLovers assemblies in scope, test and sample assemblies excluded. Repo-wide
runtime coverage is 74.1% (6609/8922).

Do not compare against any earlier number. 41.8% was stale, wrongly scoped to 6 assemblies,
and diluted by Editor code; 38.3% was compiled in Release, which silently shrank the
denominator ~40%. The register now names the sanity check that catches a repeat: MathfloatP
must report ~1002 coverable lines, not 637.

The OPEN rows added here are findings the mutation pass PROVED rather than suspected - each
one is a mutation that was applied and observed leaving its test green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r statics

The four NativeUiServiceInstance tests asserted only DoesNotThrow; they now assert the
forwarding to the static service, and each reddens in isolation.

Two fixes avoid introducing a NEW A6 coupling while removing an old one:
- Dispose_UnregistersAllHostHandlers compares against the value captured before driving the
  host, rather than asserting the bare-editor default of EditorLowPowerModeOverride.
- DefaultCtor_RegistersOnSharedHost (renamed) sets EditorLowPowerModeOverride = false itself
  before constructing, and resets it in a finally. That static is a live section 13 OPEN row;
  the test now establishes the state instead of inheriting it.

BatteryServiceTest gains #if UNITY_EDITOR guards: the PlayMode asmdef has includePlatforms: [],
so it compiles for players where EditorLowPowerModeOverride does not exist. The non-editor
branch uses Assert.Ignore rather than silently passing.

RCR: ShowAlertPopUp/ShowToastMessage/RequestReview/Share_ForwardsToStaticService <- NativeUiServiceInstance.cs each forwarding call (RED, all isolated)
RCR: OnPermissionResult_UnknownId_NoOp <- PermissionsCallbackReceiver.cs resolve the wrong pending TCS (RED, isolated)
RCR: Dispose_UnregistersAllHostHandlers <- BatteryService.cs Dispose (RED, isolated)
RCR: DefaultCtor_RegistersOnSharedHost <- BatteryService.cs parameterless ctor (RED, isolated)

EditMode 806/806, PlayMode 295/295.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e leakage row

Ctor_CapturesInitialLevelStatusAndLowPowerMode asserted IsFalse(IsLowPowerMode) while reading
BatteryService.EditorLowPowerModeOverride unpinned. It passed only because the two sibling tests
that drive the static restore it to false in their finally blocks - an ordering accident, and the
A6 pattern of inheriting ambient state rather than establishing it. [SetUp] now pins it false and
[TearDown] resets it.

That was the last piece of the process-wide editor-static leakage row: NativeUiServiceInstanceTest
already nulls NativeUiService.EditorRequestReviewOverride in [SetUp] (9b870e3). The
PermissionsService.EditorCheckOverride / AttService.EditorCurrentStatusOverride half remains OPEN
in its own row - those three fixtures are untouched.

Editor PlayMode 295/295, including all three BatteryServiceTest cases.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
§13 asserted every named symbol was 'either ACCEPTED or OPEN' while four packages had
already grown CLOSED rows — the spec forbade rows it contained. CLOSED is now first-class,
and it carries a contract: name the commit AND the observation, including the environment
the observation came from. A row closed on 'the fix landed' is still OPEN, because the fix
is the edit and the closure is the evidence.

Second rule: closing a row means re-deriving its claim against current source, never
reading the commit that claimed to fix it. A partial fix and a complete one produce the
same green suite and the same confident commit message, so the commit cannot be evidence
for its own completeness.

§1 and §2 are shared verbatim across all six packages; §13's preamble is too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CS0618: FindObjectsByType<T>(FindObjectsSortMode) is deprecated. ResolveHost
filters by reference equality, so ordering is irrelevant and the parameterless
overload is a direct replacement.

Surfaced by taking warning counts from a -runTests log rather than a plain editor
open: test assemblies carry defineConstraints UNITY_INCLUDE_TESTS and are not
compiled by a normal open, so warnings in test code never appeared.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… unowned-edit measurement

Tests/AGENTS.md section 2 said a test with no // RCR: line is a suspect by default, without
carving out Smoke/. Section 1 already exempts that directory (its defect class is "the assembly
no longer loads", which has no one-line mutation), so the omission flagged those fixtures
forever. Exemption is now explicit, on the same directory basis.

Also records that "unannotated" is three states, not one: observed RED with the write-back
lost, seen reddening only as collateral, or never probed. Only the last needs a probe, and
prepared annotation text must never be written without a matching RED-OK - it exists for tests
that were never probed, and writing it fabricates a verified claim.

Adds a section 13 row for the measured count of production edits that redden only collaterally
(223 repo-wide, from .test-all/rcr/unowned-edits.json). Recorded with the caveat that it is NOT
that many missing tests: for foundational primitives and the UiService integration hub, having
no isolated owner follows from centrality, not neglect.
Mechanical pass, no behaviour change.

- Converted 23 enum members to inline `//` comments (HapticPreset,
  NotificationStyle, PrivacyMode, OperatingMode), per §6.6's rule that enum
  values never carry `/// <summary>`. OperatingMode's members had multi-sentence
  summaries that would have become 250-character lines, so their caveats — badge
  auto-increment only when nothing sets one explicitly, and
  RescheduleAfterClearing being meaningful only alongside ClearOnForegrounding —
  moved to a <remarks> on the enum instead.
- Removed the doc comments from 21 constructors and from the private
  ActiveGesture.accumulatedNormalized field.
- Converted 7 private-member doc blocks to `//` comments rather than deleting
  them, keeping their rationale where §6.6 forbids `///`.
- Dropped <param>/<returns>/<exception> from the internal
  IGameNotificationsPlatform and ActiveGesture.SubmitPoint. The <exception> tags
  on both ScheduleNotification overloads documented real behaviour, so that is
  folded into their <summary> rather than lost.
- Added `/// <inheritdoc />` to the three MobileServicesDeviceSimulatorPlugin
  overrides (external Unity base).
- Moved the internal editor/test seams above their types' private blocks (§6.6:
  `internal` is never interleaved with `private`):
  SafeAreaService.SimulateSafeAreaChanged, DeepLinkService.SimulateLinkActivated,
  BatteryService.SimulateLowPowerModeChanged, and MockBuilders'
  BuildReviewPrompt / BuildPermissionDialog / BuildNotificationBanner.

Verified: Tools/style-audit.py reports 0 for every mechanical rule class
(A/B/C/D/K/N) across all six packages. For each reordered file the sorted
non-blank line multiset is unchanged against HEAD, proving the moves are pure
reorders; `#if`/`#endif` counts stay balanced; and every file's UTF-8 BOM still
matches HEAD.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two sentences, neither earning its place on a private nested class: the first
restated what `OverlayController` already says, and the second ("Same renderer
surface the standalone window used to provide") was change narration about a
window removed in the 1.0.0 consolidation — which §6.6's Code comments rule
forbids outright.

§6.6 now states that a comment on a private member is a last resort and that
removing a `///` block is not a licence to leave a `//` in its place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes this package — Tools/style-audit.py reports 0 items.

Documented the NotificationBuilder fluent setters (which are iOS-only, and that
an explicit BadgeNumber opts the notification out of the auto-increment path),
the MobileSimulatorState payload types / events / push entry points, the
MockBuilders factories, the DeviceServicesHost unregister pairs, the
SerializableNotification round-trip and its nullable value+flag flattening, plus
TapInput, AlertButton / AlertButtonStyle, ProjectScanResult.ReferencedPermissions
and the two callback-receiver singletons.

The three UnitySendMessage entry points — OnIosLowPowerModeChanged,
OnPermissionResult, OnAttResult — now state in their summaries that they must stay
public and keep their exact names, because the iOS bridges in Plugins/iOS/*.m
dispatch them by string. That constraint previously lived only in a `//` comment
on the member, invisible to anyone reading the API.

Six single-line summaries on public Runtime methods were expanded to the block
form §6.6 requires there; the ones on internal and Editor members stay compact.

Verified: batchmode green — EditMode 805/805, PlayMode 295/295, and 0 CS warnings
in the -runTests log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A docs pass rewrote the file with LF, changing every historical byte and
breaking the release-notes validator's baseline comparison. Restores the
committed convention per AGENTS.md; no content change.
Unity's packer uses .gitignore as its pack-ignore list, so listing .github/
drops the CI workflow from the tarball while git keeps tracking it (gitignore
does not untrack existing files). Verified on a real clone: 434 -> 433 entries,
.github 1 -> 0, Runtime unchanged.
Unify the four samples into one InputForUI-based bundle, move sample-specific editor workflows into the imported sample, and retain production native build integration in the package.

Harden service and gesture lifecycles, connect notification simulation to the sample-owned service, refresh documentation, and expand regression coverage.

RCR: Ctor_NullConfigure_Throws ← DeepLinkRouter constructor null guard
RCR: Ctor_ConfiguresBeforeSubscription ← DeepLinkRouter constructor subscription order
RCR: Ctor_ConfigurationFailure_DoesNotSubscribe ← DeepLinkRouter constructor failure cleanup
RCR: Dispose_IsIdempotentAndGuardsOperations ← DeepLinkRouter ThrowIfDisposed
RCR: Dispose_DisposesEachDistinctDisposableInstance_Once ← MobileService DisposeChild calls
RCR: Dispose_SharedDisposableChild_DisposesOnce ← MobileService reference identity guard
RCR: ScreenPixelsToPanelUnits_ScaledPanel_ConvertsInsetToPanelCoordinates ← SafeAreaContainer conversion
RCR: OnDisable_AfterEnhancedTouchIsReleased_DoesNotAccessCallbacks ← Smoke lifecycle exemption
RCR: OnDisable_WithAnotherControllerEnabled_KeepsEnhancedTouchSupportEnabled ← GestureController balanced Disable
RCR: OnPressed_DuplicateFingerDown_DoesNotThrowAndReportsBothPresses ← GestureController OnPressed replacement
RCR: Dispose_StopsOutput_DestroysHost_AndIsIdempotent ← HapticsService Dispose
RCR: ScheduleNotification_InEditor_AddsReturnedPendingToCollection ← MobileNotificationService scheduling
RCR: TrySimulateDelivery_RemovesPendingAndRaisesDeliveredEventOnce ← MobileNotificationService simulation
RCR: Dispose_DestroysOwnHost_AndIsIdempotent ← MobileNotificationService Dispose
RCR: PublicOperations_AfterDispose_ThrowObjectDisposedException ← MobileNotificationService ThrowIfDisposed
RCR: CancelNotification_RemovesPendingNotificationWithoutNativeBackend ← GameNotificationsMonoBehaviour cancellation
RCR: CancelAllScheduledNotifications_ClearsPendingNotificationsWithoutNativeBackend ← GameNotificationsMonoBehaviour cancel all
RCR: SetSafeAreaService_ScaledPanel_UsesPanelCoordinatesForScreenInset ← SafeAreaContainer Apply
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant