Skip to content

sync: port upstream server settle, tunnel/update safety, and early web polish (#5482–#5486) - #215

Merged
cursor[bot] merged 21 commits into
mainfrom
codex/sync-20260808-l1-server-settle-a13c
Aug 8, 2026
Merged

sync: port upstream server settle, tunnel/update safety, and early web polish (#5482–#5486)#215
cursor[bot] merged 21 commits into
mainfrom
codex/sync-20260808-l1-server-settle-a13c

Conversation

@aaditagrawal

@aaditagrawal aaditagrawal commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Bottom of the 2026-08-08 upstream sync stack (L1/7).

Fork was 71 unique commits behind upstream/main since ancestry a2ca89aa. This layer ports the first 18 (+ format fix).

What lands

Fork deviations preserved

  • ChatView keeps rateLimitThreads / RateLimitsView
  • #5431 applied in runtimeModePresentation.ts (fork extracted static runtimeModeConfig)

Verification

vp check and vp run typecheck pass on the branch tip.

Stacked below L2 (#216). Review bottom-up.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added clearer server update progress states: Downloading…, Restarting…, and retryable failure messages.
    • Added “Woke” controls for snoozed threads and improved wake-time notifications.
    • Added support for dynamic tool-call approval requests.
    • Improved activity displays by summarizing tool results and removing redundant updates.
  • Improvements

    • Plan-sidebar dismissal now persists across thread changes.
    • Snooze times respect 12-hour and 24-hour clock preferences.
    • Pending cards now use opaque surfaces for better readability.
    • Simplified server update buttons and guidance.

t3dotgg and others added 19 commits August 8, 2026 06:52
The version-skew banner is no longer an amber warning: it reads
"Server update available" with the raw versions (unreadable for
nightlies) moved to a tooltip. The in-flight rail (Download/Install/
Resume) becomes a single status row, "Downloading…" then
"Restarting…", since the wire installing stage is a sub-second
launcher handoff and "resuming" meant nothing to most people.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Measured on a live update: the server was back in ~9s but stayed
unreachable for ~96s, because releasing the tunnel on shutdown forces
the replacement tunnel's hostname route through 1-2 minutes of edge
propagation. An update handoff always brings a server right back
(new version or rollback), so the tunnel is never orphaned; skip the
release when the launcher state file records a pending update. The
next boot respawns the connector from the stored config against the
same tunnel and is reachable as soon as it connects.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pdate

A pending update in the launcher state is not proof a replacement
server is coming: `t3 service uninstall` or `systemctl stop` during
the pending window also tears the server down, permanently. The
launcher now writes a stop marker before signalling its child on an
explicit stop and clears it on the next start; the shutdown tunnel
release keeps the tunnel only for pending updates without the marker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A tool.updated row is the in-flight snapshot of a call; once the call
completes, the tool.completed row carries the final state and both
clients fold every matching update into it. Shipping the updates buys
nothing: 47k such rows exist in one real database, and a single thread
carries 3,291 of them.

Filter them out of thread snapshots, mirroring the existing
context-window dedup. Matching is per turn and only against a LATER
completion, so a revert that discards the completing turn cannot leave
a call unrepresented, and a later update under the same identity (the
next call, still in flight) survives. Live events are untouched.

Rows are matched on the same identity the clients collapse by: an
explicit data.toolCallId when the adapter emits one, otherwise the
itemType/title/detail triple. No tool lifecycle row in the real db
carries a toolCallId, so the fallback does the work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… test

Bugbot flagged that clients collapse only adjacent lifecycle rows, so
dropping a superseded update separated by an interleaved parallel call
diverges from full-history rendering. Measured on a real database: 1.5%
of dropped rows (553/36,581), all pure in-flight state whose final result
the retained completion still shows, and zero dropped rows carry a
client-merged payload field their completion lacks (verified across all
49,515 update rows). Documents the tradeoff and adds a test pinning it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 740d80ca-6123-46af-9d8a-997eaa3e56da

📥 Commits

Reviewing files that changed from the base of the PR and between ff2218f and f0b03e8.

📒 Files selected for processing (3)
  • apps/server/src/serviceLauncher.test.ts
  • apps/server/src/serviceLauncher.ts
  • docs/user/updating.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/user/updating.md
  • apps/server/src/serviceLauncher.ts
  • apps/server/src/serviceLauncher.test.ts

📝 Walkthrough

Walkthrough

Changes

Cloud service handoff

Layer / File(s) Summary
Launcher shutdown markers
apps/server/src/serviceLauncher.ts, apps/server/src/serviceLauncher.test.ts, apps/server/src/cloud/serviceProtocol.ts, apps/server/src/cloud/bootService.ts, apps/server/src/cloud/bootService.test.ts
The launcher writes an explicit-stop marker, avoids duplicate stop requests, clears stale markers during recovery, and uses KillMode=mixed.
Pending-update handoff detection
apps/server/src/cloud/http.ts, apps/server/src/cloud/http.test.ts
Cloud shutdown logic detects pending updates and preserves or releases tunnels and runtime configuration based on launcher state.
Managed-tunnel cleanup wiring
apps/server/src/server.ts
Cleanup registration now occurs before activation for pending updates and after activation for other runtimes.

Activity payload projection

Layer / File(s) Summary
MCP projection and snapshot filtering
apps/server/src/orchestration/ActivityPayloadProjection.ts, apps/server/src/orchestration/ActivityPayloadProjection.test.ts, apps/server/test/ActivityPayloadProjection.test.ts
MCP payloads are reduced to rendered fields and summarized results. Superseded tool.updated activities are removed from snapshots.

ACP approval classification

Layer / File(s) Summary
Dynamic tool-call approval mapping
apps/server/src/provider/acp/AcpCoreRuntimeEvents.ts, apps/server/src/provider/acp/AcpCoreRuntimeEvents.test.ts, apps/web/src/session-logic.test.ts
Known and unknown ACP permission kinds map to dynamic_tool_call, which becomes an actionable pending approval.

Server update interface

Layer / File(s) Summary
Update progress states and rendering
apps/web/src/components/ServerUpdateAction.tsx, apps/web/src/components/ServerUpdateAction.test.tsx
The step rail becomes a compact status row with “Downloading…”, “Restarting…”, and visible retryable failures.
Update banners and settings integration
apps/web/src/components/ChatView.tsx, apps/web/src/components/settings/ConnectionsSettings.tsx, docs/user/updating.md
Conversation and connection settings use shorter actions, version tooltips, revised guidance, and synchronized update status text.

Snooze and wake interaction

Layer / File(s) Summary
Persistent plan-sidebar dismissal
apps/web/src/planSidebarDismissal.ts, apps/web/src/components/ChatView.tsx
Plan-sidebar dismissal persists by thread and turn across remounts and thread transitions.
Timestamp-aware snooze formatting
apps/web/src/components/Sidebar.snooze.ts, apps/web/src/components/Sidebar.snooze.test.ts
Snooze presets and wake descriptions use the configured 12-hour or 24-hour timestamp format.
Woke-thread acknowledgement
apps/web/src/components/SidebarV2.tsx, apps/web/src/components/ChatView.tsx, apps/web/src/hooks/useThreadActions.ts
Woke statuses become dismissible, and successful thread actions acknowledge wake events.

Interface presentation polish

Layer / File(s) Summary
Composer chips and runtime wording
apps/web/src/components/composerInlineChip.ts, apps/web/src/components/ComposerPromptEditor.tsx, apps/web/src/components/chat/runtimeModePresentation.ts
Composer chip alignment and skill-label styling change. The automatic runtime description distinguishes provider behavior.
Opaque mobile card surfaces
apps/mobile/src/features/threads/PendingApprovalCard.tsx, apps/mobile/src/features/threads/PendingUserInputCard.tsx
Pending approval and input cards now use opaque light and dark surfaces.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the changes and verification, but it omits the required template sections, UI screenshots, and completed checklist. Add the required What Changed, Why, UI Changes, and Checklist sections, and include before/after screenshots for the UI changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the upstream sync and its main server, tunnel, update, and web changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/sync-20260808-l1-server-settle-a13c

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XL 500-999 effective changed lines (test files excluded in mixed PRs). labels Aug 8, 2026
Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

@coderabbitai review

@cursor
cursor Bot marked this pull request as draft August 8, 2026 07:27
@aaditagrawal
aaditagrawal marked this pull request as ready for review August 8, 2026 07:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
apps/server/src/orchestration/ActivityPayloadProjection.test.ts (1)

79-99: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the MCP files derivation.

projectMcpToolCallData now emits a files array for MCP payloads. Previously MCP data passed through unchanged, so files is a new key on the wire for this item type. Neither MCP test here nor the fixture in apps/server/test/ActivityPayloadProjection.test.ts contains a path-like key, so the branch never runs for MCP.

Add a case with a path in input or item.arguments and assert the projected files entries.

Guidelines require focused tests for changed backend behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/orchestration/ActivityPayloadProjection.test.ts` around lines
79 - 99, Extend the MCP projection test around projectActivityPayload to include
a path-like value in input or item.arguments, then assert that the projected
data.files array contains the expected derived file entry. Keep the existing
toolName, input, result, and payload-size assertions intact while covering the
projectMcpToolCallData files branch.

Source: Coding guidelines

apps/server/src/orchestration/ActivityPayloadProjection.ts (2)

434-466: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider storing only the last completion index per key.

The predicate on line 465 asks whether any completion index exceeds index. That is equivalent to comparing against the largest completion index for the key. Storing a single number removes the inner some scan and the array allocations.

The current form is correct. This is a simplification, not a fix.

♻️ Proposed simplification
-  const completionIndicesByKey = new Map<string, number[]>();
+  const lastCompletionIndexByKey = new Map<string, number>();
   for (let index = 0; index < activities.length; index += 1) {
     const activity = activities[index]!;
     if (activity.kind !== "tool.completed") {
       continue;
     }
     const identity = toolLifecycleIdentity(activity);
     if (!identity) {
       continue;
     }
-    const key = `${activity.turnId ?? ""} ${identity}`;
-    const indices = completionIndicesByKey.get(key);
-    if (indices) {
-      indices.push(index);
-    } else {
-      completionIndicesByKey.set(key, [index]);
-    }
+    // Indices increase monotonically, so the last write is the largest.
+    lastCompletionIndexByKey.set(`${activity.turnId ?? ""} ${identity}`, index);
   }
-  if (completionIndicesByKey.size === 0) {
+  if (lastCompletionIndexByKey.size === 0) {
     return activities;
   }
 
   return activities.filter((activity, index) => {
     if (activity.kind !== "tool.updated") {
       return true;
     }
     const identity = toolLifecycleIdentity(activity);
     if (!identity) {
       return true;
     }
-    const indices = completionIndicesByKey.get(`${activity.turnId ?? ""} ${identity}`);
-    return !indices?.some((completionIndex) => completionIndex > index);
+    const lastCompletionIndex = lastCompletionIndexByKey.get(
+      `${activity.turnId ?? ""} ${identity}`,
+    );
+    return lastCompletionIndex === undefined || lastCompletionIndex <= index;
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/orchestration/ActivityPayloadProjection.ts` around lines 434
- 466, Simplify completion tracking in the activity projection by changing
completionIndicesByKey to store only the latest completion index for each
tool-lifecycle key. Update the collection loop to overwrite the key with the
current index, and adjust the tool.updated filter predicate to compare that
single index directly instead of scanning an array with some.

225-229: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the changed-files block.

Lines 225-229 duplicate lines 294-299 exactly. Both call collectChangedFiles over the raw data and map the result to { path } records. A small helper keeps the two branches in sync if the bound or the mapped shape changes later.

Guidelines require extracting shared logic instead of duplicating local implementations.

♻️ Proposed helper extraction
+function projectChangedFiles(data: Record<string, unknown>): Array<{ path: string }> | undefined {
+  const changedFiles: string[] = [];
+  // Both clients discover file names by walking objects with path-like keys.
+  collectChangedFiles(data, changedFiles, new Set<string>(), 0);
+  return changedFiles.length > 0 ? changedFiles.map((path) => ({ path })) : undefined;
+}

Then in projectMcpToolCallData:

-  const changedFiles: string[] = [];
-  collectChangedFiles(data, changedFiles, new Set<string>(), 0);
-  if (changedFiles.length > 0) {
-    projectedData.files = changedFiles.map((path) => ({ path }));
-  }
+  const files = projectChangedFiles(data);
+  if (files) {
+    projectedData.files = files;
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/orchestration/ActivityPayloadProjection.ts` around lines 225
- 229, Extract the duplicated changed-files collection and mapping logic into a
shared helper near the projection utilities, reusing collectChangedFiles and
returning the { path } records. Replace the local blocks in both
projectMcpToolCallData and the other affected projection branch with this helper
while preserving the existing empty-result behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/server/src/server.ts`:
- Around line 563-590: Add focused lifecycle tests around the
cleanupBeforeActivation branch in the server startup flow, using
ServerActivation gates to cover both pendingServiceUpdateExists=true (finalizer
registered before forkParked activation) and false (finalizer registered after
activation). Assert that managed-tunnel release occurs at the appropriate
shutdown timing for each path, while preserving existing
releaseManagedTunnelOnShutdown coverage.

In `@apps/server/src/serviceLauncher.test.ts`:
- Around line 115-121: Update the stop-marker regression test around
launcher.stop("SIGTERM") to avoid asserting after yielding to the scheduler.
Start a child during recovery and have its shutdown handler verify that
SERVICE_STOP_MARKER_FILE exists, then await the stopping and running effects
while preserving the existing cleanup assertions.

In `@apps/server/src/serviceLauncher.ts`:
- Around line 352-355: In apps/server/src/serviceLauncher.ts lines 352-355,
update `#recover`() to remove the stop marker only when `#stopRequested` is false,
preserving it during explicit shutdown. In
apps/server/src/serviceLauncher.test.ts lines 115-121, add coverage that starts
a child through recovery, stops it, and verifies the child’s shutdown handler
observes the marker.

In `@apps/server/test/ActivityPayloadProjection.test.ts`:
- Around line 371-388: Update the anonymous fixture in projectedIds to use a
schema-valid non-empty summary instead of the whitespace-only value, while
preserving the existing identity-less row behavior and assertions.

In `@apps/web/src/components/ServerUpdateAction.tsx`:
- Line 75: Update the action-table entry in docs/user/updating.md from “Update
server” to “Update” to match the default label in ServerUpdateAction and its
call sites.

---

Nitpick comments:
In `@apps/server/src/orchestration/ActivityPayloadProjection.test.ts`:
- Around line 79-99: Extend the MCP projection test around
projectActivityPayload to include a path-like value in input or item.arguments,
then assert that the projected data.files array contains the expected derived
file entry. Keep the existing toolName, input, result, and payload-size
assertions intact while covering the projectMcpToolCallData files branch.

In `@apps/server/src/orchestration/ActivityPayloadProjection.ts`:
- Around line 434-466: Simplify completion tracking in the activity projection
by changing completionIndicesByKey to store only the latest completion index for
each tool-lifecycle key. Update the collection loop to overwrite the key with
the current index, and adjust the tool.updated filter predicate to compare that
single index directly instead of scanning an array with some.
- Around line 225-229: Extract the duplicated changed-files collection and
mapping logic into a shared helper near the projection utilities, reusing
collectChangedFiles and returning the { path } records. Replace the local blocks
in both projectMcpToolCallData and the other affected projection branch with
this helper while preserving the existing empty-result behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 750eaaaf-b826-4ab9-92fb-f23e2cd7cbf2

📥 Commits

Reviewing files that changed from the base of the PR and between 911276d and ff2218f.

📒 Files selected for processing (29)
  • apps/mobile/src/features/threads/PendingApprovalCard.tsx
  • apps/mobile/src/features/threads/PendingUserInputCard.tsx
  • apps/server/src/cloud/bootService.test.ts
  • apps/server/src/cloud/bootService.ts
  • apps/server/src/cloud/http.test.ts
  • apps/server/src/cloud/http.ts
  • apps/server/src/cloud/serviceProtocol.ts
  • apps/server/src/orchestration/ActivityPayloadProjection.test.ts
  • apps/server/src/orchestration/ActivityPayloadProjection.ts
  • apps/server/src/provider/acp/AcpCoreRuntimeEvents.test.ts
  • apps/server/src/provider/acp/AcpCoreRuntimeEvents.ts
  • apps/server/src/server.ts
  • apps/server/src/serviceLauncher.test.ts
  • apps/server/src/serviceLauncher.ts
  • apps/server/test/ActivityPayloadProjection.test.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/ComposerPromptEditor.tsx
  • apps/web/src/components/ServerUpdateAction.test.tsx
  • apps/web/src/components/ServerUpdateAction.tsx
  • apps/web/src/components/Sidebar.snooze.test.ts
  • apps/web/src/components/Sidebar.snooze.ts
  • apps/web/src/components/SidebarV2.tsx
  • apps/web/src/components/chat/runtimeModePresentation.ts
  • apps/web/src/components/composerInlineChip.ts
  • apps/web/src/components/settings/ConnectionsSettings.tsx
  • apps/web/src/hooks/useThreadActions.ts
  • apps/web/src/planSidebarDismissal.ts
  • apps/web/src/session-logic.test.ts
  • docs/user/updating.md

Comment thread apps/server/src/server.ts
Comment on lines +563 to +590
const releaseManagedTunnel = releaseManagedTunnelOnShutdown().pipe(
Effect.timeout("10 seconds"),
Effect.tap((released) =>
released ? Effect.logInfo("Released the managed tunnel on shutdown") : Effect.void,
),
Effect.catchCause((cause) =>
Effect.logWarning(
"Failed to release the managed tunnel on shutdown; the next link reuses it",
{ cause },
),
),
Effect.asVoid,
);
// A launcher trial can be stopped before activation. The previous
// server is already gone, so the trial owns cleanup immediately; the
// pending-state check keeps the tunnel for normal commit or rollback,
// while the launcher's explicit-stop marker allows it to be released.
// Other runtimes wait for activation so a failed standby cannot tear
// down the active runtime's tunnel.
const cleanupBeforeActivation = yield* pendingServiceUpdateExists;
if (cleanupBeforeActivation) {
yield* Effect.addFinalizer(() => releaseManagedTunnel);
}
yield* forkParked(
Effect.gen(function* () {
// Only an activated runtime owns the tunnel cleanup finalizer.
yield* Effect.addFinalizer(() =>
releaseManagedTunnelOnShutdown().pipe(
Effect.timeout("10 seconds"),
Effect.tap((released) =>
released
? Effect.logInfo("Released the managed tunnel on shutdown")
: Effect.void,
),
Effect.catchCause((cause) =>
Effect.logWarning(
"Failed to release the managed tunnel on shutdown; the next link reuses it",
{ cause },
),
),
Effect.asVoid,
),
);
if (!cleanupBeforeActivation) {
yield* Effect.addFinalizer(() => releaseManagedTunnel);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Node version:"
node -v || true

echo "Relevant files:"
git ls-files | rg 'apps/server/src/(server|.*test|.*spec).*\.(ts|tsx)$|server.ts$|test' || true

echo "server outline:"
ast-grep outline apps/server/src/server.ts --view compact | sed -n '1,220p' || true

echo "Search releaseManagedTunnelOnShutdown and cleanupBeforeActivation:"
rg -n "releaseManagedTunnelOnShutdown|cleanupBeforeActivation|pendingServiceUpdateExists|forkParked|addFinalizer|shutdown" apps/server/src/server.ts

Repository: aaditagrawal/t3code

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Git status/stat:"
git status --short || true

echo "Target server.ts lines:"
sed -n '520,610p' apps/server/src/server.ts

echo "Search for server.ts tests/specs nearby:"
git ls-files apps/server/src | rg '(^|/)server.*\.(test|spec)\.(ts|tsx)$|server\.test|server\.spec' || true

echo "Repository-wide focused search for server lifecycle cleanup terms:"
rg -n "addFinalizer|shutdown|cleanupBeforeActivation|pendingServiceUpdateExists|releaseManagedTunnelOnShutdown|forkParked" apps/server/src --glob '*.ts' --glob '*.tsx' || true

echo "List apps/server/src files with tests:"
git ls-files apps/server/src | rg '/src/.*\.(test|spec)\.(ts|tsx)$' | head -200 || true

Repository: aaditagrawal/t3code

Length of output: 44346


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "cloud http tests around releaseManagedTunnelOnShutdown:"
sed -n '36,520p' apps/server/src/cloud/http.test.ts

echo "serverActivation.ts:"
cat -n apps/server/src/serverActivation.ts

echo "serverActivation tests:"
sed -n '1,120p' apps/server/src/serverActivation.test.ts

echo "serverLifecycle/servers tests:"
sed -n '1,260p' apps/server/src/serverLifecycleEvents.test.ts
sed -n '1,260p' apps/server/src/serverActivation.test.ts || true

Repository: aaditagrawal/t3code

Length of output: 23880


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "serverRuntimeStartup relevant lines:"
sed -n '330,380p' apps/server/src/serverRuntimeStartup.ts

echo "Tests referencing ServerActivation/forkParked:"
rg -n "ServerActivation|forkParked|pendingServiceUpdateExists|releaseManagedTunnel|addFinalizer" apps/server/src --glob '*.test.ts' --glob '*.test.tsx' || true

echo "Read-only structural probe for finalizer placement invariant from source text:"
python3 - <<'PY'
from pathlib import Path
import re

server = Path("apps/server/src/server.ts").read_text()
m = re.search(r"const cleanupBeforeActivation = .*?\n\s+if \(cleanupBeforeActivation\) \{\n\s+yield\* Effect\.addFinalizer\(\(\) => releaseManagedTunnel\);\n\s+\}\n\s+yield\* forkParked\(\n\s+Effect\.gen\(function\* \(\) \{\n\s+if \(!cleanupBeforeActivation\) \{\n\s+yield\* Effect\.addFinalizer\(\(\) => releaseManagedTunnel\);\n\s+\}", server)
print("finalizer_placement_pattern_found=", bool(m))
if m:
    print("match_line=", server[:m.start()].count("\n") + 1, "-", server[:m.end()].count("\n") + 1)
else:
    print("nearest_patterns:")
    for term in ["const cleanupBeforeActivation", "yield* Effect.addFinalizer(() => releaseManagedTunnel)", "if (!cleanupBeforeActivation)"]:
        idx = server.find(term)
        print(term, "line", server[:idx].count("\n") + 1 if idx >= 0 else None)
PY

echo "Tests referencing server.ts lifecycle/finalizer path:"
rg -n "finalizer|cleanupBeforeActivation|pendingServiceUpdateExists|reconcileDesiredCloudLink|forkParked" apps --glob '*.test.ts' --glob '*.test.tsx' --glob '*.test.js' --glob '*.test.jsx' || true

Repository: aaditagrawal/t3code

Length of output: 10386


Add focused lifecycle coverage for managed tunnel finalizer placement.

releaseManagedTunnelOnShutdown() has coverage, but the lifecycle code path adds the finalizer either before forkParked(activation) when pendingServiceUpdateExists is true or after activation otherwise. Add focused tests that exercise both shutdown timings with ServerActivation gates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/server.ts` around lines 563 - 590, Add focused lifecycle
tests around the cleanupBeforeActivation branch in the server startup flow,
using ServerActivation gates to cover both pendingServiceUpdateExists=true
(finalizer registered before forkParked activation) and false (finalizer
registered after activation). Assert that managed-tunnel release occurs at the
appropriate shutdown timing for each path, while preserving existing
releaseManagedTunnelOnShutdown coverage.

Source: Coding guidelines

Comment thread apps/server/src/serviceLauncher.test.ts
Comment thread apps/server/src/serviceLauncher.ts Outdated
Comment on lines +371 to +388
it("keeps identity-less rows the clients never collapse", () => {
const anonymous: OrchestrationThreadActivity = {
id: EventId.make("upd-anon"),
tone: "tool",
kind: "tool.updated",
summary: " ",
payload: { data: { toolName: "Edit" } },
turnId: TurnId.make("turn-a"),
createdAt: "2026-07-27T00:00:00.000Z",
};
const completed: OrchestrationThreadActivity = {
...anonymous,
id: EventId.make("done-anon"),
kind: "tool.completed",
};

expect(projectedIds([anonymous, completed])).toEqual([anonymous.id, completed.id]);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the TrimmedNonEmptyString definition and how summary is branded.
fd -t f 'orchestration.ts' packages/contracts/src | while IFS= read -r f; do
  rg -n -C 5 'TrimmedNonEmptyString' "$f" | head -60
done

# Locate the schema primitive definition.
rg -n -C 8 'TrimmedNonEmptyString\s*=' packages/contracts/src

# Check whether other tests construct summary via a constructor or a cast.
rg -n -C 3 'summary:' apps/server/test/ActivityPayloadProjection.test.ts \
  apps/server/src/orchestration/ActivityPayloadProjection.test.ts

# Find producers of thread activities to see whether an empty summary is reachable.
ast-grep run --pattern 'summary: $_' --lang typescript apps/server/src/orchestration | head -40

Repository: aaditagrawal/t3code

Length of output: 6771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate ActivityPayloadProjection.ts =="
fd -t f 'ActivityPayloadProjection.ts' apps/server/src/orchestration

echo "== outline ActivityPayloadProjection.ts =="
ast-grep outline apps/server/src/orchestration/ActivityPayloadProjection.ts --view expanded | head -220 || true

echo "== summary-related code in ActivityPayloadProjection.ts =="
rg -n -C 4 'toolLifecycleIdentity|dropSupersededToolUpdatedActivities|TrimmedNonEmptyString|summary' apps/server/src/orchestration/ActivityPayloadProjection.ts

# Search for function signatures that may accept summary as bare string / schema input.
python3 - <<'PY'
from pathlib import Path
p=Path('apps/server/src/orchestration/ActivityPayloadProjection.ts')
text=p.read_text()
for needle in ['function ', 'const ', ': ']:
    pass
PY

echo "== package tsconfig node version relevant setting =="
for f in package.json apps/server/package.json; do [ -f "$f" ] && { echo "--- $f"; jq '.engines' "$f" 2>/dev/null || sed -n '1,80p' "$f"; }; done

Repository: aaditagrawal/t3code

Length of output: 1698


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ActivityPayloadProjection sections =="
sed -n '260,455p' apps/server/src/orchestration/ActivityPayloadProjection.ts

echo "== orchestration.ts summary imports/exports =="
rg -n -C 4 'OrchestrationThreadActivity|summary|TrimmedNonEmptyString|TrimmedString' packages/contracts/src/orchestration.ts

echo "== any bare string summary assignments in apps/server sources =="
python3 - <<'PY'
from pathlib import Path
for p in Path('apps/server/src').rglob('*.ts'):
    text=p.read_text(errors='ignore')
    if 'summary:' in text:
        for i,line in enumerate(text.splitlines(),1):
            if '\"summary\":' in line or 'summary:' in line:
                print(f"{p}:{i}:{line.strip()}")
PY

Repository: aaditagrawal/t3code

Length of output: 36676


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ProjectionPipeline summary producer section =="
sed -n '1045,1085p' apps/server/src/orchestration/Layers/ProjectionPipeline.ts

echo "== ActivityPayloadProjection.ts full import/function boundary =="
sed -n '1,85p' apps/server/src/orchestration/ActivityPayloadProjection.ts
sed -n '455,505p' apps/server/src/orchestration/ActivityPayloadProjection.ts

echo "== read-only model check for summary: literal in fixture =="
python3 - <<'PY'
from pathlib import Path
p=Path("apps/server/test/ActivityPayloadProjection.test.ts")
text=p.read_text()
needle='summary: " "', found="summary:" in text
print(f"fixture_summary_literal={'summary: \" \"' in text}")
print(f"fixture_summary_type_annotation='summary:' in needle")
PY

echo "== type export for OrchestrationThreadActivity and Schema inference =="
rg -n -C 2 'export type OrchestrationThreadActivity|export const OrchestrationThreadActivity' packages/contracts/src/orchestration.ts
sed -n '1,22p' packages/contracts/src/baseSchemas.ts

Repository: aaditagrawal/t3code

Length of output: 5069


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== base schema =="
sed -n '1,20p' packages/contracts/src/baseSchemas.ts

echo "== orchestration activity type assignment =="
sed -n '315,326p' packages/contracts/src/orchestration.ts
sed -n '1090,1120p' packages/contracts/src/orchestration.ts
sed -n '1205,1210p' packages/contracts/src/orchestration.ts

echo "== fixture literal syntax =="
python3 - <<'PY'
from pathlib import Path
p = Path("apps/server/test/ActivityPayloadProjection.test.ts")
text = p.read_text()
print('summary: " " in fixture:', 'summary: " "' in text)
PY

# Node can run a read-only behavioral probe of the relevant asTrimmedString logic
# without importing repository code.
node --input-type=module - <<'JS'
function asTrimmedString(value) {
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  return trimmed.length > 0 ? trimmed : null;
}
console.log({
  bareSpaceInput: JSON.stringify(asTrimmedString(" ")),
  nonEmptySpaceInput: JSON.stringify(asTrimmedString("Edit")),
});
JS

Repository: aaditagrawal/t3code

Length of output: 3113


Use a schema-valid summary in this fixture.

summary is a TrimmedNonEmptyString; " " trims to empty and violates OrchestrationThreadActivity. This literal will not pass the activity contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/test/ActivityPayloadProjection.test.ts` around lines 371 - 388,
Update the anonymous fixture in projectedIds to use a schema-valid non-empty
summary instead of the whitespace-only value, while preserving the existing
identity-less row behavior and assertions.

Comment thread apps/web/src/components/ServerUpdateAction.tsx
CodeRabbit caught that queued #recover() cleared SERVICE_STOP_MARKER_FILE
after stop() wrote it, so a child started mid-shutdown could miss the
explicit-stop signal. Skip marker removal when #stopRequested, assert the
marker survives recover, and align updating.md with the Update button label.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Addressed CodeRabbit feedback on this tip (f0b03e877):

  1. serviceLauncher stop marker (major)#recover() now skips clearing SERVICE_STOP_MARKER_FILE when #stopRequested is set, so an explicit stop() that races ahead of queued recover keeps the marker for any child started mid-shutdown. Test asserts the marker still exists after recover+stop complete.
  2. updating.md — action table label updated from “Update server” to Update to match ServerUpdateAction.
  3. Remaining comments (server finalizer placement / ActivityPayloadProjection empty-summary branding) match upstream port behavior and are intentionally unchanged in this sync layer.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff2218f614

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +4121 to +4123
const acknowledgeActiveThreadWoke = useCallback(() => {
if (activeThreadRef === null || activeThreadWokeAt === null) return;
markThreadVisited(scopedThreadKey(activeThreadRef), activeThreadWokeAt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve read acknowledgement when opening a thread

When a previously visited thread completes, hasUnseenCompletion marks it as unread until threadLastVisitedAtById advances past the completion. This change removes the effect that advanced that timestamp whenever the completed thread was opened or updated, while the replacement here only advances it for a snooze wake during explicit actions. Consequently, opening—or already viewing—a normally completed thread no longer clears its “Done” indicator, and there is no other read action for that indicator. Keep the ordinary visit acknowledgement separate from the explicit wake acknowledgement.

Useful? React with 👍 / 👎.

Comment on lines +464 to +465
const indices = completionIndicesByKey.get(`${activity.turnId ?? ""}${identity}`);
return !indices?.some((completionIndex) => completionIndex > index);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep unmatched parallel tool updates in snapshots

When parallel calls in one turn lack data.toolCallId and share the fallback item type/title/detail, a completion for just one call is indexed under the same key as every call. If another lifecycle row interleaves the calls, the clients would retain the still-running call's update, but this filter drops all earlier matching updates merely because one matching completion exists later. After reconnecting from a snapshot, the still-running call can therefore disappear until it emits another event; matching must use a per-call identifier or avoid dropping ambiguous fallback identities.

AGENTS.md reference: AGENTS.md:L20-L25

Useful? React with 👍 / 👎.

Comment on lines 42 to +43
default:
return "unknown";
return "dynamic_tool_call";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Map dynamic ACP approvals for mobile clients

For ACP permission kinds such as search, fetch, or future provider-defined kinds, this now emits dynamic_tool_call, but the mobile client's requestKindFromRequestType does not recognize that type. Its derivePendingApprovals consequently discards the request, so a mobile user sees no approval card and cannot unblock the ACP session, whereas the web client explicitly maps this type to a command approval. Add the shared/mobile mapping as part of changing the emitted request type.

AGENTS.md reference: AGENTS.md:L20-L25

Useful? React with 👍 / 👎.

Comment on lines +663 to +666
const stopping = yield* fs
.exists(path.join(runtimeDir, SERVICE_STOP_MARKER_FILE))
.pipe(Effect.orElseSucceed(() => false));
return !stopping;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle explicit stops from pre-marker service launchers

When an existing background-service installation is remotely updated to this server version, its stable launcher and systemd unit are not replaced, and the launcher protocol remains version 2; those pre-change launchers never write .service-stopping. If systemctl stop or t3 service uninstall occurs while an update is pending, this check therefore treats the explicit stop as a handoff and skips managed-tunnel deletion even though no replacement server is coming, leaving the provisioned tunnel orphaned. The handoff decision needs backward-compatible launcher detection or a launcher/protocol migration before absence of the marker can mean handoff.

AGENTS.md reference: AGENTS.md:L20-L25

Useful? React with 👍 / 👎.

Comment on lines +464 to +465
const indices = completionIndicesByKey.get(`${activity.turnId ?? ""}${identity}`);
return !indices?.some((completionIndex) => completionIndex > index);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid quadratic scans for repeated tool identities

When a long turn repeatedly invokes the same tool identity, each update scans the completion-index array from the beginning until it finds a later completion. For an alternating sequence of k updates and completions with one key, the work is 1 + 2 + … + k, making snapshot projection quadratic precisely on the large tool-heavy threads this optimization targets. Because the indices are ordered, checking the last completion index—or performing one reverse pass with a set of future completions—keeps this linear.

AGENTS.md reference: AGENTS.md:L56-L60

Useful? React with 👍 / 👎.

Comment on lines +48 to +50
<span className="min-w-0 truncate" title={state.message}>
{state.message}
</p>
) : null}
</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep full update errors accessible without hover

When an update failure message is wider than its container, the new truncate class hides the diagnostic and exposes the full text only through the HTML title. Touch users cannot invoke that hover-only affordance, so in narrow layouts they may see only the beginning of the rollback or installation error needed to decide whether retrying is useful. The previous progress view rendered the complete error; allow this alert to wrap or provide an explicitly operable details control.

Useful? React with 👍 / 👎.

@cursor
cursor Bot merged commit 0d1da72 into main Aug 8, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants