Repository navigation
Closed
Labels
enhancementNew feature or requestNew feature or request
Description
Context
objectstack-ai/objectstack#29140 merged the Console's shell mode (card objectstack-ai/objectstack#29039). An acceptance review checked it against this repository's main (8c6f13a): the bridge contract matches on both sides. The review also found that the shell does not yet use the signals the Console now sends.
- Loading cover.
onLoadEndlifts the cover (components/shell/ConsoleWebView.tsx:316-320), usually before the Console postsready. Soreadyhas no visible effect. - Held route on cold start.
markLoadedruns atonLoadEndand dispatches the held route at once (ConsoleWebView.tsx:146-157).- The Console's navigate listener mounts only after its boot round-trips (objectstack
ui/apps/console/src/main.tsx:168-176). - So the event goes unhandled and the shell falls back to
location.assign. A notification tap on cold start reloads the page.
- Last route. The shell breadcrumbs
routeChanged(ConsoleWebView.tsx:233-236) but takes the last route fromonNavigationStateChange(:275-281). That callback does not report SPApushStatereliably on every engine. - Safe area.
- The Console adds no padding: nothing under objectstack
ui/consumes--os-native-safe-*, and its README says the shell owns the padding. - The shell pads natively (
ConsoleWebView.tsx:298-300), so nothing is padded twice. - But
docs/HYBRID-SHELL.md:187says the Console should "pad with the safe-area variables", and:196-197calls native padding a stopgap.
- The Console adds no padding: nothing under objectstack
Ask
- Cover on
ready. Lift the loading cover on the Console'sreadyevent. Keep a timeout fallback (choose it in the PR) for older Consoles and for boot failures where the app never mounts. - Held route on
ready. Dispatch a held route afterready, not at load end, with the same timeout fallback. - Last route from
routeChanged. Use therouteChangedpayload as the source of the last route, which recovery uses after a WebView is re-created. KeeponNavigationStateChangeonly as the fallback for Consoles that never postrouteChanged. - Safe-area ownership. The shell keeps owning the padding.
- Update
docs/HYBRID-SHELL.md(lines 187 and 196-197) to say so, and that the CSS variables are informational. - If you find a reason to hand padding to the Console instead, stop and raise it on this card. One such reason: the shell's strip behind the status bar can clash with a dark Console theme. That change needs a handshake, so that an older Console does not lose its padding.
- Update
Note: ready currently fires at the Console's first router commit, which may be the Console's own loading fallback. objectstack-ai/objectstack#29330 settles what ready means. Lifting the cover onto a spinner the Console painted is acceptable.
Acceptance
- Unit tests cover the cover and held-route state machine:
readybefore and after load end, the timeout fallback, and a failed load. - A notification tap on cold start does not reload the page. This is a manual check on a development build, listed in the PR's 维护者速读.
docs/HYBRID-SHELL.mdmatches the shipped behaviour.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
First touch (single-lane repo). Type
enhancement, native shell behaviour.mainat7cb17d1: it holds. The line numbers shifted slightly after feat: rebrand the app to ObjectOS (name, objectos scheme, ai.objectos.mobile) #164.onLoadEnd→markLoadedis atcomponents/shell/ConsoleWebView.tsx:316-320.readyalso callsmarkLoaded(:234), so whichever event comes first wins.onNavigationStateChangeis at:275.insets.topandinsets.bottom(:286,:300).docs/HYBRID-SHELL.md.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Claim: PM loop round 1
Session:
session_01QSeKYnW1Xf6HDSWz1eGAqvAccount:
os-justinBranch:
claude/issue-162-ready-route-safe-areaWorktree:
mobile-issue-162Domain:
n/a — single-lane repo (repo:mobile)Seat:
repo:mobile#1File surface:
components/shell/ConsoleWebView.tsx, a state-machine module underlib/shell/if extracted,__tests__/**,docs/HYBRID-SHELL.md,.changeset/162-ready-route-safe-area.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: none
Premise: holds
Serial constraints cleared: #163 is dispatched in the same round. It touches
ConsoleWebView.tsxonly to deliver a window event, a different region from the cover and held-route logic.docs/HYBRID-SHELL.mdis shared with #154 and #163. Both overlaps are ordinary concurrency: the PR that lands later resolves the conflict.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
os-dev-report
{ "issue": 162, "status": "done", "branch": "claude/issue-162-ready-route-safe-area", "pr": "https://github.com/objectstack-ai/mobile/pull/166", "session": "session_01QSeKYnW1Xf6HDSWz1eGAqv", "premise_still_valid": true, "summary": "The loading cover, the held route and the last route now run through one pure state machine, lib/shell/console-load.ts: loading, then loaded at WebView load end, then ready on the Console's ready event or READY_TIMEOUT_MS = 8 s after load end, plus failed. ConsoleWebView only performs the effects the machine returns. A route asked for on cold start is now dispatched at ready, not at load end, and a ready that arrives before load end counts. The last route comes from the routeChanged payload (checked with safeConsoleRoute); onNavigationStateChange is used only until the first routeChanged. Safe area: no behaviour change. docs/HYBRID-SHELL.md now says the shell owns the padding and that --os-native-safe-* are informational, and it documents the load states. Ask-4 stop condition not triggered: nothing under objectstack ui/ reads --os-native-safe-*, and the Console's env(safe-area-inset-*) uses resolve inside the natively inset WebView frame. The dark-theme strip clash the card names stays unmeasured; it needs a device. PM assumptions verified on mobile main 7cb17d1: onLoadEnd calls markLoaded at :316-320, ready calls it at :234, onNavigationStateChange is at :275. Platform facts read at objectstack origin/main 2771575, since the local checkout d9885f5 is behind and these files are byte-equal in the two: the Console renders inside Promise.all(...).finally(async ...) at ui/apps/console/src/main.tsx:79-100, with the render at :168-176, so the card's citation holds. useNativeHostRouting (ui/packages/mobile/src/useNativeHost.ts:112-136) posts ready, posts routeChanged and mounts the navigate listener in three effects of one commit, so the listener is mounted by the time ready reaches the shell. routeChanged carries { route }: pathname, search and hash with the /_console basename stripped (ui/apps/console/src/App.tsx:155-158). Device check not run: it is listed under 需要你做的.", "tests": "head 2e52b7e. npx jest --ci --maxWorkers=2: exit 0, Test Suites 27 passed of 27, Tests 312 passed of 312. Targeted run: __tests__/lib/shell/console-load.test.ts (new, 20 cases) and __tests__/components/ConsoleWebView.test.tsx (updated, 25 cases): 45 of 45 passed. Ablation, one-time and not committed: the loadEnd transition in lib/shell/console-load.ts was mutated to `return becomeReady(state, [])`. Anchor counts were old=1 new=0 before and old=0 new=1 after. Ablated jest exited 1 with 8 failed and 37 passed of 45, including the cold-start held-route, cover-until-ready, timeout and failed-load cases. Restored with git checkout HEAD; the blob hash equals the HEAD blob 74e29d0 and git diff HEAD is empty. No build or dist step: jest compiles src through babel.", "gates": "local, head 2e52b7e, exit codes captured before any pipe: `npx tsc --noEmit` exit=0; `npx eslint . --ext .ts,.tsx` exit=0 with no output; `npx jest --ci --maxWorkers=2` exit=0. PR check-runs at head 2e52b7e, read once and not polled: `Code Quality` queued, `Lint & Type Check` in_progress. Both are in_progress, an honest value. dispatch-gates.mjs --commands/--ran not used: it is objectstack-only, and mobile has no such script. Gate set taken from the dispatch order.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "n/a — the diff touches no skills/** and no ratcheted ledger", "files_changed": [".changeset/162-ready-route-safe-area.md +19", "__tests__/components/ConsoleWebView.test.tsx +116 -11", "__tests__/lib/shell/console-load.test.ts +180", "components/shell/ConsoleWebView.tsx +85 -58", "docs/HYBRID-SHELL.md +53 -9", "lib/shell/console-load.ts +172"], "deviations": ["Local jest and tsc ran without os-verify-lock.sh. The lock script lives in objectstack scripts/pm, and this was a mobile-repo worktree; the runs were bounded to --maxWorkers=2.", "Commit trailers use the model-free pair (Claude-Session plus Co-authored-by: Claude) required by AGENTS.md, not the harness reminder's model-named Co-Authored-By."], "mcp_calls": "0", "api_writes": "2 — relay writes: (1) fleet-write dispatch pr_create into objectstack-ai/mobile, which created #166, run 41445774753 success, read-back byte-identical; (2) post-stamped.mjs comment os-dev-report on objectstack-ai/mobile#162. git push is not counted.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": ["carrier: #163 · noted, not filed — a window event the shell delivers into the page (#163's pushTokenChanged) is lost the same way a held route was when it is sent before the Console's listeners mount; #163 should gate delivery on this PR's ready phase. Whichever PR lands second resolves the ConsoleWebView.tsx overlap.", "carrier: none · noted, not filed (PR Acceptance notes only) — pre-existing and unchanged: after ready, a later full-document load (for example an older Console's location.assign fallback) does not raise the cover again, and a route asked for during that load is injected at once."] }objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
ACCEPT → PR #166 comment 6577343005 (head
2e52b7e). Ready for review; the maintainer is requested. The maintainer merges.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Landed: the first seat landing under the ruling on #160. #166 merged through the merge queue as
0fd7113; the merge was performed byobjectstack-fleet[bot]via auto-merge. Themerge_grouprun ongh-readonly-queue/main/pr-166-…passed bothLint & Type CheckandCI. The card closedcompletedviaFixes #162.pm:dispatchedremoved.The Android
onLoadStartre-arm of thepushTokenChangedhold that was onmainis gone with this merge. Next: #152 (saved connections) was serial behind this card and is dispatched now.