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
Console: an "App lock" entry on the profile page when the mobile shell exposes openAppLockSettings() #29748
Filing category ③: a direct task from a maintainer ruling. The ruling is objectstack-ai/mobile#145 comment 6593856498: Ruling: batch #1 item 2 · letter A · maintainer 「同意」. Under option A, the lock is changed only on the shell's native settings screen. The Console reads it and opens that screen; it never writes the lock.
What the shell offers
The shell side is objectstack-ai/mobile#181; the contract is in mobile docs/HYBRID-SHELL.md, section "App lock".
getAppLock() resolves { enabled: boolean, timeoutSeconds: number }.
openAppLockSettings() opens the native settings screen. Changing the setting there asks for biometrics.
Feature-detect each with has('getAppLock') and has('openAppLockSettings'). Only pages on the configured server's origin may call them.
No event announces a change. Re-read getAppLock() when the profile page is shown.
Ask (Console, ui/)
On the user profile / personal settings page, when has('openAppLockSettings'), show an "App lock" row with its current state from getAppLock(). Tapping it calls openAppLockSettings().
In a plain browser or an older shell, show nothing.
⛔ No Console-side toggle. The ruling keeps all writes native.
Business reason
Who benefits: phone users who expect the lock under their profile settings, where other apps put it.
If not done: the lock stays reachable only from the shell's connection switcher.
Who acts
The domain:ui seat. Triage decides the grade.
Duplicate check (2026-11-03)
is:issue openAppLockSettings in this repo returns 0 hits.
Read fresh. An "App lock" row on the profile page when the shell exposes openAppLockSettings(). The Console reads the lock and never writes it. Maintainer-ruled A (mobile#145). The lock itself already works in the shell, so this is discoverability: p3.
Claim: PM loop round 19 · 2026-11-03T04:16Z
Session: session_01QsR2jSM2R4osc3dyaqPHY1 (card thread, os-dev); dispatched by seat session session_01QsR2jUJ19MstFvFhjgiYTu
Account: zhuangjianguo
Branch: claude/project-thread-y37yg8 (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29748
Domain: domain:ui
Seat: domain:ui#1
File surface: ui/packages/mobile/src/nativeHost.ts (two capability union members, optional getAppLock? / openAppLockSettings? on the host interface, a NativeAppLock type, nativeGetAppLock, nativeOpenAppLockSettings), ui/packages/mobile/src/index.ts (their exports), ui/packages/mobile/src/__tests__/nativeHost.test.tsx, ui/packages/mobile/README.md, ui/apps/console/src/pages/system/ProfilePage.tsx (an App lock card only when nativeHostCan(host, 'openAppLockSettings'): read on mount, on visibilitychange to visible and on window focus; tap calls nativeOpenAppLockSettings; no toggle), new ui/apps/console/src/pages/system/__tests__/ProfilePage.appLock-29748.test.tsx, the keys profile.appLock.{title,off,onImmediately,onAfter} in all ten packs under ui/packages/i18n/src/locales/, and a root .changeset (patch). Any other file is declared by a claim amendment before code (stop on breach; explain in the report)
Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: yes (widening): new optional host members and exports in @object-ui/mobile and four new i18n keys; nothing removed or narrowed
Metadata-protocol: none
Responsibility: the Console App lock row (read-only, opens the native screen); every lock write stays in the shell per mobile#145 ruling A
Thread-read: 6595074906
Premise: holds (at origin/main7ba8b11f: the bridge reader is @object-ui/mobile nativeHost.ts (nativeHostCan + native* wrappers) with no app-lock members yet; the profile page is console ProfilePage.tsx; the contract is mobile PR #181 HYBRID-SHELL.md "App lock", not yet on mobile main)
Serial constraints cleared: none blocking. Seat 3 #29735 (claim 6595193307) edits the same nativeHost.ts union, host interface, index.ts and test fake: whichever lands second merges main and keeps both sets of members. No open PR touches these files at claim time.
{
"issue": 29748,
"status": "done",
"branch": "claude/project-thread-y37yg8",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29798",
"session": "session_01QsR2jSM2R4osc3dyaqPHY1",
"premise_still_valid": true,
"summary": "Profile page (/system/profile and account:profile_card) gains an App lock card, drawn only when nativeHostCan(useNativeHost(), 'openAppLockSettings'). It shows Off, On (immediately) or On with an Intl-named duration from getAppLock(), re-read on mount, visibilitychange to visible and window focus; a tap calls nativeOpenAppLockSettings(); no toggle and no write. @object-ui/mobile adds the two optional host members, NativeAppLock and the nativeGetAppLock / nativeOpenAppLockSettings wrappers (README updated); profile.appLock.{title,off,onImmediately,onAfter} in all ten packs. Contract read at objectstack-ai/mobile#181 head 77ded7bb (not on mobile main). One in-surface correction after the first run: the duration fell back to the machine locale (machineLocaleCensus-9909 red), now UI language then useDisplayLocale() (commit 75f435ad).",
"tests": "Head 75f435ad. New ProfilePage.appLock-29748.test.tsx: 9 passed; on main's ProfilePage.tsx (7ba8b11f, tree-only restore, git diff HEAD empty after) 6 failed / 3 passed (every shell case red, 3 controls green). Ablation via ablation-replace --delete of the visibilitychange + focus listener lines: 1 failed / 8 passed (the re-read case), restored blob == HEAD. vitest packages/i18n/ packages/mobile/ + five ProfilePage suites: Test Files 109 passed, Tests 1633 passed, 13 skipped. turbo type-check console+mobile+i18n: 73/73. eslint on five changed code files: 0 errors, 1 pre-existing warning (ProfilePage name-sync set-state-in-effect).",
"gates": "dispatch-gates --commands at 75f435ad: 35 families. 34 non-vitest run with exit captured before pipes, GITHUB_EVENT_PATH = the PR body: all exit 0. pnpm --dir ui exec vitest run scripts/: 170 files passed, 2 skipped, exit 0. check:new-line-citations (ui/): 0 new. Body gates before pr_create: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword all exit 0. CI check-runs: in_progress at report time. Not merged with main: 18 commits behind, git merge-tree clean.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "9 — GitHub MCP issue_read x5 (29748 get + comments, 29735, 29750), get_session, add_repo (read-only, mobile public); no MCP write tool",
"api_writes": "2 — git push (branch); fleet-write pr_create (run 41582956881, PR #29798, body read back byte-identical). Plus this os-dev-report comment via post-stamped.",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: b · reach: named producer — objectstack-ai/mobile app/console.tsx openAppLockSettings: () => router.push('/app-lock-settings') pushes a native screen inside the same activity; HYBRID-SHELL.md 'Reading the app lock' says no event announces a change, and an Android WebView fires no visibilitychange for an in-activity push, so a Console that re-reads on visibility/focus can show the old state after the user returns from the settings screen until it remounts. Contract text: 'No bridge method changes the lock' / card: 'No event announces a change. Re-read getAppLock() when the profile page is shown.' Seam: shell:openAppLockSettings → renderer:console ProfilePage AppLockCard. Not reproduced on a device (no device here); measured only as the absence of any shell event in the contract. · dedupe words: appLockChanged, app lock event, getAppLock stale, visibilitychange WebView",
"carrier: #29735 · serial merge on nativeHost.ts union/interface/index.ts/test fake, noted, not filed"
],
"deviations": "Clause-② line copied as the seat's handshake reply spelled it ('Clause-②: yes (widening)'), not with the claim line's trailing explanation. PR has no assignees/reviewers (os-dev: none at pr_create; the harness's assignee/reviewer MCP step skipped as an MCP write). No PR subscription: re-runs and landing are the seat's."
}
Landed · 2026-11-03T05:55Z · PR #29798 merged through the merge queue as b62463f1 at 2026-11-03T05:54Z (GitHub's clock). ACCEPT 6596343019 and contract review 6596159679 (PASS) at head 75f435ad.
probe
before (b62463f1~1)
now (origin/main)
export function nativeGetAppLock in ui/packages/mobile/src/nativeHost.ts
0
1
function AppLockCard in ui/apps/console/src/pages/system/ProfilePage.tsx
0
1
Fixes #29748 closed this card. pm:dispatched comes off in the same stroke as this note. Serial: seat 3's #29803 (#29735) now merges main and keeps both member sets in NativeHostMethod, ObjectStackNative and index.ts. The follow-up is #29800, for the repo:mobile seat: the shell sends no app-lock change event, so the row can show the old state after an in-activity Android push.
Activity
objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p3 ·
domain:ui·pm:queue· 2026-11-03T03:58Z⛔ Not a claim.
openAppLockSettings(). The Console reads the lock and never writes it. Maintainer-ruled A (mobile#145). The lock itself already works in the shell, so this is discoverability: p3.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Claim: PM loop round 19 · 2026-11-03T04:16Z
Session:
session_01QsR2jSM2R4osc3dyaqPHY1(card thread, os-dev); dispatched by seat sessionsession_01QsR2jUJ19MstFvFhjgiYTuAccount:
zhuangjianguoBranch:
claude/project-thread-y37yg8(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29748Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/mobile/src/nativeHost.ts(two capability union members, optionalgetAppLock?/openAppLockSettings?on the host interface, aNativeAppLocktype,nativeGetAppLock,nativeOpenAppLockSettings),ui/packages/mobile/src/index.ts(their exports),ui/packages/mobile/src/__tests__/nativeHost.test.tsx,ui/packages/mobile/README.md,ui/apps/console/src/pages/system/ProfilePage.tsx(an App lock card only whennativeHostCan(host, 'openAppLockSettings'): read on mount, onvisibilitychangeto visible and on window focus; tap callsnativeOpenAppLockSettings; no toggle), newui/apps/console/src/pages/system/__tests__/ProfilePage.appLock-29748.test.tsx, the keysprofile.appLock.{title,off,onImmediately,onAfter}in all ten packs underui/packages/i18n/src/locales/, and a root.changeset(patch). Any other file is declared by a claim amendment before code (stop on breach; explain in the report)Container & model: S,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: yes (widening): new optional host members and exports in @object-ui/mobile and four new i18n keys; nothing removed or narrowed
Metadata-protocol: none
Responsibility: the Console App lock row (read-only, opens the native screen); every lock write stays in the shell per mobile#145 ruling A
Thread-read: 6595074906
Premise: holds (at
origin/main7ba8b11f: the bridge reader is @object-ui/mobile nativeHost.ts (nativeHostCan + native* wrappers) with no app-lock members yet; the profile page is console ProfilePage.tsx; the contract is mobile PR #181 HYBRID-SHELL.md "App lock", not yet on mobile main)Serial constraints cleared: none blocking. Seat 3 #29735 (claim 6595193307) edits the same nativeHost.ts union, host interface, index.ts and test fake: whichever lands second merges main and keeps both sets of members. No open PR touches these files at claim time.
objectstack-fleet commentedon Nov 3, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jSM2R4osc3dyaqPHY1
os-dev-report
{ "issue": 29748, "status": "done", "branch": "claude/project-thread-y37yg8", "pr": "https://github.com/objectstack-ai/objectstack/pull/29798", "session": "session_01QsR2jSM2R4osc3dyaqPHY1", "premise_still_valid": true, "summary": "Profile page (/system/profile and account:profile_card) gains an App lock card, drawn only when nativeHostCan(useNativeHost(), 'openAppLockSettings'). It shows Off, On (immediately) or On with an Intl-named duration from getAppLock(), re-read on mount, visibilitychange to visible and window focus; a tap calls nativeOpenAppLockSettings(); no toggle and no write. @object-ui/mobile adds the two optional host members, NativeAppLock and the nativeGetAppLock / nativeOpenAppLockSettings wrappers (README updated); profile.appLock.{title,off,onImmediately,onAfter} in all ten packs. Contract read at objectstack-ai/mobile#181 head 77ded7bb (not on mobile main). One in-surface correction after the first run: the duration fell back to the machine locale (machineLocaleCensus-9909 red), now UI language then useDisplayLocale() (commit 75f435ad).", "tests": "Head 75f435ad. New ProfilePage.appLock-29748.test.tsx: 9 passed; on main's ProfilePage.tsx (7ba8b11f, tree-only restore, git diff HEAD empty after) 6 failed / 3 passed (every shell case red, 3 controls green). Ablation via ablation-replace --delete of the visibilitychange + focus listener lines: 1 failed / 8 passed (the re-read case), restored blob == HEAD. vitest packages/i18n/ packages/mobile/ + five ProfilePage suites: Test Files 109 passed, Tests 1633 passed, 13 skipped. turbo type-check console+mobile+i18n: 73/73. eslint on five changed code files: 0 errors, 1 pre-existing warning (ProfilePage name-sync set-state-in-effect).", "gates": "dispatch-gates --commands at 75f435ad: 35 families. 34 non-vitest run with exit captured before pipes, GITHUB_EVENT_PATH = the PR body: all exit 0. pnpm --dir ui exec vitest run scripts/: 170 files passed, 2 skipped, exit 0. check:new-line-citations (ui/): 0 new. Body gates before pr_create: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword all exit 0. CI check-runs: in_progress at report time. Not merged with main: 18 commits behind, git merge-tree clean.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "9 — GitHub MCP issue_read x5 (29748 get + comments, 29735, 29750), get_session, add_repo (read-only, mobile public); no MCP write tool", "api_writes": "2 — git push (branch); fleet-write pr_create (run 41582956881, PR #29798, body read back byte-identical). Plus this os-dev-report comment via post-stamped.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: b · reach: named producer — objectstack-ai/mobile app/console.tsx openAppLockSettings: () => router.push('/app-lock-settings') pushes a native screen inside the same activity; HYBRID-SHELL.md 'Reading the app lock' says no event announces a change, and an Android WebView fires no visibilitychange for an in-activity push, so a Console that re-reads on visibility/focus can show the old state after the user returns from the settings screen until it remounts. Contract text: 'No bridge method changes the lock' / card: 'No event announces a change. Re-read getAppLock() when the profile page is shown.' Seam: shell:openAppLockSettings → renderer:console ProfilePage AppLockCard. Not reproduced on a device (no device here); measured only as the absence of any shell event in the contract. · dedupe words: appLockChanged, app lock event, getAppLock stale, visibilitychange WebView", "carrier: #29735 · serial merge on nativeHost.ts union/interface/index.ts/test fake, noted, not filed" ], "deviations": "Clause-② line copied as the seat's handshake reply spelled it ('Clause-②: yes (widening)'), not with the claim line's trailing explanation. PR has no assignees/reviewers (os-dev: none at pr_create; the harness's assignee/reviewer MCP step skipped as an MCP write). No PR subscription: re-runs and landing are the seat's." }Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Landed · 2026-11-03T05:55Z · PR #29798 merged through the merge queue as
b62463f1at 2026-11-03T05:54Z (GitHub's clock). ACCEPT 6596343019 and contract review 6596159679 (PASS) at head75f435ad.b62463f1~1)origin/main)export function nativeGetAppLockinui/packages/mobile/src/nativeHost.tsfunction AppLockCardinui/apps/console/src/pages/system/ProfilePage.tsxFixes #29748closed this card.pm:dispatchedcomes off in the same stroke as this note. Serial: seat 3's #29803 (#29735) now mergesmainand keeps both member sets inNativeHostMethod,ObjectStackNativeandindex.ts. The follow-up is #29800, for the repo:mobile seat: the shell sends no app-lock change event, so the row can show the old state after an in-activity Android push.