Skip to content

dev to staging#3393

Draft
aisraelov wants to merge 118 commits into
stagingfrom
dev
Draft

dev to staging#3393
aisraelov wants to merge 118 commits into
stagingfrom
dev

Conversation

@aisraelov

@aisraelov aisraelov commented Jul 10, 2026

Copy link
Copy Markdown
Member

Summary by cubic

Cuts the mobile app over to Cloud V2 first‑party account auth and fully removes Cloud V1 + embedded Supabase, adds report triage with hardened artifact delivery and Slack notifications (with admin deep‑links), embeds request correlation in photo folders and EXIF, and renames the OEM toolkit to the Mentra Engine (@mentra/engine). Also improves Wi‑Fi/hotspot reliability, battery/charging reporting, camera error handling, and the example OEM app with pairing + public dev miniapp side‑loading, plus per‑environment infra updates and OTA/docs.

  • New Features

    • Cloud V2 account auth: /api/account/* (login/signup, PKCE OAuth with deep‑link handoff, password/email change with verification, account deletion), short‑lived subject‑token minting, and per‑IP/email rate limits; devices now hold V2 tokens only; mobile provider adds silent refresh, offline boot with cached profile, cached identity, and in‑app deletion.
    • Mobile cutover: new AccountAuthProvider; Mantle fetches subject tokens; legacy V1 exchange/websocket removed; all remaining V1 wiring and Supabase client deleted; toolkit renamed to Mentra Engine (@mentra/engine); example OEM app adds pairing and engine.dev.loadDevMiniapp(url) for public side‑loading.
    • Capture correlation: sanitized requestId embedded in photo folder names and stamped into EXIF; /api/sync + /api/gallery return request_id; save‑only SDK photos use the gateless button path and include captureId in responses.
    • Admin + Slack: read‑only report triage API/UI with secure artifact delivery (inline allowlist, nosniff, sandbox), deep‑links from Slack to the admin incident view, and capped text to avoid Slack drops; Slack notifications for reports (automatic posts routed to a separate webhook) and miniapp submissions; incident‑logs fetch script now targets the Cloud V2 admin API.
    • Dev/Infra: per‑env UDP NLB template and runbook updates; new cloud-isaiah env + deploy workflow (with UDP NLB manifest); Bluetooth SDK bumped to 0.1.19 (@mentra/bluetooth-sdk), and ASG version name set to the package version; Mentra Live OTA docs updated; engine docs added.
  • Bug Fixes

    • OAuth redirects derive the public https origin from x-forwarded-proto (or x-mentra-public-origin) to satisfy Supabase allowlists; PKCE enforced; verified email‑change flow; robust JWKS when account keys are dark; mentra tenant gate on account endpoints.
    • Wake locks are extend‑only so short acquires can’t cut OTA short; tests added.
    • Wi‑Fi provisioning selects security from scan capabilities (WPA2/WPA3/SAE); BLE status includes failure reasons and wifiError is sticky; hotspot “enabled” gates on real AP readiness with race‑hardened teardown and echo suppression.
    • Battery/charging sourced only from the PMU heartbeat; Android charging state stays in sync on percent‑only updates.
    • Camera errors: structured CameraOperationError threaded through warm‑up/capture while preserving legacy string callbacks; EXIF write order pinned by tests.
    • Native Android SDK errors decoupled from Expo, with centralized translation to prevent crashes.
    • Bluetooth file‑transfer ACK handling fixed (asymmetric BES indices) and window tuned for newer firmware.

Written for commit 61bbbf5. Summary will update on new commits.

Review in cubic

PhilippeFerreiraDeSousa and others added 30 commits July 8, 2026 11:53
Running the example host on hardware surfaced that it was a control
panel with no data source: nothing could register a miniapp, and there
was no pairing UI — and the dev-miniapp registration path (registerDevApp)
was /internal-only, unreachable by a boundary-respecting host.

- toolkit.dev.loadDevMiniapp(url): public side-loading — probes
  <url>/miniapp.json via the already-public decideDevLaunchRoute,
  registers into the single dev slot, refreshes the apps list, returns a
  typed {ok} result. The OEM-host equivalent of the first-party
  developer-URL screen.
- The example app gains a Pair section (markPendingSelection -> scan ->
  tap a found device -> pair, plus reconnect/disconnect, live status row
  from toolkit.glasses.onStatus / pairing.onScanning / pairing.onFound)
  and a Miniapps section (URL field -> loadDevMiniapp -> start/stop).
  Every call goes through the public entry; the two-phase identity
  contract is followed (pair() never saves a default eagerly).
- jest island mock gains the loadDevMiniapp stub for mock parity.

Gates: island tsc + 220 tests; OEM app tsc against rebuilt public types;
expo export bundles clean; mobile tsc + 55 suites / 429 tests.
Bug reports and feedback moved to the Cloud V2 reports service, which
posted nothing to Slack; the V1 feedback path that did is no longer
invoked. Add a fire-and-forget notifier: feedback reports post from
submitReport (ready as submitted), bug/automatic reports post from
markReportReady on the collecting->ready transition with the artifact
count. Webhook comes from CLOUD_REPORTS_SLACK_WEBHOOK_URL; unset is a
silent skip, and errors are logged, never thrown, so the API response
is never delayed. Message mirrors the V1 slack.service.ts conventions.
The incident-system tile in the internal admin console was a placeholder,
and since the Mentra App reporting flow moved to Cloud V2 the only way to
read a submitted bug report or feedback was querying Mongo by hand.

Core gains a read-only admin surface behind the existing adminAuth gate:
GET /api/admin/reports lists reports newest-first (kind/status filters,
context excluded), GET /api/admin/reports/:reportId returns the full
document plus its report_assets rows, and
GET /api/admin/reports/:reportId/artifacts/:artifactId streams artifact
payload bytes from blob storage with the stored content type. The router
is sub-mounted inside the admin router so adminAuth runs exactly once per
request; a second mount at /api/admin would run the sealed-session
authenticate twice and could burn the single-use WorkOS refresh token.
Client filenames are allowlist-sanitized before reaching the
content-disposition header, and a createdAt index backs the list sort.

The admin website replaces the incidents placeholder with a live Reports
page: kind/status filter pills, newest-first list, and a detail drawer
showing report details, device context, inline screenshot previews, and
an expandable log-bundle viewer, all through the same-origin /api proxy.

Integration tests drive the routes end-to-end with a fully local admin
principal: an org API key is not a JWT, so the bearer path falls through
to DB validation without WorkOS, and its synthetic api-key email is
allowlisted via CLOUD_CORE_ADMIN_EMAILS. Covers the 401/403 gate, list
shape and filters, detail with assets, and artifact byte round-trips.
A reporter controls both the bytes and the declared multipart type of a
screenshot upload, so the admin artifact endpoint could be handed HTML or
SVG and render it inline, same-origin with the admin console session.

The upload path now stores the declared type only when it names a
plausible screenshot format (jpeg/png/webp/gif/heic/heif), falling back
to application/octet-stream. The admin artifact route independently
allowlists what it serves inline (non-scriptable images plus JSON),
downloads everything else as an opaque attachment, and stamps nosniff
plus a deny-all sandbox CSP on every payload response. The admin UI only
inlines screenshot previews whose stored type is an image.

Covers both directions with integration tests: a genuine image stays
inline with the hardening headers, and an HTML file claiming to be a
screenshot comes back as an octet-stream attachment.
The shared static CPU/screen wake locks were replaced on every acquire:
each acquireCpuWakeLock() released whatever was held and installed a fresh
lock with the new timeout. So a short acquire (e.g. a 60s camera/util lock,
or the 5-min BES lock) landing during a long operation would shorten the
in-flight deadline. When that shortened lock expires the CPU — and the MTK
SoC — can sleep mid-update, which stalls OTA (BES/MTK transfers fail).

Make acquisition extend-only: track an absolute deadline per lock and, if a
lock is already held whose deadline is at or beyond the requested one, keep
it and ignore the shorter acquire. Only a later deadline swaps the lock. The
timeout still auto-releases as a backstop, and explicit release() clears the
tracked deadline. acquire/release are now synchronized so the deadline check
is race-free across the OTA worker, main looper, and camera/streaming threads.

This is the "longest deadline wins" fix — it stops shorter acquires from
stomping a longer-lived lock without re-plumbing every call site.

Adds WakeLockManagerTest (Robolectric) covering shorter-ignored,
longer-extends, release-then-reacquire, and the screen-lock path.
Slack rejects the entire message when any section text exceeds 2000
chars, silently losing the notification. Trigger source/reason and
other client strings were unbounded, and escaping after truncation
could push even capped behavior text past the limit (500 '<' chars
escape to 2000+). Cap every client-sourced string per field and
hard-cap the escaped result under Slack's limit.
…b8eb7

Notify Slack when Cloud V2 reports are submitted or complete
Automatic watchdog reports shared the feedback channel webhook. Read
CLOUD_REPORTS_SLACK_WEBHOOK_AUTOMATIC_URL for kind automatic, falling
back to CLOUD_REPORTS_SLACK_WEBHOOK_URL when unset — the same routing
V1 used for SLACK_WEBHOOK_AUTOMATIC_INCIDENTS. Bug and feedback
reports keep using the main webhook.
Cloud V2's release lifecycle had no Slack notification on submit, so
once console2 replaces the V1 developer console the team's
#mini-app-submissions channel would silently stop receiving posts
(V1 posts via notifyMiniAppSubmission / SLACK_WEBHOOK_MINI_APP_SUBMISSION).

Mirrors the report-slack.service pattern: webhook from
CLOUD_MINIAPP_SLACK_WEBHOOK_URL, silent skip when unset, errors logged
never thrown, 10s timeout, fire-and-forget from submitRelease.
…utomatic-split

Route automatic report Slack posts to their own webhook
Bugbot review finding: nullish coalescing meant a falsy-but-non-nullish
manifest.type (empty string, or any non-string junk) blocked the
appType fallback. Take the first candidate that is a non-empty string.
Issue 019 plans moving mobile login off Cloud V1: the app currently embeds
supabase-js and relies on the legacy core token; V2's exchange accepts both via
the symmetric mentra-tenant branch. Decision recorded: build the account module
inside packages/core (api/account + services/account) rather than a separate
deployable, dogfooding the OEM path with a mentra-issuer Ed25519 subject token.
Phase 1 moves the surface (core drives Supabase server side), Phase 2 may swap
the credential store. Deliverables gated: spike.md -> spec.md -> design.md.
…lacing)

Records the security/reliability rationale: symmetric shared secrets (any env
can forge any user), three client-glued token systems, credentials at rest on
device + in URLs, no central revocation, third-party baked into the app binary.
Notes scope honesty: 019 fixes auth-chain fragility, not the July ops/env drift.
…ency ledger)

README: the headline goal is a MentraOS build with zero legacy Cloud V1
dependency; login is the enabling first cut, not the whole job.

spike.md first pass: the current login chain (supabase-js client-side ->
legacy /auth/exchange coreToken -> V1 glasses-ws; V2 exchange accepts both),
the full V1 dependency ledger (RestComms REST surface incl. account deletion
and settings/calendar/location/notifications feeds; the V1 WS still owning the
OS dashboard + app bridge), ranked tech debt, a 4-cut sequencing (identity ->
client data feeds -> dashboard/app-bridge -> V2-only build flag), and the open
questions gating spec.md (OAuth redirect mechanics, device session model,
legacy coexistence, migration, identity side-channels).
…-refcount

ASG: make WakeLockManager extend-only so a short acquire can't cut OTA short
Decisions: core-hosted OAuth via system browser (mirrors console PKCE flow),
V2-tokens-only on device (GoTrue used transiently + admin API, no Supabase
material at rest), NO legacy coexistence bridge (SocketComms is being
deprecated and V1 removal lands in the next PR, so V1 WS/REST break by design
and the ledger in section 6 is that PR's work list), silent session migration
via the existing exchange then retire the symmetric branch, single
/api/account/me for identity side-channels, and account deletion moves in
Phase 1 (store compliance cannot be in the broken list). Spike complete;
spec.md unblocked.
Decision 4 revised: no silent bootstrap or migration ramp. On update, stored
Supabase/legacy material is wiped and users sign in again, which IS the
migration. Lets the symmetric Supabase/legacy exchange branch be deleted
immediately instead of lingering behind a usage metric.
spec.md: the full /api/account contract (credentials, password/email flows,
core-hosted OAuth with PKCE + one-time deep-link code, account deletion),
error taxonomy extending the RFC-shaped oauth errors, token design (Ed25519
mentra-account subject token through the existing exchange; `mentra` becomes
a real oems row; symmetric branch deleted), the authClient method mapping so
mobile screens change minimally, and breaking-cutover rollout gated by
min-version.

design.md: module layout in core, server-side GoTrue integration table with
error mapping isolated in gotrue.client.ts (the Phase 2 swap seam), keys +
startup migration for the mentra oems row, the OAuth sequence end to end,
mobile changes (delete supabase-js/anon key/exchangeToken; new provider
behind authClient), deletion fan-out to V1 with reconciliation, test plan
incl. negative proofs, and the staged rollout ending in the V1-removal PR.
…+ tests

Implements the /api/account contract (issue 019 spec.md): gotrue.client (the
only Supabase-aware file, error-mapped), account.service orchestration,
one-time-code service + account-code model, account.api + oauth.api routes,
Ed25519 mentra-account subject token fed through the existing OEM exchange,
mentra oems-row startup migration, per-user session revoke, and AccountError
rendered by the app error handler. Additive only; symmetric branch deletion
deferred to the V1-cutover PR.

Integration tests (mock GoTrue, 6/6): login mints a V2 session whose refresh
works, invalid_credentials uniform, verification_required, /me, single-use
reset codes, logout-everywhere. Typecheck clean.
… at the public exchange

Refresh authorization now checks the mentra oems row like every OEM (the
early-return is demoted to a transitional fallback for environments whose seed
migration has not run; it dies at the V1 cutover). Two parity tests added: the
account subject token exchanges at the PUBLIC /api/client/auth/exchange
endpoint (no private path), and a disabled mentra oems row blocks refresh like
any OEM. design.md gains section 9 mapping each account-module behavior to its
external-OEM equivalent so the module doubles as OEM reference code.
session.service.ts was three concerns in one: session lifecycle, token minting,
and crypto key management + JWKS. Move the key concern (keypair loading for
access/miniapp/account, kid constants, MENTRA_ALG, requireEnv, getPublicJwks,
resetSigningKeyCache, getAccountPublicKeyPem) into signing-keys.service.ts, so
session.service is about sessions again. Clean dependency direction:
signing-keys <- session. Re-exported the moved public symbols from
session.service for existing importers; no behavior change.

Also fixed test isolation: account-auth test now resets the signing-key cache
in beforeAll (mirrors the audio tests) so it does not inherit another file's
cached keys, and session.service.test seeds the account key that getPublicJwks
now publishes. Full auth suite 32/32 + jwks unit 1/1, typecheck clean.
- POST /api/account/subject-token: authenticated by the current V2 session,
  mints a short-lived `mentra` subject token the device's cloud-client
  exchanges at the public /api/client/auth/exchange, exactly as an external
  OEM app would (OEM parity).
- dropLegacyUserIdentityIndex: guard collection.indexes() so a fresh database
  (no users collection) boots instead of crashing on "ns does not exist".
…upabase)

Replace the embedded Supabase client and the legacy Cloud V1 token exchange
with AccountAuthProvider, which talks only to Cloud V2's first-party account
backend (/api/account/*). The device holds V2 tokens directly; cloud-client
mints a fresh subject token via getSubjectToken() and exchanges it, so Mentra
behaves as just another OEM.

- accountClient.ts: AccountAuthProvider (login/signup/me/reset/subject-token,
  MMKV token storage, single V2 refresh). Notifies AuthContext synchronously
  on sign-in so home-boot doesn't race the listener and bounce to /auth/start.
- authClient.ts: default (non-China) provider is now AccountAuthProvider;
  adds getSubjectToken() to the AuthClient contract.
- MantleManager.ts: toolkit.configure getSubjectToken pulls from the provider.
- index.tsx: boot needs only a valid V2 session, then mantle.init(); drops the
  restComms/socketComms V1 exchange.
Bugbot review finding: the top-level text field is rendered as mrkdwn
like the blocks, so developer-controlled strings in the fallback need
the same escaping, mirroring V1's escaped fallback text.
…ithout account keys

Addresses the cursor[bot]/codex review findings on #3382:

- authorizeUrl now sends code_challenge (S256 of core's verifier) +
  code_challenge_method to GoTrue; without it GoTrue issues a non-PKCE code
  and the callback's pkce exchange fails.
- Email change is verification-gated: /email/change re-verifies the password
  and emails a one-time code to the NEW address; the change is applied only by
  the new /email/change/confirm. (The old path used the GoTrue admin update,
  which applies immediately with no confirmation, contradicting the API's
  verification_sent contract.)
- getPublicJwks degrades gracefully when MENTRA_ACCOUNT_JWT_* is unset:
  publishes access/runtime/miniapp kids and omits the account kid instead of
  500ing the whole JWKS in environments where the account feature is dark
  (mirrors the startup migration's skip).
- Tests: email-change gating (request applies nothing, code confirm applies),
  PKCE params on the authorize URL, JWKS with and without account keys.
Completes the provider methods the review flagged as unimplemented stubs:

- googleSignIn/appleSignIn: build the /api/account/oauth/:provider/start URL
  with a fresh state + S256 PKCE challenge; verifier and state stay in MMKV.
  New pkce.ts (pure-JS SHA-256, verified against NIST + RFC 7636 vectors;
  Hermes has no WebCrypto and the app does not ship expo-crypto).
- completeOAuthHandoff: the /auth/callback deep link now handles the new
  ?code&state query form (alongside the legacy #fragment form), verifies
  state, and swaps code + verifier at /oauth/complete for the V2 session.
- updateUserPassword/updateUserEmail/confirmEmailChange wired to
  /api/account/password/change and /email/change{,/confirm}. The settings
  screens now collect the current password (server re-verifies it; a stolen
  session alone must not rotate credentials), and change-email gains the
  code-confirmation step matching the new verified flow.
Photos saved on the glasses were named IMG_<timestamp>_<rand> with the
originating take_photo requestId discarded, so bulk gallery imports could
only correlate synced files with photo requests by timestamp matching,
which breaks under clock skew and rapid bursts. Videos already embed the
requestId in their capture directory name; photos now follow the same
convention (sanitized, for both save:true gallery captures and transient
save:false captures).

- Add CaptureRequestId util: sanitizes requestIds for directory-name
  embedding and extracts them back out of capture IDs.
- /api/sync capture groups and /api/gallery entries expose the embedded
  ID as an explicit request_id field (absent for button captures and
  legacy files).
- Button captures have no originating request; their stable ID is the
  capture directory name itself, which now replaces the throwaway
  local_<timestamp> string in photo status messages.
- Sanitize the requestId embedded in video capture directory names.

Tested: scripts/check-android-compile.sh asg passes; new
CaptureRequestIdTest unit suite (7 tests) passes.
…kflow)

Personal sandbox in the aws-us-west-2 cluster while cloud-debug is frozen
for enterprise customers. Same shape as cloud-debug: one Porter app
(cloud-isaiah), core + runtime from the shared image, Doppler config
dev_isaiah synced via the cloud-v2-isaiah-doppler env group, hostnames
core/runtime.isaiah.us-west-2.mentraglass.com (Cloudflare DNS-only CNAMEs
to the cluster ingress). Triggers: workflow_dispatch + push on
mentra-account-auth. No per-env UDP NLB (WS fallback), matching dev/debug.
Verified against the cluster/DNS/Doppler on 2026-07-09:

- No environment has a working public UDP path today: the Doppler configs
  advertise audio-udp.<env> hostnames that have no Cloudflare records, so
  clients fail UDP and fall back to WS. The runbook's 'What's deployed'
  section now says so instead of describing an NLB in front of an app name
  (cloud-v2) that no longer deploys.
- NLB Services are per-environment (selector pins one app's runtime pods):
  audio-udp-nlb-debug.yaml is the template; audio-udp-nlb.yaml is marked
  LEGACY do-not-apply.
- Provisioning steps rewritten per-env: clone the template, direct kubectl
  (porter kubectl is read-only; aws sso login if the token expired), set
  AUDIO_UDP_ADVERTISED_HOST in the env's OWN Doppler config (raw NLB
  hostname is fine for dev-grade envs), redeploy that env.
- Fixed both manifests' comments that said 'porter kubectl apply', which
  contradicted the runbook's own read-only warning.
- Annotated example now shows the per-env shape including the mandatory
  externalTrafficPolicy: Local.
…evice-errors

Improve camera device error reporting
…sh-the-toolkit-on-npm-as-mentraengine

OS-1668: Rename island and toolkit APIs to Mentra Engine
@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 23
Consecutive no-new-review cycles 23

CI status

Check Status Conclusion
Mobile App Quality Checks in_progress
Mobile App Android Build in_progress
MentraOS ASG Client Build in_progress

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 24
Consecutive no-new-review cycles 24

CI status

Check Status Conclusion
Mobile App Quality Checks completed success
Mobile App Android Build in_progress
MentraOS ASG Client Build in_progress

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 25
Consecutive no-new-review cycles 25

CI status

Check Status Conclusion
Mobile App Quality Checks completed success
Mobile App Android Build in_progress
MentraOS ASG Client Build completed success

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

- Drop the postinstall patch-package step: it pointed at the deleted
  patches-runtime/ dir, whose only patch was the retired Supabase one
  (a missing --patch-dir is a no-op, so this is dead weight, not a fix)
- Verify logout against both MMKV account tokens so the check matches
  the "token pair" comment
- Update stale comments that still described the deleted
  WebSocketManager/SocketComms/ws-types as live
Dev's #3411 renamed @mentra/island to @mentra/engine (toolkit → engine)
across every file this branch deletes or edits. Resolution re-expresses
the ripout in the engine world:

- Files this branch deletes (host SocketComms/WSM/RestComms/ws-types,
  BackendUrl, NonProdWarning, and the module RestComms): deletion wins;
  dev's only changes to them were the mechanical rename.
- Content conflicts: this branch's version plus dev's exact rename sweep
  (@mentra/island → @mentra/engine, toolkit → engine, useToolkitSnapshot
  → useEngineSnapshot), with comment wording aligned to dev's engine
  naming.

tsc clean on the merged tree (mobile src + modules).
feat(020): rip out Cloud V1 from the mobile app
@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 26
Consecutive no-new-review cycles 26

CI status

Check Status Conclusion
Mobile App Quality Checks completed success
Mobile App Android Build in_progress
MentraOS ASG Client Build completed success

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

…logs-v2

Port fetch-incident-logs.sh to Cloud V2 admin reports API
@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 27
Consecutive no-new-review cycles 27

CI status

Check Status Conclusion
Mobile App Quality Checks in_progress
Mobile App Android Build in_progress
MentraOS ASG Client Build completed success

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 28
Consecutive no-new-review cycles 28

CI status

Check Status Conclusion
Mobile App Quality Checks completed success
Mobile App Android Build completed success
MentraOS ASG Client Build completed success

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 29
Consecutive no-new-review cycles 29

CI status

Check Status Conclusion
Mobile App Quality Checks completed success
Mobile App Android Build in_progress
MentraOS ASG Client Build completed success

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

@github-actions

Copy link
Copy Markdown
Contributor

PR Agent Handoff

Exit reason: Budget exhausted — fix or review cycle cap reached.

Metric Value
Fix rounds 0
Review cycles 30
Consecutive no-new-review cycles 30

CI status

Check Status Conclusion
Mobile App Quality Checks in_progress
Mobile App Android Build in_progress
MentraOS ASG Client Build completed success

Remaining blocking findings

None

Nits (informational)

None


Humans merge when ready. Agents do not auto-merge. Label agent-resume to re-run the loop after new commits.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants