Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .agents/skills/codex-app-parity/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1225,3 +1225,73 @@ After each feature implementation session that uses this skill:
- CX-Codex previously combined a running shortcut, pinned shortcut, optional chronological mode, project grouping, pinned-project priority, and manual project reordering. On the real 7420 sidebar this placed stale pinned projects above the active `codexui` directory and made one conversation appear in several navigation collections.
- The user explicitly chose a simpler fixed model, so CX-Codex intentionally narrows rather than copies the full Codex option set: `置顶` conversations, then `目录`, then directory conversations. All three levels use the latest conversation timestamp; running, waiting, and unread remain row state rather than sorting priority or a separate collection.
- Pinned conversations remain discoverable inside their owning directory, preserving project context. Directory collapse, search, current-thread reveal, thread menus, synchronization, routes, persistence, Runtime Store, and Android lifecycle ownership are unchanged; stored project pin/order metadata is retained for compatibility but no longer affects or advertises the fixed recent presentation.

## Findings: Persistent Plan and Thread-goal Parity (2026-08-02)

- Installed Windows Codex `26.721.4979` treats Plan as a persistent collaboration mode, consumes `turn/plan/updated` plus `item/plan/delta`, and offers `是,实施此计划`; it owns long-running work through `thread/goal/get`, `thread/goal/set`, `thread/goal/clear`, `thread/goal/updated`, and `thread/goal/cleared` rather than by injecting ordinary user text.
- CX-Codex previously reset Plan to execute after every submission and only exposed an unrelated one-turn requirement. Plan output was flattened or hidden, and no frontend owner hydrated or controlled the App Server's thread-goal state.
- CX-Codex now preserves Plan until the user disables it, merges structured plan events into one expandable step card, and switches to execute only when the latest eligible plan is explicitly implemented. Historical plan items remain readable through the same card fallback.
- A compact thread-goal bar now hydrates authoritative state per conversation and supports create, edit, pause, resume, clear, token/time progress, refresh recovery, and idle continuation. Continuation waits for message hydration and yields to running turns, queued work, and pending requests; a user stop pauses the active goal to prevent immediate restart.
- Follow-up UX hardening keeps Plan visibly dismissible above the composer, coalesces plan text deltas on the existing 48 ms live-update cadence, preserves an explicit submitted state after implementation, defaults older plans closed, and previews long plans around the active step.
- Thread-goal reads are deduplicated per thread and guarded by a state generation so a late read cannot overwrite a notification or edit. Mobile keeps pause/resume direct, moves edit/clear behind a compact menu, confirms clear inline with the objective, and exposes local retry without dropping the editor draft.
- Headless Playwright verified one plan card, three structured steps, the implementation action, active-goal controls, and zero horizontal overflow at phone and desktop sizes. The full 34-surface 7420 frontend regression, frontend normalizer smoke, server-module smoke, and production frontend build passed. The existing one-turn requirement remains separate and is now labeled explicitly as one-time.

## Findings: Plan and Goal Interaction Hardening (2026-08-03)

- Installed Windows Codex `26.721.4979` keeps Plan as a collaboration mode while long-running work remains thread-owned goal state. CX-Codex now states that coexistence directly: Plan changes how the next message is handled, while the active goal continues independently and can still be paused.
- Plan implementation state now survives refresh and cross-client synchronization by deriving `已提交执行` from the exact first implementation-confirmation message after that plan, while a newer plan closes the eligibility window. Long cards disclose collapsed context as `已隐藏 N 步` instead of silently omitting steps.
- Thread switches now reset goal-editor drafts, overflow menus, pending clear confirmations, and focus ownership. Goal-menu arrow/Home/End/Escape behavior, durable destructive confirmation, 44 px phone actions, visible mobile usage, and explicit pending labels match the shared Codex interaction boundaries while adapting them to the narrower 7420 surface.
- Headless Playwright at 393 x 852 and 1280 x 800 verified coexistence copy, cross-thread isolation, keyboard focus restoration, clear confirmation after 6.2 seconds, history-derived implementation state, visible hidden-step count, 44 px actions, and zero horizontal overflow.

## Findings: Durable Mobile Queue and Message Fidelity (2026-08-03)

- Desktop-grade conversation ownership requires queued work to remain thread-owned after the renderer disappears. CX-Codex now adapts that boundary to Android by persisting queue records in the 7420 Runtime Store and advancing them from a server coordinator, independent of the selected route, WebView, or Activity lifecycle.
- A queued row becomes an optimistic user bubble only when the server announces that it is starting. Its stable client id and pre-send message anchor then remain in the durable outbox until authoritative history contains the matching user item, preventing assistant output from appearing above a missing prompt after refresh.
- Manual conversation titles now have explicit durable ownership and reject later automatic name notifications. Generated images prefer the saved local path over oversized raw base64 payloads and expose a real remounting retry action on failure.
- Phone Markdown tables retain the semantic desktop table and use a focusable horizontal scroll owner instead of changing rows into cards. Headless Playwright at 393 x 852 measured 322 px visible width over 620 px content, 13 px cells, zero table-card DOM, and no browser console errors.

## Findings: Quiet Docked Android Task Pet (2026-08-03)

- Installed Windows Codex `26.721.4979` keeps exact-conversation navigation on the activity owner through `localConversationId` and `open-in-main-window`, carries a bounded notification count on the avatar frame, and distinguishes waiting (`Needs input`) from settled review (`Ready`).
- CX-Codex adapts those state and navigation boundaries to Android instead of reproducing the desktop window: the edge pet is a compact attention owner, a new assistant item gets one transient reply peek, a tap opens a bounded task stack, and full reading or input stays in the conversation.
- Unread ownership is now a persisted reply-event cursor. Stream chunks from the same assistant item update visible text without extending the five-second interruption window; opening the exact conversation advances the cursor and prevents stale completion attention after restart.
- The app foreground is deliberately quiet, restart returns to the docked state, and transient panel/peek state is not persisted. The native monitor remains authoritative in the background, while notification actions continue to provide the system-level fallback.

## Findings: Restart-safe Queued Speed Handoff (2026-08-03)

- A real failed 7420 queue record showed `queued -> queue_failed` in 53 ms with `Turn start was not confirmed after bridge restart`; its prompt was absent from the full authoritative thread history. The queued coordinator had marked the request `pending_start` before `config/batchWrite`, while that configuration call could restart app-server and trigger pending-start reconciliation.
- Queue speed configuration now remains preflight work. A row stays `queued` until any required configuration change has completed, so restart reconciliation cannot mistake preflight for a dispatched turn. A deletion during preflight is re-read and cannot be resurrected as `pending_start`.
- `config/read` suppresses no-op speed writes. Standard speed already represented by `service_tier: null` no longer performs the configuration write that triggered the observed restart; fast mode still enables its feature and changes service tier only when necessary.
- This preserves the desktop queue contract: genuine first-item failures still pause for retry, edit, or delete, while renderer-independent server ownership and stable `clientMessageId` ordering remain unchanged.

## Findings: Structured File Citations and Legacy PDF Preview (2026-08-03)

- Installed Windows Codex `26.721.4979` parses `codex-file-citation` as structured content, displays the basename, keeps the complete path as secondary context, and does not expose the transport directive or descriptive metadata in conversation text.
- CX-Codex previously let its broad plain-path matcher consume the directive prefix, which converted a valid absolute resume path into a nonexistent workspace-relative path. Historical local messages contain the same directive across PDF, DOCX, XLSX, and PPTX outputs, so the fix is a shared parser rather than a PDF-only string replacement.
- CX-Codex now adapts the desktop citation boundary to mobile: the basename is the compact link label, the full path remains the title and open target, and internal links stay in the current Android WebView. Invalid or incomplete directives degrade to safe text.
- The current `pdfjs-dist` standard build requires `Promise.withResolvers` and `Math.sumPrecise`, which are absent in some Android WebViews. The isolated local preview now uses PDF.js's official legacy main and worker bundles so compatibility applies on both execution contexts.
- Headless phone verification at 393 x 852 showed no protocol or metadata leakage, no document overflow, two rendered resume pages, and no browser errors or compatibility warnings. Evidence is under `output/regression-7420/file-citation-20260803`.

## Findings: External-session Progress and Bounded History Recovery (2026-08-03)

- Installed Windows Codex `26.727.51351` keeps request dispatch, turn start, first data, and first visible response as separate milestones. Its avatar overlay retains the owning `localConversationId`, opens through `open-in-main-window`, and keeps queued messages as a distinct composer-adjacent surface with retry and steer states.
- CX-Codex must preserve those ownership boundaries when mirroring a task executed by another desktop process. A session-file append is authoritative new progress, but it must not make 7420 re-read the complete rollout or invent a local Runtime-running state.
- Default recent-message and Runtime reads now use a bounded session-log projection. High-frequency response, reasoning, plan, command, file, MCP, and process deltas no longer evict that projection; explicit full-history and older-turn reads still use the authoritative App Server.
- Session-log recovery retains `phase: commentary`, so a progress-only tail renders under phase replies instead of becoming a fake final answer or an empty result. Native Task Pet reply freshness remains tied only to a newer `latestReplyEventSeq`.
- A 72 MB real conversation previously issued 6.7-8.0 second heavy reads every 8-10 seconds. After the boundary change, a fresh reload plus 35 seconds of live external updates produced zero heavy reads; light metadata reads peaked at 24 ms, with no RPC queue, pending server request, or uncertain Runtime request.

## Findings: Task-pet Wake and Lossless Session Convergence (2026-08-03)

- Installed Windows Codex `26.727.6591` still owns avatar attention through an exact `localConversationId`, dispatches `open-in-main-window`, presents a bounded notification count, and labels waiting work separately from review-ready work.
- A docked CX-Codex task pet previously stayed minimized when work arrived after an idle snapshot, and running-task replies were excluded from its badge. It now restores on task arrival, shows one five-second reply peek, counts unread reply cursors alongside waiting tasks, and clears only the exact conversation that is opened.
- Empty expanded stacks now return to the minimized pet after eight seconds. Close confirmation pauses that timer and cancellation starts a fresh timeout, matching the native Android interaction owner instead of allowing a destructive prompt to disappear mid-decision.
- Session-log ownership is split deliberately: the bounded file projection supplies immediate text, but it is not treated as structurally complete. File notifications reset a 1.8-second quiet window, after which one coalesced authoritative read restores plans, commands, file changes, approvals, and tool items without returning to per-append heavy reads.
- Real 7420 browser checks covered idle-to-task wake, `2 -> 1` unread acknowledgement, exact `fixture-running` navigation, empty-stack minimization, and close-confirmation timer ownership. Frontend normalization, production build, CLI build, server-module smoke, Android policy tests, and the full frontend regression remained the verification boundary.

## Findings: Queue Generation Isolation and Stable Local Merge (2026-08-04)

- Installed Windows Codex `26.727.6591` keeps queued work as a composer-adjacent list with stable message identity, explicit retry/edit/delete ownership, and first-failure blocking. Queue UI changes do not impersonate App Server turn lifecycle events.
- CX-Codex had two ownership leaks: its internal `runtime/queue/updated` broadcast reconciled a newly claimed request against the previous turn's completed snapshot, and a second-precision pre-start `thread/read` could release the active lease before `turn/start` materialized.
- Internal queue notifications now remain replayable UI signals but cannot mutate or reconcile Runtime state. A same-second non-running thread snapshot is ignored during a bounded 20-second materialization window, keeping queued work isolated behind the real active turn without making restart recovery permanent.
- Frontend persistence acknowledgements merge into the latest queue by stable `clientMessageId`, cancel server rows deleted during an in-flight write, and stop behind the first failed local row. After a complete sync, an exact-set server reorder makes the retained UI order authoritative without letting stale or partial client state move another client's message.
- A real controlled 7420 run recorded `BASE2 -> QUEUE2-A -> QUEUE2-B` as three distinct turns. A/B reached first token in 2.27/1.85 seconds, while a separate direct no-tool reply reached first token in 1.71 seconds; no queued prompt was lost or injected into the previous turn. A second run reordered `A -> B -> C` into `C -> A -> B`, and both the immediate server list and authoritative history followed that exact order.
37 changes: 37 additions & 0 deletions .opencodereview/rule.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"include": [
"android/app/src/test/**/*.java",
"**/*Test.java",
"**/*Tests.java",
"**/*.{test,spec}.{ts,tsx,js,jsx,mjs,cjs}"
],
"exclude": [
"node_modules/**",
"dist/**",
"dist-cli/**",
"coverage/**",
"android/**/build/**"
],
"rules": [
{
"path": "src/server/**/*.{ts,js}",
"rule": "Preserve the CLI -> Express bridge -> Codex app-server contract. Treat session-log reads as potentially lossy fallback data, keep normal thread/read responses authoritative, and verify cache invalidation, thread identity, response-view windows, abort handling, and per-thread ordering. Flag optimizations that can hide structured messages such as plans, command executions, file changes, approvals, or tool calls.",
"merge_system_rule": true
},
{
"path": "src/**/*.{ts,vue}",
"rule": "Preserve Vue reactivity and the desktop/mobile runtime state contract. Check cleanup of timers, listeners, and abort controllers; guard stale async responses by thread identity or version; retain optimistic messages until authoritative reconciliation; and avoid visual-only changes that diverge from the installed Codex desktop behavior.",
"merge_system_rule": true
},
{
"path": "android/app/src/**/*.java",
"rule": "Preserve Android foreground-service, notification, overlay, and activity lifecycle behavior. Check main-thread UI access, background execution restrictions, PendingIntent identity, configuration changes, accessibility timeouts, lock-screen privacy, resource cleanup, and parity between native task-pet state and the web preview.",
"merge_system_rule": true
},
{
"path": "scripts/**/*.{ts,mjs,js,ps1}",
"rule": "Keep verification deterministic and behavior-based. Distinguish expected diagnostic logs from failures, avoid timing-only assertions when state can be observed directly, preserve Windows PowerShell compatibility, and make regression checks fail with a specific actionable message.",
"merge_system_rule": true
}
]
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Self-hosted OpenAI Codex Web UI and Android client bridge.
## 快速入口

- 最新 Release: [github.com/Qjzn/CX-Codex/releases/latest](https://github.com/Qjzn/CX-Codex/releases/latest)
- 2.7.3 发布说明: [docs/release-notes-2.7.3.zh-CN.md](./docs/release-notes-2.7.3.zh-CN.md)
- 2.7.2 发布说明: [docs/release-notes-2.7.2.zh-CN.md](./docs/release-notes-2.7.2.zh-CN.md)
- 2.6.0 发布说明: [docs/release-notes-2.6.0.zh-CN.md](./docs/release-notes-2.6.0.zh-CN.md)
- 2.5.9 发布说明: [docs/release-notes-2.5.9.zh-CN.md](./docs/release-notes-2.5.9.zh-CN.md)
Expand Down
21 changes: 21 additions & 0 deletions android/app/src/main/java/com/cxcodex/bridge/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
public class MainActivity extends BridgeActivity {

private static final long CONNECTION_TIMEOUT_MS = 15000L;
private static volatile boolean appForeground;
private boolean initialCreateComplete;
private boolean mainFrameLoadFailed;
private LinearLayout connectionOverlay;
Expand Down Expand Up @@ -84,6 +85,26 @@ public void onResume() {
openPendingTaskPetThread();
}

@Override
public void onStart() {
super.onStart();
appForeground = true;
TaskPetOverlayService.refreshPresentation(this);
}

@Override
public void onStop() {
if (!isChangingConfigurations()) {
appForeground = false;
TaskPetOverlayService.refreshPresentation(this);
}
super.onStop();
}

static boolean isAppForeground() {
return appForeground;
}

private void captureTaskPetThreadFromIntent(Intent intent) {
if (intent == null || !intent.hasExtra(TaskPetOverlayService.EXTRA_THREAD_ID)) {
return;
Expand Down
Loading