feat: Smart App Banner deep-linking + universal links (iOS) and App Links (Android) for app.kilo.ai - #4783
Conversation
|
(bot) Standin review — posted because the automated reviewer posts no findings on this repository's PRs; this is not a Kilobot review. A fresh
Context from the reviewer's notes: it independently confirmed the new line is the only |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe only change since the last review ( Files Reviewed (2 files changed since last review)
Note: All other files that appeared in the raw Previous Review Summaries (5 snapshots, latest commit 9202b5f)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 9202b5f)Status: No Issues Found | Recommendation: Merge Executive SummaryThe incremental commit ( Files Reviewed (2 files changed since last review)
Note: The previously reported CRITICAL in Previous review (commit 4ac8256)Status: 2 Issues Found | Recommendation: Address before merge Executive SummaryThe highest-risk issue remains an unguarded native-bridge call in Overview
Issue Details (click to expand)CRITICAL
WARNING
Files Reviewed (5 files changed since last review)
Fix these issues in Kilo Cloud Previous review (commit 13ac1c1)Status: 3 Issues Found | Recommendation: Address before merge Executive SummaryThe highest-risk issue remains an unguarded native-bridge call in Overview
Issue Details (click to expand)CRITICAL
WARNING
Files Reviewed (1 file changed since last review)
Fix these issues in Kilo Cloud Previous review (commit 9383120)Status: 3 Issues Found | Recommendation: Address before merge Executive SummaryThe highest-risk issue remains an unguarded native-bridge call in Overview
Issue Details (click to expand)CRITICAL
WARNING
Files Reviewed (1 file changed since last review)
Fix these issues in Kilo Cloud Previous review (commit 980d090)Status: 3 Issues Found | Recommendation: Address before merge Executive SummaryThe highest-risk issue is an unguarded native-bridge call in Overview
Issue Details (click to expand)CRITICAL
WARNING
Files Reviewed (28 files)
Reviewed by claude-sonnet-5 · Input: 40 · Output: 9.1K · Cached: 1.1M Review guidance: REVIEW.md from base branch |
Standin review comment — PR #4783 @ 980d090(bot) Standin review of record (Kilobot replacement, per workflow override) — fresh Verdict: 1 finding (medium) — triaged below; not actionable on this PR
Triage: rejected as an actionable PR remark — the finding restates a deliberate, documented
No code change. The finding stays open as the recorded manual step, which the human approver Residual risks noted by the reviewer (accepted, not findings)
Checks the reviewer ranapp-shared 258 tests, mobile 1894 tests, web targeted 17 tests — green; mobile |
|
(bot) Standin review round 3 (head 13ac1c1, kilo/moonshotai/kimi-k3 high, full diff vs origin/main) returned one low finding, now fixed:
The same review verified: all mapped target routes exist; no middleware/rewrite interferes with Separately, the four Kilobot threads were triaged in-thread: the stale-restash warning was valid and is fixed in 4ac8256; the unguarded- |
|
(bot) Standin review round 4 (head
The reviewer verified all prior fixes (round-3 GetSubstitution |
|
(bot) Standin review round 5 (head The reviewer traced the round-4 single-pass This concludes the review loop: CI is fully green on |
Batch O — Smart App Banner
app-argument+ Universal Links (iOS) + App Links (Android)Widens the batch-L Smart App Banner work into full deep linking: tapping an
app.kilo.ailinkon a phone opens the Kilo app on the matching screen instead of Safari/Chrome, and the banner's
OPEN button deep-links into the current page's app screen.
What ships
6c729fe51feat(app-shared): add universal link route map— single source of truth12c08e213feat(web): serve app site association and asset links for deep linking4e9d5d986feat(web): deep-link the smart app banner open button(app-argument)14742fa41feat(mobile): associate app.kilo.ai domain for universal and app links980d09025feat(mobile): route incoming web links to matching app screensOne route-map artifact in
@kilocode/app-shared/universal-linksgenerates every consumer: thecommitted AASA file,
assetlinks.json, the AndroidintentFilters, and the app's runtime URLtranslator. Drift between the committed JSON files and the map is CI-caught
(
apps/web/src/lib/app-site-association.test.ts); drift between the Android config literals andthe map is CI-caught (
apps/mobile/src/lib/universal-link-paths.test.ts).The route map (11 rows)
/profile/claw/cloud/sessions/security-agent·/security-agent/findings/code-reviews·/code-reviews/*/organizations/*/security-agent(+/findings)/organizations/*/code-reviews(+/*)/code-reviews/review-mdand its org twin are excluded (thereview.mdguide is not areview). Auth/payment/IDE-handoff flows (
/users/sign_in,/auth/*,/payments/*,/device-auth,/sign-in-to-editor, …),/admin/*, static pages, public share pages(
/s/*,/share/*), and unmappable detail routes are deliberately not mapped — hijackingthose would break browser-based flows or land on screens that don't exist. The runtime is
synchronous
redirectSystemPath(+native-intent): cold starts stash the target in a pendingslot (consumed after the auth/consent gates clear, sharing the push-notification channel —
the slot moved to
src/lib/deep-link-launch.ts, with source-tag precedence so a universallink always wins over a possibly-stale notification response); warm opens navigate directly.
Android cold start additionally reads the launch URL synchronously via
getLinkingURL(),closing expo-router's 150ms
getInitialURL()race.Decisions (recorded)
app-sounding paths (
/claw,/code-reviews) 301 to marketing pages by design, and nothingelse has an app screen. An empty
detailslist is dead config with a real per-install cost.Reasoning recorded in Kilo-Org/kilocode-landing#1798's description. If a kilo.ai path ever
gains an app screen, adding the file is a ~10-line PR.
app-argumenton exactly four surfaces (/profile,/code-reviews,/security-agent/findings,/cloud/sessions) viasmartAppBannerItunes(path), which throwsat build time on any path the app can't resolve.
/clawand/security-agentare excluded(their only metadata hosts wrap unmapped children); dynamic detail routes stay appId-only.
Native-build note (not OTA-updatable)
associatedDomains, the intent filters, and the iOS entitlement are native changes: they takeeffect in the next native build, not via OTA. Manual provisioning (stacked on #4779's
steps): enable the Associated Domains capability on the App ID; EAS regenerates the
provisioning profile on the next remote-credentials build.
Composition note for #4779 (share target)
apps/mobile/src/app/+native-intent.tsxis created by both PRs. Whichever lands secondcomposes both concerns in one
redirectSystemPath, share-intent check first (a share URLmust return early before web-path mapping), per the comment in the file.
Verification
Automated (green now):
packages/app-shared: 258 tests — every row, every exclusion, wildcard single-segmentsemantics, trailing slashes,
kiloapp://forms, foreign hosts, garbage.apps/web: 17 tests — committed AASA/assetlinks vs the route map (drift guard), bannerhelper both ways + unmapped-throws guard.
apps/mobile: 1894 tests — handler cold/warm/falsy/passthrough invariants, pending-slotprecedence (both write orders), synchronous launch capture, the intent-filter-literal drift
test; existing notification suites unweakened.
npx expo config --type publicshowsapplinks:app.kilo.ai+ all 11pathPatterns;expo prebuildemitsapplinks:app.kilo.aiin the iOS entitlements and theautoVerifyintent-filter with all 11 paths inAndroidManifest.xml.pnpm test && pnpm typecheck && pnpm lint && pnpm check:unused(apps/mobile);pnpm test -- <new suites> && pnpm typecheck(apps/web);pnpm test && pnpm typecheck(packages/app-shared); oxfmt clean.
On-device (this PR's verifiable scope): app-side resolution via the custom scheme on the
iOS simulator (
simctl openurl "kiloapp:///<path>"— HTTPSsimctl openurlis NOT a universallink without a CDN-verified AASA and must not be used as an oracle) and
adb shell am start -W -a android.intent.action.VIEW -d "<url>" com.kilocode.kiloappon theemulator. Sequences: signed-in warm, signed-in cold, signed-out cold (target survives login),
consent cold; tab-root rows and deep rows verified separately; an unmapped URL resolves to
nullwith no navigation. Results (all PASS):iOS simulator (dev client):
/profile,/claw,/cloud/sessions(tab roots, verifiedseparately),
/security-agent/findings,/code-reviews/rev_e2e_1(bogus id → the screen'sown query-error not-found),
/organizations/o1/security-agent.kiloapp:///profile→ Profile tab;kiloapp:///security-agent/findings→Findings.
kiloapp:///profileandkiloapp://profileresolve (parsernormalises both forms).
after the gate). Harness note: the scripted login helper wipes the slot (its preflight
re-opens the dev client with a different Metro URL string, reloading the JS process) — proven
with a manual OTP that skips the preflight.
Findings.
/admin,/code-reviews/review-md): matcher returnsnull, no navigation(expo-router
+not-found) — the documented accepted equivalence.Android emulator (dev client,
am startVIEW intents onhttps://app.kilo.aiURLs):/profile→ Profile tab;/security-agent/findings→ Findings — thesynchronous
getLinkingURL()capture survives Android cold start (plan risk R7 retired)./cloud/sessions→ Agents tab./admin→ no mapped screen (+not-found), accepted as documented.pm get-app-linkslistsapp.kilo.aias unverified — expected locally; Google's verifieris post-deploy and Play builds additionally need manual step 4 below.
Deferred to post-merge/post-release (cannot be verified locally — Apple/Google must fetch
the association files from the deployed domain):
curl -sIboth/.well-known/apple-app-site-association(200,content-type: application/json, no redirect) and/.well-known/assetlinks.json.integrity → App signing key certificate) to
sha256_cert_fingerprintsinapps/web/public/.well-known/assetlinks.jsonand redeploy. The shipped fingerprint is theverified EAS upload certificate (two independent sources agree), which covers EAS
internal-distribution and dev-client builds. Until the Play fingerprint is appended, App
Links will not verify for Play-store-installed builds — the single manual input this
batch needs.
Non-goals (documented)
link previews. An Android Smart App Banner (Safari feature; no Android analogue).
webcredentials. Mapping detail routes whose app screen needs an id the web URL lacks.target is lost — identical to pre-feature behaviour, not a regression.
means a new binary, hence a new JS process).
staging-app.kilo.aiassociation:app-argumentcorrectly points at the staging origin viaAPP_URL, but staging is not inassociatedDomains, so the deep link is simply not honouredthere.
Accepted residuals
components*is an Apple glob that crosses/while the app matcher issingle-segment — a deliberate, analysed superset: every URL in the gap is already a web 404,
so the app landing on its own
+not-foundis an acceptable equivalence (no browser-handofffallback built).
Content-Typeheader cannot be unit-tested (Next runtime behaviour); it is coveredby the post-deploy
curl -Istep above.captureLaunchDeepLink()is not try/caught at module scope: its throw surface is nil(bundled native module, pure never-throwing parser, trivial setter), so the
never-brick-launch guarantee is preserved.