{"total_count":6,"incomplete_results":false,"items":[{"id":4418883920,"number":71,"state":"open","locked":false,"title":"Phase 3 — Polish: screenshot attachment, breadcrumbs, throttling","body":"Parent: #68\nDepends on: #70\n\n## Goal\n\nOptional polish layered on top of Phase 2. Each item is independently deferrable — if Phase 2 ships clean and feedback quality is sufficient, this phase can stay open indefinitely.\n\n## Deliverables\n\n### Optional screenshot attachment\n\n- New `Switch` on `ReportBugScreen`: \"Attach screenshot of the previous screen\" (default off).\n- When enabled at submit time, capture the screenshot taken when the user opened the report screen (cached in VM, **not** re-captured on submit, so the report screen itself is never in the image).\n- Attached via `Sentry.captureFeedback`'s attachments API.\n- **Never** capture silently — opt-in per submission, no global setting.\n\n### Navigation breadcrumbs\n\n- Lightweight breadcrumb attached on each Nav3 destination change: screen name only, **no arguments** (avoid leaking vehicle IDs or record IDs).\n- Implemented as a `NavController` listener installed in `AppNavGraph` when `SentryAndroid.isEnabled()`.\n- Capped at the last 20 entries via Sentry's default breadcrumb buffer.\n\n### Client-side throttle\n\n- Max 1 successful report per 60 seconds, persisted in DataStore as a timestamp.\n- On submit attempt within the window, show snackbar \"Please wait a moment before sending another report\" and do not call Sentry.\n- Purely a courtesy throttle — not a security boundary (Sentry's per-DSN rate limit is the real defense).\n\n### Tests\n\n- VM tests for throttle window (just-before / just-after boundary, persistence across VM recreation).\n- Robolectric test: nav breadcrumb listener installed only when telemetry enabled.\n- Roborazzi: report screen with screenshot toggle visible + checked.\n\n## Out of scope\n\n- Anonymous user IDs, session tracking.\n- Performance / replay capture.\n- Server-side ingestion proxy.\n\n## Acceptance\n\n- [ ] Screenshot opt-in works end-to-end; submitted report in Sentry shows the attached image.\n- [ ] Breadcrumbs visible on a crash report; no IDs or PII leak into breadcrumb data.\n- [ ] Throttle correctly blocks rapid resubmissions.\n- [ ] All tests pass; `./gradlew test ktlintCheck detekt lintDebug verifyRoborazziDebug` clean.","author_association":"OWNER","user":{"login":"Mesya82","id":32867735,"node_id":"MDQ6VXNlcjMyODY3NzM1","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4","html_url":"https://github.com/Mesya82","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/Mesya82","events_url":"https://api.github.com/users/Mesya82/events{/privacy}","following_url":"https://api.github.com/users/Mesya82/following{/other_user}","followers_url":"https://api.github.com/users/Mesya82/followers","gists_url":"https://api.github.com/users/Mesya82/gists{/gist_id}","organizations_url":"https://api.github.com/users/Mesya82/orgs","received_events_url":"https://api.github.com/users/Mesya82/received_events","repos_url":"https://api.github.com/users/Mesya82/repos","starred_url":"https://api.github.com/users/Mesya82/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Mesya82/subscriptions"},"labels":[{"id":10572864083,"url":"https://api.github.com/repos/Mesya82/Service-Book/labels/enhancement","name":"enhancement","color":"a2eeef","description":"New feature or request","default":true,"node_id":"LA_kwDOR2Hmq88AAAACdjEaUw"}],"comments":0,"created_at":"2026-05-11T07:26:53Z","updated_at":"2026-05-11T07:26:53Z","url":"https://api.github.com/repos/Mesya82/Service-Book/issues/71","html_url":"https://github.com/Mesya82/Service-Book/issues/71","comments_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/71/comments","events_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/71/events","labels_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/71/labels{/name}","repository_url":"https://api.github.com/repos/Mesya82/Service-Book","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0,"url":"https://api.github.com/repos/Mesya82/Service-Book/issues/71/reactions"},"node_id":"I_kwDOR2Hmq88AAAABB2LRUA"},{"id":4418882555,"number":70,"state":"open","locked":false,"title":"Phase 2 — Report a bug screen + Sentry User Feedback","body":"Parent: #68\nDepends on: #69\n\n## Goal\n\nAdd the user-facing \"Report a bug or suggest a feature\" screen and submit reports via Sentry's User Feedback API. Strictly Material 3 Expressive.\n\n## Deliverables\n\n### Navigation\n\n- `@Serializable object ReportBug : NavKey` in `ui/navigation/Screen.kt`.\n- Wire entry into `AppNavGraph`.\n\n### Settings entry\n\n- New `ListItem` in `SettingsScreen` titled \"Report a bug or suggest a feature\" with a `BugReport` icon, routing to `ReportBug`.\n- **Always visible** regardless of telemetry opt-in — submitting a report is its own explicit consent.\n\n### Screen\n\n`ui/reportbug/ReportBugScreen.kt` + `ReportBugViewModel.kt` following the existing event-driven VM pattern (`onEvent` sealed events, `StateFlow` with no public setter, internal logic reads `_state.value`).\n\n**Form fields:**\n- **Type**: Material 3 Expressive **connected `ButtonGroup`** — `Bug` / `Suggestion`, default `Bug`.\n- **Summary**: single-line `OutlinedTextField`, required, max 80 chars, character counter, IME `Next`.\n- **Description**: multi-line `OutlinedTextField` (min 4 lines visible), required, max 2000 chars, character counter.\n- **Contact email** (optional): `OutlinedTextField`, validated as email shape, IME `Done`.\n- **Include device info**: `SwitchListItem`, default on. Expandable Expressive `Card` shows the literal payload: app version, Android version, locale, device model, vault-configured flag.\n\n**Chrome:**\n- `MediumFlexibleTopAppBar` titled \"Report a bug\" with back action, scroll behavior.\n- Bottom button row inside a `Surface`: `TextButton` Cancel + filled `Button` Submit (Expressive `extraLarge` shape).\n\n**Submit flow:**\n- Disable Submit while either required field empty.\n- Tap Submit → state becomes `Submitting`, show Material 3 Expressive `LoadingIndicator` overlay.\n- VM constructs a `SentryFeedback` with `name` (optional, empty), `contactEmail` (if provided), `message` (summary + \"\\n\\n\" + description), and tags `{type, app_version, android_version, locale, device_model, vault_configured}`.\n- If `SentryAndroid.isEnabled()`, call `Sentry.captureFeedback(...)`. Otherwise, lazily init Sentry with the configured DSN, send, then close — the user's persistent telemetry preference is **not** changed.\n- Success → snackbar \"Thanks — report sent\" + `popBackStack()`.\n- Failure → inline error with retry. After 2 failed retries, fall back to `ACTION_SENDTO` mailto intent with the same payload pre-filled to a configured email address.\n\n### Tests\n\n- `ReportBugViewModelTest` (JVM): validation rules, submit state transitions, success, error+retry, mailto fallback after exhaustion. Telemetry-off lazy-init path verified via a `SentryWrapper` seam.\n- `ReportBugScreenTest` (Robolectric): renders all fields, type toggle works, submit disabled until valid, submit dispatches event, loading state.\n- Roborazzi: empty form, filled form, expanded device-info card, loading, error state.\n\n### Smoke test amendment\n\nAdd one scenario to `docs/plan-smoke-tests.md` and implement in `src/androidTest`:\n\n- **S22: Report a bug entry — Settings → screen** — From Settings, tap \"Report a bug or suggest a feature\"; assert `ReportBugScreen` renders with type toggle, Summary and Description fields visible, and Submit disabled. Tap back; assert return to Settings. Protects the Settings entry + Nav3 wiring; does **not** exercise actual Sentry submission (kept out of smoke to avoid network coupling).\n\n### Material 3 Expressive checklist\n\n- `MaterialExpressiveTheme` wrapper used by the screen.\n- `MediumFlexibleTopAppBar` with `pinnedScrollBehavior`.\n- Connected `ButtonGroup` for type.\n- `LoadingIndicator` (Expressive) — **not** `CircularProgressIndicator`.\n- Spring motion (`MotionScheme.expressive()` defaults) for content transitions.\n- Shape tokens: `largeIncreased` cards, `extraLarge` submit button.\n\n## Out of scope\n\n- Screenshot attachment, breadcrumbs, throttling (Phase 3).\n- Changing `crashReportingEnabled` from this screen (handled in Settings/Setup, Phase 1).\n- Submit-path coverage in smoke (S22 covers entry + rendering only; full submit flow is covered by Phase 2's local test suite).\n\n## Acceptance\n\n- [ ] A submitted report appears in the Sentry project with the expected tags, regardless of whether telemetry is opted-in.\n- [ ] Disabling network and submitting eventually falls back to a mailto intent with the full payload pre-filled.\n- [ ] S22 added to `docs/plan-smoke-tests.md` and passes on `pixel2api33Check`.\n- [ ] All new tests pass; `./gradlew test ktlintCheck detekt lintDebug verifyRoborazziDebug` clean.\n- [ ] Screen renders correctly in both light/dark and LTR/RTL (Hebrew).","author_association":"OWNER","user":{"login":"Mesya82","id":32867735,"node_id":"MDQ6VXNlcjMyODY3NzM1","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4","html_url":"https://github.com/Mesya82","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/Mesya82","events_url":"https://api.github.com/users/Mesya82/events{/privacy}","following_url":"https://api.github.com/users/Mesya82/following{/other_user}","followers_url":"https://api.github.com/users/Mesya82/followers","gists_url":"https://api.github.com/users/Mesya82/gists{/gist_id}","organizations_url":"https://api.github.com/users/Mesya82/orgs","received_events_url":"https://api.github.com/users/Mesya82/received_events","repos_url":"https://api.github.com/users/Mesya82/repos","starred_url":"https://api.github.com/users/Mesya82/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Mesya82/subscriptions"},"labels":[{"id":10572864083,"url":"https://api.github.com/repos/Mesya82/Service-Book/labels/enhancement","name":"enhancement","color":"a2eeef","description":"New feature or request","default":true,"node_id":"LA_kwDOR2Hmq88AAAACdjEaUw"}],"comments":1,"created_at":"2026-05-11T07:26:39Z","updated_at":"2026-05-11T07:41:08Z","url":"https://api.github.com/repos/Mesya82/Service-Book/issues/70","html_url":"https://github.com/Mesya82/Service-Book/issues/70","comments_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/70/comments","events_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/70/events","labels_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/70/labels{/name}","repository_url":"https://api.github.com/repos/Mesya82/Service-Book","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0,"url":"https://api.github.com/repos/Mesya82/Service-Book/issues/70/reactions"},"node_id":"I_kwDOR2Hmq88AAAABB2LL-w"},{"id":4418877641,"number":68,"state":"open","locked":false,"title":"Feature: Report a bug / Suggest a feature (Sentry-backed)","body":"## Goal\n\nLet users report bugs and suggest improvements from inside the app, and automatically capture crashes/ANRs — both routed to Sentry. Opt-in by default to match the app's local-first, privacy-respecting ethos. UI strictly adheres to Material 3 Expressive guidelines.\n\n## Architecture summary\n\n- `SentryAndroid.init` is gated by a `crashReportingEnabled` user preference (default **off**). No telemetry leaves the device until the user explicitly opts in.\n- DSN injected via `BuildConfig` from `local.properties` (gitignored) — separate DSNs for debug and release.\n- New `ReportBugScreen` (`ui/reportbug/`) hosts a Material 3 Expressive form. Submission uses `Sentry.captureFeedback`; if telemetry is disabled, Sentry is briefly initialized for the one-shot send, then shut down. If Sentry submission fails after retries, the form falls back to a `mailto:` intent pre-filled with the same payload.\n- Spike protection + per-DSN rate limits enabled in the Sentry project to bound abuse risk (documented in `docs/sentry-setup.md`).\n- No anonymous user IDs, no session/performance/replay capture — errors only.\n\n## Phases\n\nEach phase is a self-contained, individually-reviewable chunk. Phases must merge in order; Phase 3 is optional polish.\n\n- [ ] #69 Phase 1 — Sentry foundation: SDK, opt-in pref, Setup/Settings toggle\n- [ ] #70 Phase 2 — Report a bug screen + Sentry User Feedback\n- [ ] #71 Phase 3 — Polish: screenshot attachment, breadcrumbs, throttling (optional)\n\n## Material 3 Expressive adherence\n\n- `MaterialExpressiveTheme` wrapping for new screens\n- `MediumFlexibleTopAppBar` with scroll behavior\n- Connected `ButtonGroup` for Bug/Suggestion type toggle\n- Expressive `LoadingIndicator` (not `CircularProgressIndicator`) for in-flight submits\n- Spring-based motion specs for content transitions\n- Expressive shape tokens (`largeIncreased` for cards, `extraLarge` for primary submit button)\n\n## Out of scope\n\n- Anonymous user IDs / session tracking\n- Performance, tracing, or session replay capture\n- Server-side ingestion proxy (revisit only if DSN abuse becomes a real problem in practice)\n- iOS / Web\n\n## Acceptance\n\nThis umbrella closes when Phases 1–2 are merged (Phase 3 may remain open), a real bug report submitted from a debug build appears in the Sentry project, and an intentional crash in a debug build is captured with stack trace + device context.","author_association":"OWNER","user":{"login":"Mesya82","id":32867735,"node_id":"MDQ6VXNlcjMyODY3NzM1","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4","html_url":"https://github.com/Mesya82","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/Mesya82","events_url":"https://api.github.com/users/Mesya82/events{/privacy}","following_url":"https://api.github.com/users/Mesya82/following{/other_user}","followers_url":"https://api.github.com/users/Mesya82/followers","gists_url":"https://api.github.com/users/Mesya82/gists{/gist_id}","organizations_url":"https://api.github.com/users/Mesya82/orgs","received_events_url":"https://api.github.com/users/Mesya82/received_events","repos_url":"https://api.github.com/users/Mesya82/repos","starred_url":"https://api.github.com/users/Mesya82/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Mesya82/subscriptions"},"labels":[{"id":10572864083,"url":"https://api.github.com/repos/Mesya82/Service-Book/labels/enhancement","name":"enhancement","color":"a2eeef","description":"New feature or request","default":true,"node_id":"LA_kwDOR2Hmq88AAAACdjEaUw"}],"comments":0,"created_at":"2026-05-11T07:25:56Z","updated_at":"2026-05-11T07:27:07Z","url":"https://api.github.com/repos/Mesya82/Service-Book/issues/68","html_url":"https://github.com/Mesya82/Service-Book/issues/68","comments_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/68/comments","events_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/68/events","labels_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/68/labels{/name}","repository_url":"https://api.github.com/repos/Mesya82/Service-Book","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0,"url":"https://api.github.com/repos/Mesya82/Service-Book/issues/68/reactions"},"node_id":"I_kwDOR2Hmq88AAAABB2K4yQ"},{"id":4341066553,"number":42,"state":"open","locked":false,"title":"Phase 7 — On-device verification + Google Play setup","body":"Part of #35.\n\n## Goal\n\nValidate end-to-end on a real phone. No code changes — manual run-through plus the one-time Play Console setup that's a prerequisite for real billing testing.\n\n## Tracks\n\n### Track A — Fast loop (debug override)\nRun on every `installDebug`. Verifies free / paid / upgrade paths and read-only mode using the developer panel; no Play involvement.\n\n### Track B — Real Play Billing (once per release)\n**One-time Play Console setup**\n- [ ] App entry created; applicationId matches `app/build.gradle.kts` namespace\n- [ ] In-app product `premium_lifetime` (Non-consumable, Active)\n- [ ] License-tester account added under Setup → License testing\n- [ ] Release-signed AAB uploaded to *Internal testing* track\n- [ ] Test account added to internal testers; opt-in URL accepted on test phone\n\n**End-to-end run-through**\n- [ ] Real purchase flow completes; premium unlocks; banner clears\n- [ ] Restore after uninstall/reinstall returns `Restored`\n- [ ] Refund flow (Play Console → Order management → cancel) flips `isPremium` back on next `onResume` refresh\n- [ ] PENDING test (slow test card): premium does **not** flash to true during pending window\n\n### Track C — Inspection helpers (reference)\nadb commands to inspect cached state and reset app state. See `docs/plan-paywall.md` § Phase 7 Track C.\n\n## Acceptance\n\n- [ ] All Track A flows pass on a real device\n- [ ] Track B end-to-end purchase + restore + refund cycle pass on a license-tester device\n- [ ] Track B PENDING test confirms no premature `isPremium` flip\n- [ ] Umbrella issue #35 ready to close\n\n## Depends on\n\nPhases 1–6.\n\n## Reference\n\n`docs/plan-paywall.md` § Phase 7.\n","author_association":"OWNER","user":{"login":"Mesya82","id":32867735,"node_id":"MDQ6VXNlcjMyODY3NzM1","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4","html_url":"https://github.com/Mesya82","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/Mesya82","events_url":"https://api.github.com/users/Mesya82/events{/privacy}","following_url":"https://api.github.com/users/Mesya82/following{/other_user}","followers_url":"https://api.github.com/users/Mesya82/followers","gists_url":"https://api.github.com/users/Mesya82/gists{/gist_id}","organizations_url":"https://api.github.com/users/Mesya82/orgs","received_events_url":"https://api.github.com/users/Mesya82/received_events","repos_url":"https://api.github.com/users/Mesya82/repos","starred_url":"https://api.github.com/users/Mesya82/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Mesya82/subscriptions"},"labels":[{"id":10572864083,"url":"https://api.github.com/repos/Mesya82/Service-Book/labels/enhancement","name":"enhancement","color":"a2eeef","description":"New feature or request","default":true,"node_id":"LA_kwDOR2Hmq88AAAACdjEaUw"}],"comments":0,"created_at":"2026-04-28T06:28:01Z","updated_at":"2026-04-28T06:28:01Z","url":"https://api.github.com/repos/Mesya82/Service-Book/issues/42","html_url":"https://github.com/Mesya82/Service-Book/issues/42","comments_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/42/comments","events_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/42/events","labels_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/42/labels{/name}","repository_url":"https://api.github.com/repos/Mesya82/Service-Book","parent_issue_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/35","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0,"url":"https://api.github.com/repos/Mesya82/Service-Book/issues/42/reactions"},"node_id":"I_kwDOR2Hmq88AAAABAr9rOQ"},{"id":4340949876,"number":35,"state":"open","locked":false,"title":"Feature: Paywall / Premium (1-vehicle free, lifetime upgrade)","body":"## Goal\n\nImplement the freemium paywall: 1 vehicle free with full read/write; unlimited vehicles via a one-time lifetime Google Play purchase (`premium_lifetime`, `INAPP`). Free users with \u003e1 vehicle in their vault enter app-wide read-only mode with a persistent warning banner.\n\n## References\n\n- Spec: `docs/specs.md` §5.7\n- ADR: `docs/adr.md` ADR-012\n- Implementation plan: `docs/plan-paywall.md` (canonical, detailed)\n- Smoke-test amendments: `docs/plan-smoke-tests.md` S18–S20\n\n## Architecture summary\n\n- `BillingManager` wraps Google Play Billing 7.x.\n- `AppGate` derives `isReadOnly` and exposes `isPremium` — single source of truth for every paywalled VM.\n- `PaywallScaffold` hosts the read-only banner — single source of truth for every paywalled screen.\n- DataStore-cached premium status for offline / cold-start resilience.\n- Debug developer panel in Settings overrides billing state at runtime so free / paid / upgrade paths can be tested on a debug APK without round-tripping Google Play.\n\n## Phases\n\nEach phase is a self-contained, individually-reviewable chunk. Phases must merge in order; Phase 6 additionally depends on Phase 3 being live.\n\n- [x] #36 Phase 1 — Foundation: preferences + billing types\n- [x] #37 Phase 2 — BillingManager + Debug Override\n- [ ] #38 Phase 3 — App wiring + AppGate\n- [ ] #39 Phase 4 — ViewModel gating\n- [ ] #40 Phase 5 — UI components + screen integration\n- [ ] #41 Phase 6 — Smoke test coverage (S18–S20)\n- [ ] #42 Phase 7 — On-device verification + Google Play setup\n\n## Out of scope\n\n- Subscriptions, recurring fees, cloud sync, multi-device account-based entitlement.\n- Server-side signature verification (offline-first, single non-consumable).\n- iOS / Web (post-MVP).\n\n## Acceptance\n\nThis umbrella issue closes when all 7 phase sub-issues are closed and the on-device verification (Phase 7 Tracks A and B) passes on a real license-tester device.\n","author_association":"OWNER","user":{"login":"Mesya82","id":32867735,"node_id":"MDQ6VXNlcjMyODY3NzM1","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4","html_url":"https://github.com/Mesya82","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/Mesya82","events_url":"https://api.github.com/users/Mesya82/events{/privacy}","following_url":"https://api.github.com/users/Mesya82/following{/other_user}","followers_url":"https://api.github.com/users/Mesya82/followers","gists_url":"https://api.github.com/users/Mesya82/gists{/gist_id}","organizations_url":"https://api.github.com/users/Mesya82/orgs","received_events_url":"https://api.github.com/users/Mesya82/received_events","repos_url":"https://api.github.com/users/Mesya82/repos","starred_url":"https://api.github.com/users/Mesya82/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Mesya82/subscriptions"},"labels":[{"id":10572864083,"url":"https://api.github.com/repos/Mesya82/Service-Book/labels/enhancement","name":"enhancement","color":"a2eeef","description":"New feature or request","default":true,"node_id":"LA_kwDOR2Hmq88AAAACdjEaUw"}],"comments":0,"created_at":"2026-04-28T06:01:51Z","updated_at":"2026-04-29T10:46:42Z","url":"https://api.github.com/repos/Mesya82/Service-Book/issues/35","html_url":"https://github.com/Mesya82/Service-Book/issues/35","comments_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/35/comments","events_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/35/events","labels_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/35/labels{/name}","repository_url":"https://api.github.com/repos/Mesya82/Service-Book","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0,"url":"https://api.github.com/repos/Mesya82/Service-Book/issues/35/reactions"},"node_id":"I_kwDOR2Hmq88AAAABAr2jdA"},{"id":4333489940,"number":21,"state":"open","locked":false,"title":"Prep work: flip repo from public to private (Free plan)","body":"## Goal\nPrepare repo for public→private migration on a **Free** GitHub plan, replacing free-on-public features with free alternatives.\n\n## Phase 1 — Pre-flip audit (do before changing visibility)\n- [x] Secrets audit: `gitleaks detect --log-opts=\"--all\"`; review history for `*.jks`, `local.properties`, `play-config.json`, `google-services.json`\n- [x] Decide on Play Store key + service account rotation (assume leaked? rotate or accept)\n- [x] List existing forks via `gh api repos/{owner}/{repo}/forks`; decide outreach vs. accept-snapshot\n- [x] Audit raw GitHub links in `README.md`, `GEMINI.md`, `docs/**`, `TESTING_SETUP.md` (will 404 for non-collaborators after flip)\n\n## Phase 2 — Replace paid-on-private features\n- [x] **Replace CodeQL with semgrep** (CodeQL on private = GHAS-only, ~$49/committer/mo)\n - [x] Delete `.github/workflows/codeql.yml`\n - [x] Add `.github/workflows/semgrep.yml` using `semgrep/semgrep-action@v1` with `p/default` + `p/kotlin` + `p/owasp-top-ten`; PR + weekly cron; SARIF as artifact\n- [x] **Smoke test trigger redesign**\n - [x] Move smoke job to its own workflow `.github/workflows/smoke.yml`\n - [x] Triggers: `push: master` + `workflow_dispatch` + `pull_request` with `paths:` filter on `app/src/androidTest/**`, `app/src/main/**/ui/**`, `app/build.gradle.kts`\n - [x] Add `concurrency: { group: smoke-${{ github.ref }}, cancel-in-progress: true }`\n - [x] Optional: `[smoke]` label trigger for cases path filter misses\n- [x] **Coverage strategy** (LFS-stored baseline + fresh unit coverage on PR)\n - [x] Surface the existing `./gradlew generateGmdCoverage` runbook in `TESTING_SETUP.md` (already implemented at `app/build.gradle.kts:135-145`); add the commit step (`git add app/coverage-baselines/gmd_smoke.ec \u0026\u0026 git commit -m \"chore: refresh smoke coverage baseline\"`)\n - [x] Drop `lfs: true` from non-coverage-consuming jobs (`lint`, `build` in `ci.yml`) — only the test job needs the baseline; saves LFS bandwidth (Free cap = 1 GB/mo)\n - [x] Add stale-baseline warning check (warn-only) on PRs: compare `git log` of `app/src/main/**` against the baseline's last commit; add a checkbox in the PR template to acknowledge (Implemented via `baseline-reminder` bot comment)\n\n## Phase 3 — Free-on-private hardening (do these anyway)\n- [x] Add `concurrency: cancel-in-progress` to `ci.yml` to recover minutes from rapid PR pushes\n- [x] Add `.github/dependabot.yml` (gradle + github-actions ecosystems)\n- [x] Configure branch protection on `master` (require `ci-success`, ≥1 review) — free on private since 2023\n- [x] Enable secret scanning + push protection in repo settings (free on private)\n\n## Phase 4 — Flip + verify\n- [x] Change repo visibility to private\n- [x] Verify CI still green on next PR\n- [x] Verify Play Store deploy workflow still runs (secrets unchanged)\n- [ ] After first month: confirm Actions minutes burn ≤ ~500 min (Free cap = 2,000)\n- [ ] After first month: confirm LFS bandwidth ≤ 500 MB/mo (Free cap = 1 GB)\n\n## Out of scope / decided against\n- **GitHub Advanced Security** (CodeQL on private) — too expensive\n- **Codecov** — local merge of `.ec` files is sufficient for now; revisit only if combined-coverage tooling proves brittle\n- **Self-hosted runners** — projected burn (~370 min/mo) fits Free tier comfortably\n\n## Notes / context\n- Public forks of a public repo **detach** and remain public with their snapshot when the source goes private. Going private does not redact what is already in public history — hence the secrets audit.\n- Watchers are preserved but those without read access get auto-unsubscribed.","author_association":"OWNER","user":{"login":"Mesya82","id":32867735,"node_id":"MDQ6VXNlcjMyODY3NzM1","avatar_url":"https://avatars.githubusercontent.com/u/32867735?v=4","html_url":"https://github.com/Mesya82","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/Mesya82","events_url":"https://api.github.com/users/Mesya82/events{/privacy}","following_url":"https://api.github.com/users/Mesya82/following{/other_user}","followers_url":"https://api.github.com/users/Mesya82/followers","gists_url":"https://api.github.com/users/Mesya82/gists{/gist_id}","organizations_url":"https://api.github.com/users/Mesya82/orgs","received_events_url":"https://api.github.com/users/Mesya82/received_events","repos_url":"https://api.github.com/users/Mesya82/repos","starred_url":"https://api.github.com/users/Mesya82/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Mesya82/subscriptions"},"comments":1,"created_at":"2026-04-27T06:17:21Z","updated_at":"2026-05-03T15:32:05Z","url":"https://api.github.com/repos/Mesya82/Service-Book/issues/21","html_url":"https://github.com/Mesya82/Service-Book/issues/21","comments_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/21/comments","events_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/21/events","labels_url":"https://api.github.com/repos/Mesya82/Service-Book/issues/21/labels{/name}","repository_url":"https://api.github.com/repos/Mesya82/Service-Book","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0,"url":"https://api.github.com/repos/Mesya82/Service-Book/issues/21/reactions"},"node_id":"I_kwDOR2Hmq88AAAABAkvPFA"}]}