Skip to content

CL-7802: start gated tool elapsed at grant, not announcement - #1176

Open
TheGreatAxios wants to merge 5 commits into
mainfrom
cl-7802-start-in-flight-tool-elapsed-at-grant-not-at-the-permission
Open

TheGreatAxios wants to merge 5 commits into
mainfrom
cl-7802-start-in-flight-tool-elapsed-at-grant-not-at-the-permission

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Rebases gate-waited tool clocks to the grant so post-grant stats read time-since-grant.

Grant signal (read-only finding): the bridge has no per-call execution-start event and the product-host gate wiring reports only a single settle per gate (gate-wire onceClosed, fired for allow and deny alike). So gateClosed is the rebase point, scoped to calls tracked as gate-waited. No product-host or gate-wire changes.

What changes (src/tui/runtime-bridge.ts):

  • New BridgeBag.gatedToolCalls set: populated from the timed-call snapshot in gateOpened plus applyToolCall arrivals while blockedGateCount > 0.
  • gateClosed calls new rebaseGatedElapsed: waited ids get toolCallStartedAt = now() and a painted 0:00 via rowUpdates.scheduleRowUpdate. Later ticks flow through the existing syncToolElapsed path, so post-grant cadence is identical to ungated rows.
  • Deny needs no special case: gateClosed fires for deny too, and the result merge already drops the clock-owned stat for the answer's own addendum (omitStat).
  • Guards: spawn_agent ids are skipped (session clock owns those rows); diff rows never enter the set. Ids are dropped in applyToolResult and rollbackAttempt (no leaks).
  • shell.inFlightTool.startedAt deliberately untouched: its only reader (resolveWaitingOn) uses it as a steer-wait threshold, not an execution clock.

Open questions / call-outs:

  • Nested gates rebase uniformly at each settle rather than per gate/call pair: the bridge sees gate lifecycles but not which grant covers which call.
  • Visible-gate wait also rebases to the grant (uniform with hidden gates): the wait belongs to the gate in both cases. If the team wants visible waits to count, say so and I will scope the rebase to hidden gates only.
  • Checkbox 1 (hidden-freeze) was already covered by CL-8792: allow-once no longer stalls a second destructive command without re-prompt #1173; its tests stay green, no new work there.

Verification:

  • Red: new R1/R3 tests fail pre-fix (2:00 leftover instead of 0:00).
  • Focused suites: bun test src/tui/runtime-bridge.test.ts src/tui/allow-once-reprompt.test.ts src/tui/agent-progress.test.ts src/tui/turn-state.test.ts — 150 pass, 0 fail.
  • bun run typecheck exit 0; bun run lint exit 0.
  • Full suite bun test ./src ./tests ./evals ./scripts --randomize --seed 424242: 8040 pass, 1 fail — the single failure is background shell registry > collect with an aborted signal releases as running without killing the child, a load-sensitive timing flake in src/shell (zero imports from src/tui); it passes 3/3 standalone and flaps with the fix applied, so it is unrelated to this change.

Fixes CL-7802

Allow-once settles exactly once and frees the single-slot overlay host, so an already-queued or newly raised card paints before any deferred slash/settings/MCP surface. Command surfaces suspend and return after the gate settles; inline popups keep their stacking contracts.
@linear-code

linear-code Bot commented Sep 25, 2026

Copy link
Copy Markdown

CL-7802

gateOpened snapshotted every timed call into the rebase set, so an
auto-allowed sibling that was already executing reset to 0:00 when a
later gate settled. Skip ids that already carry a live elapsed clock.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant