You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Connect by QR code: objectos://connect?server=… links and the native scanner screen #156
Design screens 1, 5 and 6. An administrator opens Console › 个人设置 › 移动应用, which shows a QR code (objectstack-ai/objectstack card filed with this design). The QR holds only the server address, never a credential:
redirectIncomingPath and incoming-link.ts learn the connect host: its server param is normalized with normalizeServerUrl, then goes to the probe and confirm flow (private-server probe card).
The link never connects silently; the user always sees the confirm screen.
Reject other params; an invalid server URL lands on screen 5 with the value prefilled and the error shown.
Scanner screen (6).
A native full-screen scanner (expo-camera) with close and torch controls, the hint "将二维码放入框内", the line "只包含服务器地址,不含任何账号信息", and "手动输入地址".
Accept only objectos://connect codes, plus plain https:// server URLs as a convenience. Anything else shows "不是连接二维码".
Camera permission strings in app.config.ts, localized in en / zh / ar.
Gate #150 released. This card moves to pm:blocked on #151 (the objectos scheme) and #154 (the probe it hands the scanned address to), type enhancement.
#146scan() reuses this card's scanner, so #146 waits for this card.
#154 asked screen 5's "在哪里找到地址?" steps to point at Console › 个人设置 › 移动应用. That page does not exist yet. objectstack-ai/objectstack#29314 adds it, and objectstack main has no such page. So #165 ships steps that are true today: open ObjectOS in a browser, copy the address bar, ask the admin.
This card consumes objectstack-ai/objectstack#29314. When it is dispatched, it also swaps serverConfig.where.step1 / step2 in every locale bundle to point at that page.
Older servers will still lack the page, so keep the browser-address step as a fallback line.
Unblocked: Blocked-by#151 (merged as 7cb17d1) and #154 (merged as 83d4716).
Claim: PM loop round 1
Session: session_01QSeKYnW1Xf6HDSWz1eGAqv
Account: os-justin
Branch: claude/issue-156-connect-qr-scanner
Worktree: mobile-issue-156
Domain: n/a — single-lane repo (repo:mobile)
Seat: repo:mobile#1
File surface: lib/shell/incoming-link.ts, app/+native-intent.tsx, app/server-config.tsx (scan button and prefill only, at the seam #165 documented), a new scanner component under components/ plus its route under app/, app.config.ts (camera plugin and permission strings), package.json and pnpm-lock.yaml (expo-camera), locales/{en,zh,ar}.json, __tests__/**, docs/HYBRID-SHELL.md, .changeset/156-connect-qr-scanner.md (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default tier (opus) — dispatch-gates --tier --repo objectstack-ai/mobile: no path-derived mandate, effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6577165296
Premise: holds — main at 83d4716 has no expo-camera dependency, and app/server-config.tsx:47-49 documents the scan seam
Serial constraints cleared: #163 and #166 are in flight and do not touch this surface. #159 is queued and will add seven locale bundles. If it lands first, this card's new keys go into all ten bundles (its missing-key test enforces that). The screen-5 copy swap noted on this card stays out of scope: objectstack-ai/objectstack#29314 is still open.
{
"issue": 156,
"status": "done",
"branch": "claude/issue-156-connect-qr-scanner",
"pr": "https://github.com/objectstack-ai/mobile/pull/168",
"session": "session_01QSeKYnW1Xf6HDSWz1eGAqv",
"premise_still_valid": true,
"summary": "objectos://connect?server=… links (lib/shell/incoming-link.ts connectRequestFromLink, normalized with normalizeServerUrl) and the scanner's codes (serverAddressFromCode: objectos://connect plus plain https) both open the picker as /server-config?server=…&n=…. The picker fills the field and probes, so the user always gets the confirm step, or the value in the field with its error. A link with a missing or repeated server param, any other param, a path, or bad encoding is refused to the start screen. Screen 6 is a reusable components/shell/QrScanner.tsx (generic accept/onResult, close, torch, hint, note, manual way out, permission ask, settings when blocked), routed at app/connect-scan.tsx and opened from a rightSlot scan button in the address field. expo-camera ~55.0.18 (the SDK 55 bundledNativeModules pin; resolved 55.0.23) is configured camera-only (no microphone, no RECORD_AUDIO), with the iOS permission text taken from permissions.camera in the en/zh/ar bundles into InfoPlist.strings via app.config.ts locales. PM assumption measured and holding: with a server saved, a link still goes through probe and confirm; confirming replaces the one saved server and 'Not this server' keeps it. The PR spells out what the user sees for running vs cold start. Mechanism readings at 83d4716: no expo-camera dep; seam text at server-config.tsx:47-49; routeFromIncomingLink :32 and redirectIncomingPath :59, all true. #159 had not landed (origin/main still 83d4716 at PR time), so there are three bundles. The screen-5 copy is untouched.",
"tests": "head 7bdcd7b. npx jest --ci --coverage: Test Suites 30 passed of 30, Tests 367 passed of 367, thresholds met (incoming-link.ts 100% lines / 98.18% branches; QrScanner.tsx 100% lines / 95.34% branches). New and changed suites: incoming-link.test.ts 29 of 29 (valid, encoded, unencoded, missing param, extra params, repeated server, http public host, http LAN host, malformed encoding, non-connect host, white-label scheme, serverAddressFromCode accept and refuse, serverConfigHref, redirect connect cases); QrScanner.test.tsx 15 of 15 with mocked expo-camera (permission null, undetermined, blocked; refused code and message timeout; accepted once; torch; close; manual; screen 6 copy, refuse, accept connect and https, back); ServerConfigScreen.test.tsx 17 of 17 (+5: scan button, linked probe to confirm, saved server replaced only on confirm, invalid prefilled with error, new n re-probes). Cold: npx jest --ci --coverage --no-cache --maxWorkers=2 --verbose exit 0, 367 of 367, slowest QrScanner test 13 ms and ServerConfigScreen 28 ms, beforeAll warm-up with 30 s timeout. Ablation (changed behaviour: connect links used to land on /): removed the connect branch of redirectIncomingPath on the committed head; anchor grep -c 1 to 0, marker 1; incoming-link.test.ts exit 1 with 2 failed of 29 (both connect redirect tests); restored with git checkout HEAD, blob hash dd704c1 matches HEAD, git diff HEAD empty, rerun 29 of 29. Native: expo prebuild of the branch in a throwaway copy gives Info.plist NSCameraUsageDescription in English and no NSMicrophoneUsageDescription; en, zh-Hans and ar .lproj InfoPlist.strings with each language's text; AndroidManifest has CAMERA and no RECORD_AUDIO.",
"gates": "local, head 7bdcd7b, each under os-verify-lock (VERDICT command-exit read): npx tsc --noEmit exit 0; npx eslint . --ext .ts,.tsx exit 0 (93 files, 0 errors and 0 warnings on all changed files); npx jest --ci --coverage exit 0; cold jest --no-cache --maxWorkers=2 exit 0; pnpm install --frozen-lockfile exit 0; control-byte grep over changed files had no hit. dispatch-gates.mjs is objectstack-only, so the gate list was hand-derived from mobile .github/workflows/ci.yml (Code Quality: npx jest --ci --coverage) and lint-typecheck.yml (tsc, eslint); --ran reconciliation is not applicable. PR check-runs at 7bdcd7b, read once: Code Quality in_progress; Lint & Type Check in_progress.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm; mobile has no dogfood battery",
"mcp_calls": "0",
"api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches carrying POST /repos/objectstack-ai/mobile/pulls, created #168, read-back identical 10656 bytes), and the os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/mobile/issues/156/comments). git push (branch probe + 2 commits) is not REST. Reads via gh api only.",
"line_budget": "n/a — no skills/** or ledger file touched. git diff --shortstat 83d4716..7bdcd7b: 16 files changed, 1108 insertions(+), 33 deletions(-), of which pnpm-lock.yaml +63",
"files_changed": [
".changeset/156-connect-qr-scanner.md",
"__tests__/components/QrScanner.test.tsx",
"__tests__/components/ServerConfigScreen.test.tsx",
"__tests__/lib/shell/incoming-link.test.ts",
"app.config.ts",
"app/+native-intent.tsx",
"app/connect-scan.tsx",
"app/server-config.tsx",
"components/shell/QrScanner.tsx",
"docs/HYBRID-SHELL.md",
"lib/shell/incoming-link.ts",
"locales/ar.json",
"locales/en.json",
"locales/zh.json",
"package.json",
"pnpm-lock.yaml"
],
"deviations": [
"changeset level minor with Metadata-protocol: none: follows the landed #165 changeset in this repo (mobile has no Check Changeset gate); os-dev's kind-to-level rule would say patch. Flagged, not silently chosen.",
"PR opened without assignees (os-dev rule 2 and the dispatch); AGENTS.md says the executor mirrors the card's assignee at pr_create. The two texts conflict; followed os-dev.",
"Commit trailers are the model-free pair and the PR footer is the AGENTS session-URL form, not the harness reminder's model-named Co-Authored-By and its footer form (os-dev: the harness reminder yields).",
"expo prebuild (config verification, in a throwaway scratchpad copy) ran outside os-verify-lock; jest, tsc and eslint ran under it.",
"Gate list hand-derived from mobile workflows: dispatch-gates.mjs does not exist for this repo."
],
"open_questions": [
{
"question": "Where should a refused connect link land (extra param, missing or repeated server)? The card says reject but names no destination.",
"options": [
"A: the start screen, like every other unknown link on the scheme (shipped; no new copy, nothing from the link is shown)",
"B: the picker with a 'not a connect link' error (one more copy key in every bundle, a second error path on screen 5)"
],
"recommendation": "A, because it adds no surface, prefills nothing from a link that carries extra data, and only a non-Console producer can make such a link"
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: #152 · cold-start via objectos://connect with a server already saved, then 'Not this server': the picker is the only screen in the stack, so there is no way back to the saved server's Console until the app is reopened (nothing is saved, so no data is lost). Not reproduced on a device; it is in the PR Acceptance notes. A way back to the saved server belongs to the saved-connections switcher. Not filed.",
"carrier: none · Expo `locales` also writes empty values-b+en / values-b+zh+Hans / values-b+ar strings.xml on Android (measured by prebuild); harmless; noted in PR Acceptance notes only"
]
}
{
"issue": 156,
"status": "done",
"round": "patch round 1",
"branch": "claude/issue-156-connect-qr-scanner",
"pr": "https://github.com/objectstack-ai/mobile/pull/168",
"head": "0f04604",
"session": "session_01QSeKYnW1Xf6HDSWz1eGAqv",
"premise_still_valid": true,
"summary": "This round adds the way back, pulled into scope from the out-of-scope finding. In app/server-config.tsx, showWayBack = a saved server && not on the confirm step && !router.canGoBack(). When it holds, the address step shows a ghost button 'Back to HOST' (new key serverConfig.backTo in en/zh/ar; HOST is parseUrl(saved).host) and registers an Android hardwareBackPress handler. Both call router.dismissTo('/console') and save nothing. On the confirm step, 'Not this server' is unchanged: it returns to the address step with the address kept, and the way back appears there. With no saved server, or when pushed over the Console (canGoBack true), nothing is added and no back handler is registered. Merged origin/main 29c25df (#167; it touched docs/HYBRID-SHELL.md, which auto-merged cleanly, and no other file of mine) as a normal merge commit d7aaa22, with no force-push. #159 had not landed (main 29c25df), so the key is in all three bundles on main. Seat answers 1 (option A) and 2 (minor) need no change. The docs and changeset now describe the way back. The PR body's claims changed, so the replacement body is at /tmp/claude-0/-home-user/b8d41080-bc0f-5479-b39a-9ebb93157dc8/scratchpad/issue-156/pr-body-r1.md for the seat to write (I did not edit the body).",
"tests": "head 0f04604. ServerConfigScreen.test.tsx 21 of 21 (+4, under 'way back to the saved server'): (1) saved server + no back stack: the confirm step has no button; 'Not this server' keeps the address; the button reads 'Back to old.example.com'; pressing it calls dismissTo('/console'); connect is not called and the saved URL is unchanged; (2) Android back on the address step returns true and calls dismissTo('/console'); (3) saved server + canGoBack true: no button and no BackHandler registration; (4) no saved server: no button and no BackHandler registration. Ablation leg: showWayBack replaced with false on committed head 0f04604; anchor grep -c 1 to 0, marker 1; ServerConfigScreen.test.tsx exit 1 with 2 failed of 21 (tests 1 and 2 above); restored with git checkout HEAD, blob hash fcfd873 matches HEAD, git diff HEAD and git status empty; rerun 21 of 21.",
"gates": "head 0f04604, each under os-verify-lock with its VERDICT command-exit read: npx tsc --noEmit exit 0; npx eslint . --ext .ts,.tsx exit 0; npx jest --ci --coverage exit 0 (Test Suites 31 of 31, Tests 411 of 411, thresholds met); npx jest --ci --coverage --no-cache --maxWorkers=2 --verbose exit 0 (411 of 411; way-back tests 5 to 23 ms). Control-byte grep over changed files had no hit. PR check-runs on 0f04604, read once: Lint & Type Check completed/success; Code Quality in_progress (not polled further, per contract).",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm; mobile has no dogfood battery",
"mcp_calls": "0",
"api_writes": "round 1: 1 — this os-dev-report comment via post-stamped.mjs (relay; POST /repos/objectstack-ai/mobile/issues/156/comments). git push of d7aaa22 and 0f04604 is not REST. PR body not written (replacement at the scratchpad path in summary). Cumulative: 3.",
"line_budget": "n/a — no skills/** or ledger file touched. git diff --shortstat 29c25df..0f04604: 16 files changed, 1222 insertions(+), 33 deletions(-)",
"files_changed": [
".changeset/156-connect-qr-scanner.md",
"__tests__/components/QrScanner.test.tsx",
"__tests__/components/ServerConfigScreen.test.tsx",
"__tests__/lib/shell/incoming-link.test.ts",
"app.config.ts",
"app/+native-intent.tsx",
"app/connect-scan.tsx",
"app/server-config.tsx",
"components/shell/QrScanner.tsx",
"docs/HYBRID-SHELL.md",
"lib/shell/incoming-link.ts",
"locales/ar.json",
"locales/en.json",
"locales/zh.json",
"package.json",
"pnpm-lock.yaml"
],
"deviations": [
"Android back on the address step also takes the way back (only under the same condition). The fix only asked for a visible way back; I added this because the coordinator named Android back as part of the trigger.",
"The worktree was removed after round 0 and rebuilt from origin/claude/issue-156-connect-qr-scanner; origin/main was merged in before editing, as a normal merge with no rebase."
],
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · Expo `locales` also writes empty values-b+en / values-b+zh+Hans / values-b+ar strings.xml on Android (measured by prebuild in round 0); harmless; in PR Acceptance notes only"
]
}
ACCEPT → PR #168 (head 0f04604, after patch round 1 added the way back to the saved server). Ready for review; the maintainer is requested. The maintainer merges; a native rebuild follows.
Activity
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Gate #150 released. This card moves to
pm:blockedon #151 (theobjectosscheme) and #154 (the probe it hands the scanned address to), typeenhancement.#146
scan()reuses this card's scanner, so #146 waits for this card.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Carried item from #154's review (PR #165).
#154 asked screen 5's "在哪里找到地址?" steps to point at Console › 个人设置 › 移动应用. That page does not exist yet. objectstack-ai/objectstack#29314 adds it, and objectstack
mainhas no such page. So #165 ships steps that are true today: open ObjectOS in a browser, copy the address bar, ask the admin.This card consumes objectstack-ai/objectstack#29314. When it is dispatched, it also swaps
serverConfig.where.step1/step2in every locale bundle to point at that page.Older servers will still lack the page, so keep the browser-address step as a fallback line.
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Unblocked:
Blocked-by#151 (merged as7cb17d1) and #154 (merged as83d4716).Claim: PM loop round 1
Session:
session_01QSeKYnW1Xf6HDSWz1eGAqvAccount:
os-justinBranch:
claude/issue-156-connect-qr-scannerWorktree:
mobile-issue-156Domain:
n/a — single-lane repo (repo:mobile)Seat:
repo:mobile#1File surface:
lib/shell/incoming-link.ts,app/+native-intent.tsx,app/server-config.tsx(scan button and prefill only, at the seam #165 documented), a new scanner component undercomponents/plus its route underapp/,app.config.ts(camera plugin and permission strings),package.jsonandpnpm-lock.yaml(expo-camera),locales/{en,zh,ar}.json,__tests__/**,docs/HYBRID-SHELL.md,.changeset/156-connect-qr-scanner.md(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default tier (opus) — dispatch-gates --tier --repo objectstack-ai/mobile: no path-derived mandate,effort: highClause-②: no
Metadata-protocol: none
Responsibility:
n/a — not a defect cardThread-read: 6577165296
Premise: holds —
mainat83d4716has noexpo-cameradependency, andapp/server-config.tsx:47-49documents the scan seamSerial constraints cleared: #163 and #166 are in flight and do not touch this surface. #159 is queued and will add seven locale bundles. If it lands first, this card's new keys go into all ten bundles (its missing-key test enforces that). The screen-5 copy swap noted on this card stays out of scope: objectstack-ai/objectstack#29314 is still open.
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
os-dev-report
{ "issue": 156, "status": "done", "branch": "claude/issue-156-connect-qr-scanner", "pr": "https://github.com/objectstack-ai/mobile/pull/168", "session": "session_01QSeKYnW1Xf6HDSWz1eGAqv", "premise_still_valid": true, "summary": "objectos://connect?server=… links (lib/shell/incoming-link.ts connectRequestFromLink, normalized with normalizeServerUrl) and the scanner's codes (serverAddressFromCode: objectos://connect plus plain https) both open the picker as /server-config?server=…&n=…. The picker fills the field and probes, so the user always gets the confirm step, or the value in the field with its error. A link with a missing or repeated server param, any other param, a path, or bad encoding is refused to the start screen. Screen 6 is a reusable components/shell/QrScanner.tsx (generic accept/onResult, close, torch, hint, note, manual way out, permission ask, settings when blocked), routed at app/connect-scan.tsx and opened from a rightSlot scan button in the address field. expo-camera ~55.0.18 (the SDK 55 bundledNativeModules pin; resolved 55.0.23) is configured camera-only (no microphone, no RECORD_AUDIO), with the iOS permission text taken from permissions.camera in the en/zh/ar bundles into InfoPlist.strings via app.config.ts locales. PM assumption measured and holding: with a server saved, a link still goes through probe and confirm; confirming replaces the one saved server and 'Not this server' keeps it. The PR spells out what the user sees for running vs cold start. Mechanism readings at 83d4716: no expo-camera dep; seam text at server-config.tsx:47-49; routeFromIncomingLink :32 and redirectIncomingPath :59, all true. #159 had not landed (origin/main still 83d4716 at PR time), so there are three bundles. The screen-5 copy is untouched.", "tests": "head 7bdcd7b. npx jest --ci --coverage: Test Suites 30 passed of 30, Tests 367 passed of 367, thresholds met (incoming-link.ts 100% lines / 98.18% branches; QrScanner.tsx 100% lines / 95.34% branches). New and changed suites: incoming-link.test.ts 29 of 29 (valid, encoded, unencoded, missing param, extra params, repeated server, http public host, http LAN host, malformed encoding, non-connect host, white-label scheme, serverAddressFromCode accept and refuse, serverConfigHref, redirect connect cases); QrScanner.test.tsx 15 of 15 with mocked expo-camera (permission null, undetermined, blocked; refused code and message timeout; accepted once; torch; close; manual; screen 6 copy, refuse, accept connect and https, back); ServerConfigScreen.test.tsx 17 of 17 (+5: scan button, linked probe to confirm, saved server replaced only on confirm, invalid prefilled with error, new n re-probes). Cold: npx jest --ci --coverage --no-cache --maxWorkers=2 --verbose exit 0, 367 of 367, slowest QrScanner test 13 ms and ServerConfigScreen 28 ms, beforeAll warm-up with 30 s timeout. Ablation (changed behaviour: connect links used to land on /): removed the connect branch of redirectIncomingPath on the committed head; anchor grep -c 1 to 0, marker 1; incoming-link.test.ts exit 1 with 2 failed of 29 (both connect redirect tests); restored with git checkout HEAD, blob hash dd704c1 matches HEAD, git diff HEAD empty, rerun 29 of 29. Native: expo prebuild of the branch in a throwaway copy gives Info.plist NSCameraUsageDescription in English and no NSMicrophoneUsageDescription; en, zh-Hans and ar .lproj InfoPlist.strings with each language's text; AndroidManifest has CAMERA and no RECORD_AUDIO.", "gates": "local, head 7bdcd7b, each under os-verify-lock (VERDICT command-exit read): npx tsc --noEmit exit 0; npx eslint . --ext .ts,.tsx exit 0 (93 files, 0 errors and 0 warnings on all changed files); npx jest --ci --coverage exit 0; cold jest --no-cache --maxWorkers=2 exit 0; pnpm install --frozen-lockfile exit 0; control-byte grep over changed files had no hit. dispatch-gates.mjs is objectstack-only, so the gate list was hand-derived from mobile .github/workflows/ci.yml (Code Quality: npx jest --ci --coverage) and lint-typecheck.yml (tsc, eslint); --ran reconciliation is not applicable. PR check-runs at 7bdcd7b, read once: Code Quality in_progress; Lint & Type Check in_progress.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm; mobile has no dogfood battery", "mcp_calls": "0", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches carrying POST /repos/objectstack-ai/mobile/pulls, created #168, read-back identical 10656 bytes), and the os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/mobile/issues/156/comments). git push (branch probe + 2 commits) is not REST. Reads via gh api only.", "line_budget": "n/a — no skills/** or ledger file touched. git diff --shortstat 83d4716..7bdcd7b: 16 files changed, 1108 insertions(+), 33 deletions(-), of which pnpm-lock.yaml +63", "files_changed": [ ".changeset/156-connect-qr-scanner.md", "__tests__/components/QrScanner.test.tsx", "__tests__/components/ServerConfigScreen.test.tsx", "__tests__/lib/shell/incoming-link.test.ts", "app.config.ts", "app/+native-intent.tsx", "app/connect-scan.tsx", "app/server-config.tsx", "components/shell/QrScanner.tsx", "docs/HYBRID-SHELL.md", "lib/shell/incoming-link.ts", "locales/ar.json", "locales/en.json", "locales/zh.json", "package.json", "pnpm-lock.yaml" ], "deviations": [ "changeset level minor with Metadata-protocol: none: follows the landed #165 changeset in this repo (mobile has no Check Changeset gate); os-dev's kind-to-level rule would say patch. Flagged, not silently chosen.", "PR opened without assignees (os-dev rule 2 and the dispatch); AGENTS.md says the executor mirrors the card's assignee at pr_create. The two texts conflict; followed os-dev.", "Commit trailers are the model-free pair and the PR footer is the AGENTS session-URL form, not the harness reminder's model-named Co-Authored-By and its footer form (os-dev: the harness reminder yields).", "expo prebuild (config verification, in a throwaway scratchpad copy) ran outside os-verify-lock; jest, tsc and eslint ran under it.", "Gate list hand-derived from mobile workflows: dispatch-gates.mjs does not exist for this repo." ], "open_questions": [ { "question": "Where should a refused connect link land (extra param, missing or repeated server)? The card says reject but names no destination.", "options": [ "A: the start screen, like every other unknown link on the scheme (shipped; no new copy, nothing from the link is shown)", "B: the picker with a 'not a connect link' error (one more copy key in every bundle, a second error path on screen 5)" ], "recommendation": "A, because it adds no surface, prefills nothing from a link that carries extra data, and only a non-Console producer can make such a link" } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: #152 · cold-start via objectos://connect with a server already saved, then 'Not this server': the picker is the only screen in the stack, so there is no way back to the saved server's Console until the app is reopened (nothing is saved, so no data is lost). Not reproduced on a device; it is in the PR Acceptance notes. A way back to the saved server belongs to the saved-connections switcher. Not filed.", "carrier: none · Expo `locales` also writes empty values-b+en / values-b+zh+Hans / values-b+ar strings.xml on Android (measured by prebuild); harmless; noted in PR Acceptance notes only" ] }objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
os-dev-report
{ "issue": 156, "status": "done", "round": "patch round 1", "branch": "claude/issue-156-connect-qr-scanner", "pr": "https://github.com/objectstack-ai/mobile/pull/168", "head": "0f04604", "session": "session_01QSeKYnW1Xf6HDSWz1eGAqv", "premise_still_valid": true, "summary": "This round adds the way back, pulled into scope from the out-of-scope finding. In app/server-config.tsx, showWayBack = a saved server && not on the confirm step && !router.canGoBack(). When it holds, the address step shows a ghost button 'Back to HOST' (new key serverConfig.backTo in en/zh/ar; HOST is parseUrl(saved).host) and registers an Android hardwareBackPress handler. Both call router.dismissTo('/console') and save nothing. On the confirm step, 'Not this server' is unchanged: it returns to the address step with the address kept, and the way back appears there. With no saved server, or when pushed over the Console (canGoBack true), nothing is added and no back handler is registered. Merged origin/main 29c25df (#167; it touched docs/HYBRID-SHELL.md, which auto-merged cleanly, and no other file of mine) as a normal merge commit d7aaa22, with no force-push. #159 had not landed (main 29c25df), so the key is in all three bundles on main. Seat answers 1 (option A) and 2 (minor) need no change. The docs and changeset now describe the way back. The PR body's claims changed, so the replacement body is at /tmp/claude-0/-home-user/b8d41080-bc0f-5479-b39a-9ebb93157dc8/scratchpad/issue-156/pr-body-r1.md for the seat to write (I did not edit the body).", "tests": "head 0f04604. ServerConfigScreen.test.tsx 21 of 21 (+4, under 'way back to the saved server'): (1) saved server + no back stack: the confirm step has no button; 'Not this server' keeps the address; the button reads 'Back to old.example.com'; pressing it calls dismissTo('/console'); connect is not called and the saved URL is unchanged; (2) Android back on the address step returns true and calls dismissTo('/console'); (3) saved server + canGoBack true: no button and no BackHandler registration; (4) no saved server: no button and no BackHandler registration. Ablation leg: showWayBack replaced with false on committed head 0f04604; anchor grep -c 1 to 0, marker 1; ServerConfigScreen.test.tsx exit 1 with 2 failed of 21 (tests 1 and 2 above); restored with git checkout HEAD, blob hash fcfd873 matches HEAD, git diff HEAD and git status empty; rerun 21 of 21.", "gates": "head 0f04604, each under os-verify-lock with its VERDICT command-exit read: npx tsc --noEmit exit 0; npx eslint . --ext .ts,.tsx exit 0; npx jest --ci --coverage exit 0 (Test Suites 31 of 31, Tests 411 of 411, thresholds met); npx jest --ci --coverage --no-cache --maxWorkers=2 --verbose exit 0 (411 of 411; way-back tests 5 to 23 ms). Control-byte grep over changed files had no hit. PR check-runs on 0f04604, read once: Lint & Type Check completed/success; Code Quality in_progress (not polled further, per contract).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm; mobile has no dogfood battery", "mcp_calls": "0", "api_writes": "round 1: 1 — this os-dev-report comment via post-stamped.mjs (relay; POST /repos/objectstack-ai/mobile/issues/156/comments). git push of d7aaa22 and 0f04604 is not REST. PR body not written (replacement at the scratchpad path in summary). Cumulative: 3.", "line_budget": "n/a — no skills/** or ledger file touched. git diff --shortstat 29c25df..0f04604: 16 files changed, 1222 insertions(+), 33 deletions(-)", "files_changed": [ ".changeset/156-connect-qr-scanner.md", "__tests__/components/QrScanner.test.tsx", "__tests__/components/ServerConfigScreen.test.tsx", "__tests__/lib/shell/incoming-link.test.ts", "app.config.ts", "app/+native-intent.tsx", "app/connect-scan.tsx", "app/server-config.tsx", "components/shell/QrScanner.tsx", "docs/HYBRID-SHELL.md", "lib/shell/incoming-link.ts", "locales/ar.json", "locales/en.json", "locales/zh.json", "package.json", "pnpm-lock.yaml" ], "deviations": [ "Android back on the address step also takes the way back (only under the same condition). The fix only asked for a visible way back; I added this because the coordinator named Android back as part of the trigger.", "The worktree was removed after round 0 and rebuilt from origin/claude/issue-156-connect-qr-scanner; origin/main was merged in before editing, as a normal merge with no rebase." ], "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · Expo `locales` also writes empty values-b+en / values-b+zh+Hans / values-b+ar strings.xml on Android (measured by prebuild in round 0); harmless; in PR Acceptance notes only" ] }objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
ACCEPT → PR #168 (head
0f04604, after patch round 1 added the way back to the saved server). Ready for review; the maintainer is requested. The maintainer merges; a native rebuild follows.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Landed. #168 merged as
728b3c5, merged byos-zhuang, from head0f04604(CI green). The card closedcompletedviaFixes #156.pm:dispatchedremoved.expo-camerais a new native module, so an OTA update cannot carry it.scan()— barcode / QR scanning for Console fields #146, the bridgescan()reusingcomponents/shell/QrScanner.tsx. Dispatched now.