Skip to content

feat: select and switch runtime per session prompt - #283

Merged
pedronauck merged 3 commits into
mainfrom
session-runtime
Jul 31, 2026
Merged

pedronauck merged 3 commits into
mainfrom
session-runtime

Conversation

@pedronauck

@pedronauck pedronauck commented Jul 30, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Move runtime selection into the session prompt composer so users can choose provider, model, reasoning effort, and speed for every prompt.
  • Support runtime changes inside one durable session, including safe ACP process replacement, replay, rollback, queued prompts, steering, and interruption.
  • Make session creation promptless and runtime-unbound, simplify the creation modal, and automatically open the newly created session window.
  • Carry the new runtime contract through storage, HTTP, UDS, CLI, native tools, extensions, generated SDKs, web state, documentation, and QA.

Changes

Session runtime lifecycle

  • Logical sessions are created active and without a bound runtime or ACP process.
  • The first prompt binds the selected runtime and starts the provider process.
  • Subsequent prompts may reuse the current runtime, live-reconfigure a compatible provider, or replace the ACP process when the provider, harness, or model transition requires it.
  • Runtime replacement replays durable history and rolls back cleanly if the transition fails.

Prompt snapshots and transitions

  • Prompt submissions persist immutable provider/model/reasoning/speed snapshots.
  • Queued, steered, and interrupted prompts retain the runtime selected when the user submitted them.
  • Queue generations prevent stale work from crossing runtime transitions.
  • Runtime projection remains consistent across persistence, SSE events, caches, replay, and read APIs.

Session creation and web UX

  • Remove prompt and runtime controls from the create-session modal.
  • Keep only agent selection in the Simple view; move workspace, session name, path, and network controls into Advanced.
  • Activate the selected workspace, navigate to the new session, and open its window immediately after creation.
  • Preserve the navigation intent while the window manager hydrates.
  • Add the runtime selector to the in-session prompt composer and reconcile its state with the session's current runtime.
  • Intersect workspace provider policy with actual authentication availability and disable signed-out providers.

Structured and agent-managed surfaces

  • Change compozy__session_create to create promptless logical sessions.
  • Change compozy__session_prompt to accept the nested runtime selection used by the daemon.
  • Update native-tool descriptors, schemas, digests, registry coverage, and capability gates.
  • Update HTTP, UDS, CLI, extension Host API, OpenAPI, Go SDK, and TypeScript SDK contracts together.
  • Update the bundled Compozy skill and public documentation for the new create-then-prompt lifecycle.

Runtime and persistence hardening

  • Add the schema changes required for prompt-bound runtime state and refresh Atlas/sqlc output.
  • Fix daemon readiness so boot errors cannot race with shutdown readiness.
  • Harden runtime rollback, provider authentication availability, and session-window routing behavior.

Research basis

  • Review the ACP session/runtime approaches in .resources/synara and .resources/opendesign.
  • Record Open Design as a reproducible repository resource in .repoclone.rc.

Breaking changes

  • Session creation no longer accepts an initial prompt or runtime. Callers create the logical session first, then submit a prompt with a nested runtime selection.
  • Runtime read payloads use the nested session.runtime projection.
  • The first prompt for an unbound session requires a provider selection; model, reasoning, and speed may use provider defaults where supported.
  • Obsolete compatibility paths and aliases were removed. OpenAPI, generated SDKs, native tools, CLI, web, extensions, and docs ship the new contract together.

Migrations and configuration

  • Add migrations 00029_schema.sql and 00030_schema.sql and update the declarative schema, Atlas checksum, and generated sqlc code.
  • No config.toml keys or defaults changed.

Release notes

Features

  • Select provider, model, reasoning effort, and speed for each session prompt, including mid-session runtime switching.
  • Create sessions without starting a provider process and open the new session automatically.

Bug fixes

  • Preserve prompt runtime snapshots across queue, steer, interrupt, replay, and rollback flows.
  • Fix session-window hydration routing, provider authentication availability, and daemon readiness handling.

QA

Final status: Behavioral PASS; the full engineering gate is current.

  • Exercised 5 personas across 9/9 charters and 23/23 changed scenarios.
  • Fixed 2 discovered issues: 1 Blocks-Completion defect and 1 Trust-Damage defect; no Data-Loss, Friction, or Cosmetic defects remained.
  • Reviewed 13 isolated visual captures for the changed web flows.
  • Passed the strict evidence audit with zero blockers and zero warnings.
  • Completed QA teardown with clean: true and no surviving lab processes.
  • Human verification required: none.

Evidence:

Deep review

  • Completed one multi-agent review round with 17 review jobs across 3 cohorts.
  • Resolved all 6 major findings and all 37 advisory findings.
  • No unresolved review findings remain.

Compozy Impact Audit

  • Native tools: Updated compozy__session_create and compozy__session_prompt, their descriptors, input/output schemas, digests, registry coverage, availability behavior, and capability gates.
  • Extensibility and hooks: Updated the extension Host API session/runtime projection, OpenAPI contract, Go and TypeScript SDKs, runtime registries, prompt replay, and provider lifecycle. No new config lifecycle keys or defaults were introduced.
  • Workspace data isolation: Runtime selection is session-scoped within its owning workspace. workspace_id remains propagated and covered through CLI, HTTP, UDS, core, store, web, SSE, cache, events, queues, and replay so session runtime data cannot cross workspace boundaries.
  • Official Compozy skill: Updated skills/compozy/references/native-tools.md and skills/compozy/references/runtime-operations.md for promptless creation and prompt-bound runtime selection.

Test plan

  • make gate-full — PASS CURRENT for fingerprint cf66e95f4ff5db7b655fa80917d5d5087f0faf94.
  • Full monorepo verification: code generation drift, installer checks, Bun lint/typecheck/tests, web build, Go formatting/lint/tests/build, and architecture boundaries.
  • Multi-agent deep review with every major and advisory finding resolved.
  • Real-user QA: 9/9 charters and 23/23 changed scenarios passed.
  • Strict QA evidence audit and visual review passed.
  • QA process teardown completed cleanly.

Summary by CodeRabbit

  • New Features

    • Sessions can be created as active, unbound records and configured when the first prompt is sent.
    • Choose provider, model, reasoning effort, and speed for individual prompts, including queued or interrupted prompts.
    • Session status now displays runtime state, effective configuration, capabilities, transitions, and relevant failures.
    • Added native tools for creating sessions and sending prompts.
    • Improved prompt cancellation, working indicators, and transcript position preservation when loading older messages.
    • Session creation now supports clearer workspace, agent, and session-name configuration.
  • Bug Fixes

    • Improved session routing recovery and runtime transition error handling.

Preserve prompt-bound runtime snapshots across live, queued, steer, and
interrupt flows while keeping logical session creation runtime-unbound.

Harden runtime projection across API, CLI, native tools, extensions, SDKs,
and web state. Fix session-window routing hydration, provider auth
availability, daemon shutdown readiness, and runtime rollback behavior.

Record the completed deep review and real-user QA coverage for session
creation, runtime transitions, and structured prompt failures.
@greptile-apps

greptile-apps Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Too many files changed for review. (338 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

@vercel

vercel Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
compozy-site Ready Ready Preview Jul 31, 2026 1:04am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The change moves runtime selection from session creation to prompt dispatch, adds runtime lifecycle and persistence metadata, exposes nested runtime API payloads, adds native session tools, updates CLI and SDK contracts, and introduces web controls for runtime selection, prompt cancellation, and durable unbound sessions.

Changes

Session runtime architecture

Layer / File(s) Summary
Runtime lifecycle and configuration
internal/session/*, internal/acp/*
Sessions can remain active and unbound until prompting, then bind, reconfigure, or replace ACP runtimes with persisted lifecycle state and rollback handling.
Contracts and persistence
internal/api/contract/*, internal/store/*, sdk/go/contracts/*
Runtime status, effective selection, speed resolution, prompt snapshots, queue metadata, migrations, and generated contracts are represented through nested runtime payloads.
Prompt entry points and tools
internal/api/*, internal/cli/*, internal/extension/*, internal/daemon/*, internal/tools/*
HTTP, UDS, CLI, extension, and native tools accept runtime-qualified prompts and return runtime-aware admission and session data.
Web session experience
web/src/systems/session/*, web/src/components/assistant-ui/*, web/src/systems/os/*
Session creation submits durable metadata only; prompt runtime selection, dispatch cancellation, runtime snapshots, working indicators, and nested runtime projections are added.
Supporting migrations and fixtures
internal/**, web/**, packages/site/*, .repoclone.rc
Runtime fields and lifecycle defaults are propagated through tests, fixtures, site test configuration, and managed resources.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • compozy/compozy#34: Introduced ACP agent and session abstractions extended by the runtime configuration flow.
  • compozy/compozy#267: Related ACP speed selection, validation, negotiation, and persistence changes.
  • compozy/compozy#271: Related session transcript UI and runtime-aware prompting changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes selecting and switching runtime configurations for individual session prompts, matching the pull request's primary change.
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 session-runtime

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 commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found 1 new issue in 1 file · 1 error · score 84 / 100 (Needs work) · 0 fixed · vs main

Errors

Reviewed by React Doctor for commit 3cfaf79. See inline comments for fixes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
internal/cli/session_list.go (1)

123-132: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align JSONL with the runtime-provider projection.

sessionListBundle() calls writeJSONLine(cmd, item) for JSONL, so it serializes SessionRecord directly while the displayed rows use sessionRuntimeProvider(item). The Runtime.Effective.Provider projection is only exposed in Table/JSON modes unless JSONL is built with the same projected value used by the row renderer.

🤖 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 `@internal/cli/session_list.go` around lines 123 - 132, Update the JSONL
serialization path in sessionListBundle so writeJSONLine receives a
SessionRecord whose runtime provider matches sessionRuntimeProvider(item),
including the Runtime.Effective.Provider projection. Preserve the existing
displayed-row and other output-mode behavior while applying this projection
specifically to JSONL output.
internal/session/manager_input_dispatch.go (1)

89-100: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Failed prompt-request construction leaves the queue entry claimed forever.

Every other failure branch in this file (handleQueuedInputDispatchError) releases or marks-failed the claimed entry and calls m.startNextQueuedInputPrompt(target) to keep the queue moving. When newQueuedInputPromptRequest fails (line 125-132), dispatchHumanQueuedInput just returns (line 89-92) — the entry claimed via ClaimNext is never released, never marked failed, and the queue is never advanced. If this path is ever hit, the session's queued input pipeline stalls silently.

🐛 Proposed fix
 	req, ok := m.newQueuedInputPromptRequest(session, target, entry)
 	if !ok {
+		if releaseErr := m.inputQueue.Release(m.fallbackLifecycleContext(), target, entry.id); releaseErr != nil {
+			m.sessionLogger(session).Warn(
+				"session: release queued input failed",
+				"entry_id", entry.id,
+				"error", releaseErr,
+			)
+		}
+		m.startNextQueuedInputPrompt(target)
 		return
 	}

Also applies to: 115-142

🤖 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 `@internal/session/manager_input_dispatch.go` around lines 89 - 100, Update
dispatchHumanQueuedInput so the !ok branch after newQueuedInputPromptRequest
handles the claimed entry consistently with handleQueuedInputDispatchError:
release or mark the entry failed as appropriate, then call
m.startNextQueuedInputPrompt(target) before returning. Ensure failed
prompt-request construction cannot leave the queue stalled.
internal/api/udsapi/transport_parity_integration_test.go (1)

686-696: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Discarded errors violate the "never discard errors with _" guideline.

io.ReadAll and Body.Close() errors are silently dropped here, while the resumeResp handling a few lines below in the same test correctly checks both. As per coding guidelines, "Never discard errors with _ in production code or tests; handle every error or provide a justification."

🧹 Proposed fix
 	if stopResp.StatusCode != http.StatusNoContent {
-		body, _ := io.ReadAll(stopResp.Body)
-		_ = stopResp.Body.Close()
+		body, readErr := io.ReadAll(stopResp.Body)
+		closeErr := stopResp.Body.Close()
+		if readErr != nil {
+			t.Fatalf("read UDS stop response body error = %v", readErr)
+		}
+		if closeErr != nil {
+			t.Fatalf("close UDS stop response body error = %v", closeErr)
+		}
 		t.Fatalf(
 			"UDS stop session status = %d, want %d; body=%s",
 			stopResp.StatusCode,
 			http.StatusNoContent,
 			string(body),
 		)
 	}
-	_ = stopResp.Body.Close()
+	if err := stopResp.Body.Close(); err != nil {
+		t.Fatalf("close UDS stop response body error = %v", err)
+	}
🤖 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 `@internal/api/udsapi/transport_parity_integration_test.go` around lines 686 -
696, Update the stopResp error-handling block in the UDS stop-session test to
check and handle errors from io.ReadAll and stopResp.Body.Close instead of
assigning them to _. Match the explicit error-checking pattern already used by
the nearby resumeResp handling, while preserving the existing failure message
and status validation.

Source: Coding guidelines

internal/store/globaldb/sqlcgen/session_core.sql.go (1)

265-315: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Runtime fields can still be silently dropped by the network-only optimistic-concurrency guard.

UpsertSessionParams is built from the in-memory session snapshot, so the mapper emits current Runtime* values even when session.NetworkSpecSnapshot() is a stale participation snapshot. Since ON CONFLICT ... WHERE compares only network_spec_json/network_mode/network_channel/network_source, a runtime-status update can be gate-locked out whenever any network field has changed. Include the runtime fields in the concurrency guard where that intent applies, or split the upsert path so runtime lifecycle updates are not bypassed by stale network metadata.

🤖 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 `@internal/store/globaldb/sqlcgen/session_core.sql.go` around lines 265 - 315,
The session upsert’s optimistic-concurrency guard must not reject current
runtime lifecycle updates because of stale network metadata. Update the conflict
handling around UpsertSessionParams and its ON CONFLICT WHERE clause to include
the relevant Runtime* fields in the comparison, or separate runtime lifecycle
updates from the network-guarded path; preserve the existing network protections
while ensuring runtime status and transition changes are applied.
sdk/go/contracts/types_019_gen.go (1)

248-289: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the SDK session status/runtime field changes.

Provider is no longer present on the generated SessionStatus/SessionSummary, and ACPSessionID is now under SessionRuntimePayload; add an entry to the migration/upgrade notes for external Go SDK consumers, since the migration guide only covers general SDK incompatibility and provider name mapping.

🤖 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 `@sdk/go/contracts/types_019_gen.go` around lines 248 - 289, Update the
migration/upgrade notes for external Go SDK consumers to document that Provider
was removed from SessionStatus and SessionSummary, while ACPSessionID is now
accessed through SessionRuntimePayload. Add this alongside the existing SDK
incompatibility and provider-name mapping guidance, without changing the
generated types.
🧹 Nitpick comments (18)
internal/api/httpapi/httpapi_integration_test.go (1)

712-719: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add t.Parallel() to the new parallelizable subtests.

TestHTTPTaskRunLifecycleRoutesRoundTrip and other lifecycle helpers already use t.Parallel() with newIntegrationRuntime(t), indicating the harness supports concurrent subtests. Add t.Parallel() at the start of the closures for the new stream cursor, stop reason, and crash stop reason subtests to keep this package aligned with the default parallel pattern.

🤖 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 `@internal/api/httpapi/httpapi_integration_test.go` around lines 712 - 719, Add
t.Parallel() at the beginning of the closures for the new stream cursor, stop
reason, and crash stop reason subtests, including the test invoking
exerciseHTTPSessionStreamReconnectPreservesCursorWhenNoNewEventsExistYet. Keep
each subtest’s existing body and helper calls unchanged.

Source: Coding guidelines

internal/session/additional_test.go (1)

365-378: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wrap the new unbound-runtime scenario in its own t.Run("Should ...") subtest.

This adds a new, distinct test case (runtime-bind failure propagation) appended inline to TestPromptErrorPaths rather than as a subtest.

As per path instructions, "MUST use t.Run("Should...") pattern for ALL test cases."

♻️ Suggested wrapping
-	h = newHarness(t)
-	session = createSession(t, h)
-	session.markRuntimeUnbound(time.Now().UTC())
-	if got := session.Info().RuntimeStatus; got != RuntimeStatusUnbound {
-		t.Fatalf("unbound session runtime status = %q, want %q", got, RuntimeStatusUnbound)
-	}
-	bindErr := errors.New("runtime bind unavailable")
-	h.driver.startHook = func(_ acp.StartOpts, _ int) (*fakeProcess, error) {
-		return nil, bindErr
-	}
-	if _, err := h.manager.Prompt(testutil.Context(t), session.ID, "bind-runtime"); !errors.Is(err, bindErr) {
-		t.Fatalf("Prompt(unbound) error = %v, want runtime bind failure", err)
-	}
+	t.Run("Should propagate runtime bind failure for an unbound session", func(t *testing.T) {
+		t.Parallel()
+		h := newHarness(t)
+		session := createSession(t, h)
+		session.markRuntimeUnbound(time.Now().UTC())
+		if got := session.Info().RuntimeStatus; got != RuntimeStatusUnbound {
+			t.Fatalf("unbound session runtime status = %q, want %q", got, RuntimeStatusUnbound)
+		}
+		bindErr := errors.New("runtime bind unavailable")
+		h.driver.startHook = func(_ acp.StartOpts, _ int) (*fakeProcess, error) {
+			return nil, bindErr
+		}
+		if _, err := h.manager.Prompt(testutil.Context(t), session.ID, "bind-runtime"); !errors.Is(err, bindErr) {
+			t.Fatalf("Prompt(unbound) error = %v, want runtime bind failure", err)
+		}
+	})
🤖 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 `@internal/session/additional_test.go` around lines 365 - 378, Wrap the
unbound-runtime bind failure scenario in a dedicated t.Run subtest named with
the required “Should ...” pattern within TestPromptErrorPaths. Keep the existing
setup, startHook error injection, Prompt invocation, and assertion together
inside that subtest.
internal/session/manager_start.go (1)

52-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Error message is coupled to the helper's current single failure mode.

sessionStartSpecFromMeta today can only fail on speed normalization, so "validate resume speed" is accurate — but the wording will misattribute any future failure added to the shared helper. Prefer a neutral prefix and let the wrapped error carry the specifics.

♻️ Suggested wording
-	spec, err := sessionStartSpecFromMeta(meta, &resolvedWorkspace, cwd)
-	if err != nil {
-		return sessionStartSpec{}, fmt.Errorf("session: validate resume speed for %q: %w", meta.ID, err)
-	}
+	spec, err := sessionStartSpecFromMeta(meta, &resolvedWorkspace, cwd)
+	if err != nil {
+		return sessionStartSpec{}, fmt.Errorf("session: build resume start spec for %q: %w", meta.ID, err)
+	}
🤖 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 `@internal/session/manager_start.go` around lines 52 - 55, Update the error
wrapping in the session-start flow around sessionStartSpecFromMeta to use a
neutral description of the helper failure rather than specifically naming
resume-speed validation, while preserving meta.ID context and the wrapped error.
internal/session/manager_start_meta.go (1)

11-32: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Take the workspace by value to remove the nil-deref surface.

Line 32 dereferences workspace unconditionally. The only caller passes &resolvedWorkspace, so it is safe today, but this is a new shared helper and the pointer buys nothing here — the spec stores a copy anyway. Passing by value makes the nil case unrepresentable instead of requiring a guard.

♻️ Proposed signature change
 func sessionStartSpecFromMeta(
 	meta store.SessionMeta,
-	workspace *workspacepkg.ResolvedWorkspace,
+	workspace workspacepkg.ResolvedWorkspace,
 	cwd string,
 ) (sessionStartSpec, error) {
-		workspace:               *workspace,
+		workspace:               workspace,

Caller in internal/session/manager_start.go:

-	spec, err := sessionStartSpecFromMeta(meta, &resolvedWorkspace, cwd)
+	spec, err := sessionStartSpecFromMeta(meta, resolvedWorkspace, cwd)
🤖 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 `@internal/session/manager_start_meta.go` around lines 11 - 32, Change
sessionStartSpecFromMeta to accept workspacepkg.ResolvedWorkspace by value
instead of a pointer, and update its caller in the session-start flow to pass
resolvedWorkspace directly. Preserve the existing spec.workspace copy assignment
while eliminating the unconditional pointer dereference.
internal/session/manager_start_test.go (1)

161-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Also assert the rolled-back runtime state after a failed initial bind.

The subtest verifies the transcript stays empty but not the rollback invariant introduced by restoreRuntimeBinding — the session should be back to RuntimeStatusUnbound with RuntimeFailure populated, and the durable meta should agree. Without that, a regression that leaves the session stuck in binding would still pass.

💚 Suggested additional assertions
 		if len(page.Entries) != 0 {
 			t.Fatalf("TranscriptPage() entries = %#v, want empty", page.Entries)
 		}
+		live, ok := h.manager.Get(created.ID)
+		if !ok {
+			t.Fatalf("Get(%q) did not find created session", created.ID)
+		}
+		info := live.Info()
+		if info.RuntimeStatus != RuntimeStatusUnbound || info.RuntimeFailure == "" {
+			t.Fatalf(
+				"runtime after failed bind = %q/%q, want unbound with failure detail",
+				info.RuntimeStatus,
+				info.RuntimeFailure,
+			)
+		}
+		if meta := readMeta(t, live.MetaPath()); meta.RuntimeStatus != RuntimeStatusUnbound {
+			t.Fatalf("meta runtime status = %q, want %q", meta.RuntimeStatus, RuntimeStatusUnbound)
+		}
🤖 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 `@internal/session/manager_start_test.go` around lines 161 - 192, Extend the
“Should preserve an empty transcript when initial binding fails” test to assert
the rollback state after SendPrompt returns startErr: the session must have
RuntimeStatusUnbound with RuntimeFailure populated, and the durable metadata
must report the same runtime state. Use the existing session/runtime inspection
APIs and meta symbols used by restoreRuntimeBinding, while preserving the
transcript and cleanup assertions.
internal/session/manager_test.go (1)

4488-4492: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the fake driver did not stop the bound process.

proc.done default-case does not make Shutdown() async-stop behavior impossible: driver.Stop() increments stopCalls before calling the hook, so a buggy path can call Stop() and return only after the hook/closure signals shutdown. Add if h.driver.stopCalls != 0 { t.Fatalf("driver stop calls = %d, want 0", h.driver.stopCalls) } alongside the done check.

🤖 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 `@internal/session/manager_test.go` around lines 4488 - 4492, Strengthen the
Shutdown() async-stop assertion by checking the fake driver’s stopCalls counter
alongside the proc.done check. In the relevant test, fail when
h.driver.stopCalls is nonzero and report the observed count, ensuring the bound
process was not stopped.
internal/session/manager_prompt_event_storage.go (1)

67-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate prompt-runtime fallback logic between normalizeEvent and enrichRecordedAgentEvent.

Both functions perform the identical "clone snapshot, then fall back to runtimeBindingSnapshot().selection" sequence. Extracting a shared helper would avoid the two call sites silently diverging on a future change.

♻️ Proposed refactor
+func ensurePromptRuntime(event acp.AgentEvent, session *Session) acp.AgentEvent {
+	if session == nil || event.PromptRuntimeSnapshot() != nil {
+		return event
+	}
+	return event.WithPromptRuntime(promptRuntimeFromSelection(session.runtimeBindingSnapshot().selection))
+}

Then in normalizeEvent:

-		if normalized.PromptRuntimeSnapshot() == nil {
-			normalized = normalized.WithPromptRuntime(
-				promptRuntimeFromSelection(session.runtimeBindingSnapshot().selection),
-			)
-		}
+		normalized = ensurePromptRuntime(normalized, session)

And in enrichRecordedAgentEvent:

-	enriched = enriched.WithPromptRuntime(event.PromptRuntimeSnapshot())
-	if enriched.PromptRuntimeSnapshot() == nil {
-		enriched = enriched.WithPromptRuntime(promptRuntimeFromSelection(session.runtimeBindingSnapshot().selection))
-	}
+	enriched = enriched.WithPromptRuntime(event.PromptRuntimeSnapshot())
+	enriched = ensurePromptRuntime(enriched, session)

Also applies to: 172-175

🤖 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 `@internal/session/manager_prompt_event_storage.go` around lines 67 - 71,
Extract the duplicated prompt-runtime fallback sequence from normalizeEvent and
enrichRecordedAgentEvent into a shared helper that preserves the existing
snapshot check, cloning behavior, and runtimeBindingSnapshot().selection
fallback. Replace both call sites with the helper so future changes apply
consistently.
internal/store/types_session.go (1)

59-70: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider requiring RuntimeFailure when status is failed.

validateSessionRuntime (in internal/store/session_runtime.go) enforces that a failed runtime status carries a non-empty SessionRuntimeTransition, but nothing enforces that RuntimeFailure (the failure detail string) is also non-empty in that case. A session could persist RuntimeStatus: SessionRuntimeFailed with an empty RuntimeFailure, making the failure undiagnosable downstream (CLI/API/observe surfaces).

If RuntimeFailure is meant to always describe why binding failed, add that requirement alongside the existing transition check.

♻️ Possible fix
 func validateSessionRuntime(status SessionRuntimeStatus, transition SessionRuntimeTransition) error {
 	...
 	if status == SessionRuntimeFailed && strings.TrimSpace(string(transition)) == "" {
 		return fmt.Errorf("store: failed session runtime requires a transition strategy")
 	}
+	return nil
+}

Note: this would need to thread RuntimeFailure into validateSessionRuntime's signature (or a sibling check in types_session.go), since it currently only receives status and transition.

Also applies to: 111-116

🤖 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 `@internal/store/types_session.go` around lines 59 - 70, Update
validateSessionRuntime and its callers to also receive RuntimeFailure, and
require it to be non-empty whenever RuntimeStatus is SessionRuntimeFailed,
alongside the existing transition validation. Preserve current validation for
other statuses and ensure the Session struct validation path passes its
RuntimeFailure value through.
web/src/systems/network/mocks/fixtures.ts (1)

28-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consolidate the duplicate sessionRuntime mock helper.

@/systems/session/mocks/fixtures.ts already exports an equivalent sessionRuntime(provider, acpCaps?) helper, while web/src/systems/network/mocks/fixtures.ts re-implements it privately. Re-export or import a single shared helper so the runtime mock shape does not drift between fixtures.

🤖 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 `@web/src/systems/network/mocks/fixtures.ts` around lines 28 - 39, Remove the
private sessionRuntime implementation from the network mock fixtures and reuse
the exported sessionRuntime helper from the session mock fixtures via import or
re-export. Update references in the network fixtures to use that shared helper
while preserving the existing provider and acpCaps behavior.
internal/daemon/native_tools_test.go (1)

5589-5599: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Unconditional <-promptAccepted hangs the suite when the prompt call fails early.

If session_prompt is rejected before reaching SendPromptFn (schema/policy/workspace change), promptAccepted is never closed and the test blocks until the package timeout instead of reporting the failure. Select over the call result as well.

♻️ Fail fast instead of blocking
-		<-promptAccepted
-		select {
-		case call := <-promptCalls:
-			t.Fatalf("Registry.Call(session_prompt) returned before its live stream closed: %#v", call)
-		default:
-		}
+		select {
+		case <-promptAccepted:
+		case call := <-promptCalls:
+			t.Fatalf("Registry.Call(session_prompt) returned before submission: %#v", call)
+		case <-time.After(10 * time.Second):
+			t.Fatal("Registry.Call(session_prompt) never reached SendPrompt")
+		}
+		select {
+		case call := <-promptCalls:
+			t.Fatalf("Registry.Call(session_prompt) returned before its live stream closed: %#v", call)
+		default:
+		}
🤖 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 `@internal/daemon/native_tools_test.go` around lines 5589 - 5599, Update the
test flow around promptAccepted and promptCalls so it does not unconditionally
block on the acceptance signal. Select between promptCalls and promptAccepted,
immediately fail the test with the returned error when
Registry.Call(session_prompt) rejects before reaching SendPromptFn, and retain
the existing live-stream closure assertion for successfully accepted prompts.
web/src/components/assistant-ui/session-prompt-dispatch-store.ts (1)

47-52: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

requestStarted silently orphans an in-flight controller.

If a second dispatch starts before the first completes, the previous AbortController is dropped without abort(), so the first request keeps running and its later requestCompleted is discarded (controller mismatch). Consider aborting the superseded controller and bumping generation, mirroring pendingCanceled.

♻️ Abort the superseded controller
-      requestStarted: (context, event) => ({
-        ...context,
-        canceled: false,
-        controller: event.controller,
-        pending: true,
-      }),
+      requestStarted: (context, event, enqueue) => {
+        const previous = context.controller;
+        if (previous !== null && previous !== event.controller) {
+          enqueue.effect(() => previous.abort());
+        }
+        return {
+          ...context,
+          canceled: false,
+          controller: event.controller,
+          generation: previous === null ? context.generation : context.generation + 1,
+          pending: true,
+        };
+      },
🤖 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 `@web/src/components/assistant-ui/session-prompt-dispatch-store.ts` around
lines 47 - 52, Update requestStarted to abort any existing context.controller
before replacing it with event.controller, and increment generation when
superseding an in-flight request, mirroring pendingCanceled. Preserve the new
request’s pending state and controller while ensuring the prior request is
canceled and cannot leave stale work running.
web/src/components/assistant-ui/hooks/use-session-composer-state.ts (1)

56-65: 🩺 Stability & Availability | 🔵 Trivial

Synchronize draft updates with live composer text before applying persisted state.

composerDraftChanged is synchronous, so the current keystroke path doesn’t drop characters. But if draft persistence is made async/debounced later, this effect can apply a lagging draftText and overwrite the live input; keep the persisted draft out of the setText call when it cannot stay in sync with the user’s current text.

🤖 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 `@web/src/components/assistant-ui/hooks/use-session-composer-state.ts` around
lines 56 - 65, Update the useLayoutEffect synchronization logic in the session
composer state hook so persisted draftText is not passed to
aui.composer().setText when it may be stale relative to the live composer text;
validate or synchronize the current composer text before applying persisted
state, while preserving the existing session hydration guard and normal draft
restoration behavior.
web/src/systems/session/hooks/use-session-prompt-runtime.ts (1)

18-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the exported SessionRuntimeEffective alias instead of re-deriving it.

web/src/systems/session/types.ts (line 15) now exports exactly this type; the inline import("../types") form duplicates it and will drift.

♻️ Suggested change
-type SessionRuntimeEffective = NonNullable<
-  NonNullable<import("../types").SessionPayload["runtime"]>["effective"]
->;
+import type { SessionRuntimeEffective } from "../types";

(place the import with the other ../types imports)

🤖 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 `@web/src/systems/session/hooks/use-session-prompt-runtime.ts` around lines 18
- 20, Update the session prompt runtime type references to import and reuse the
exported SessionRuntimeEffective alias from ../types alongside the existing type
imports, then remove the locally re-derived alias definition.
web/src/systems/session/hooks/use-session-create-dialog.ts (2)

114-118: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing agent aborts submit with no user feedback.

Unlike the participation and workspace-path branches, an empty agentName returns silently and leaves submitError untouched, so the dialog appears inert. Route it through validationFailed for consistency.

🛠️ Suggested fix
-    if (agentName.length === 0) return;
+    if (agentName.length === 0) {
+      store.trigger.validationFailed({ message: "Select an agent to start a session." });
+      return;
+    }
🤖 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 `@web/src/systems/session/hooks/use-session-create-dialog.ts` around lines 114
- 118, Update submit in use-session-create-dialog so an empty trimmed agentName
calls validationFailed before returning, instead of silently returning. Preserve
the existing validation flow and feedback behavior used by the participation and
workspace-path checks.

96-97: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

selectedAgent lookup no longer changes the result.

selectedAgent?.name can only ever be draft.agentName (the predicate matches on that name), so the fallback makes the lookup redundant now that first-agent defaulting is gone. Either drop the lookup or use selectedAgent for validation (e.g. reject an agent that isn't in agentList).

🤖 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 `@web/src/systems/session/hooks/use-session-create-dialog.ts` around lines 96 -
97, The selectedAgent lookup in the session creation dialog is redundant because
its name always equals draft.agentName when found. Remove selectedAgent and
derive selectedAgentName directly from draft.agentName, unless the flow needs to
validate membership in agentList; do not retain the current no-op fallback.
web/src/systems/session/hooks/__tests__/use-session-prompt-runtime.test.tsx (1)

43-50: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Type authState with the payload's auth-state union.

authState: string plus the as ProviderSummary cast lets invalid states compile, weakening the fixture as a contract check.

♻️ Suggested typing
-function provider(name: string, authState: string): ProviderSummary {
+function provider(
+  name: string,
+  authState: NonNullable<ProviderSummary["auth_status"]>["state"]
+): ProviderSummary {
🤖 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 `@web/src/systems/session/hooks/__tests__/use-session-prompt-runtime.test.tsx`
around lines 43 - 50, Update the provider fixture helper’s authState parameter
to use the payload’s auth-state union type rather than string, and remove the
broad ProviderSummary cast so invalid states are rejected at compile time while
preserving the existing fixture fields.
web/src/systems/session/contexts/session-prompt-runtime-context.tsx (1)

22-29: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

input is recreated each render, so the sync effect runs on every render.

sessionPromptRuntimeInput returns a fresh object, so [input, store] never stays stable. Correctness is saved only by sameInput no-op inside the store. Memoizing on the primitive inputs keeps the effect a genuine external-system sync.

♻️ Suggested memoization
-  const input = sessionPromptRuntimeInput(session, canPrompt);
+  const input = useMemo(() => sessionPromptRuntimeInput(session, canPrompt), [session, canPrompt]);

(add useMemo to the React import)

🤖 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 `@web/src/systems/session/contexts/session-prompt-runtime-context.tsx` around
lines 22 - 29, Memoize the input created by sessionPromptRuntimeInput in the
session prompt runtime context using useMemo, with its primitive source values
as dependencies. Keep the existing store binding and inputUpdated effect
unchanged so the effect only synchronizes when the underlying input values
change.
web/src/systems/session/hooks/__tests__/use-session-create-dialog.test.tsx (1)

227-229: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Ordering assertion inside the navigate mock can be swallowed.

If this expect throws, the rejection is handled by the store's navigation execution path and the test may still pass. Assert ordering explicitly instead.

♻️ Deterministic ordering assertion
-    mockNavigate.mockImplementation(async () => {
-      expect(mockActivateWorkspace).toHaveBeenCalledWith(sessionInOtherWorkspace);
-    });
+    mockNavigate.mockResolvedValue(undefined);

and after waitFor:

expect(mockActivateWorkspace).toHaveBeenCalledWith(sessionInOtherWorkspace);
expect(mockActivateWorkspace.mock.invocationCallOrder[0]).toBeLessThan(
  mockNavigate.mock.invocationCallOrder[0]
);
🤖 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 `@web/src/systems/session/hooks/__tests__/use-session-create-dialog.test.tsx`
around lines 227 - 229, Move the activation assertion out of the mockNavigate
implementation in the session creation dialog test so failures cannot be
swallowed by the navigation execution path. After the existing waitFor, assert
mockActivateWorkspace was called with sessionInOtherWorkspace and explicitly
verify its first invocation order precedes mockNavigate’s first invocation using
their invocationCallOrder values.
🤖 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 `@internal/api/contract/prompt_runtime_selection.go`:
- Around line 12-17: Update the runtime selection conversion to apply
strings.TrimSpace to payload.Provider, payload.Model, and the string-converted
payload.ReasoningEffort before assigning them to session.RuntimeSelection,
matching runtimeSelectionPayloadFromInfo and SessionPayloadFromStoreInfo while
leaving Speed unchanged.

In `@internal/api/core/handlers_test.go`:
- Around line 215-250: Add API-level tests alongside the session handler
coverage using performRequest and fixture.Engine for ACP NegotiationError and
provider-auth failures. Assert the HTTP response preserves each error’s
ErrorPayload.Diagnostic.code, evidence, and SuggestedCommand, including the
expected negotiation/provider-auth diagnostic values rather than only checking
the status.

In `@internal/extension/host_api_test.go`:
- Around line 532-670: Wrap the bodies of
TestHostAPIHandlerSessionReadsProjectNestedRuntime and
TestHostAPIHandlerSessionsPromptReturnsQueuedRuntimeAdmission in t.Run subtests
named with the required “Should ...” pattern. Keep t.Parallel and all existing
setup and assertions within the corresponding subtest, matching the repository’s
established test structure.

In `@internal/observe/reconcile.go`:
- Around line 153-178: Update the session projection in the reconcile path to
apply strings.TrimSpace to meta.Model, meta.ReasoningEffort, and
meta.RuntimeFailure, matching sessionInfoFromCatalog and
sessionInfoFromSandboxReconcileMeta while leaving the other field mappings
unchanged.

In `@internal/session/manager_managed_input_submit.go`:
- Around line 29-59: Extend managedInput and managedInputFromQueueEntry to
retain the queued runtime snapshot by adding and copying
store.SessionInputRuntime from entry.Runtime. Update managedInputPromptRequest
and the related dispatch path to forward this runtime data into prompt runtime
selection, preserving provider, model, reasoning effort, and speed for queued
and steered prompts.

In `@internal/session/manager_process_watchers.go`:
- Around line 5-14: The helper ensureProcessWatchContext requires processWatchMu
to protect its writes, but manager_defaults.go calls it without holding that
mutex. Rename it to ensureProcessWatchContextLocked and update every caller to
acquire processWatchMu before invoking it, or document the locking requirement
if preserving the existing name.

In `@internal/session/manager_prompt_runtime.go`:
- Around line 153-158: Update the rollback branch in the session lifecycle flow
around persistSessionLifecycleState to persist the restored binding using
runtimeCleanupContext() instead of the failing request ctx. Keep the existing
restoreRuntimeBinding, candidate shutdown, and joined-error behavior unchanged.

In `@internal/session/manager_prompt_submit.go`:
- Around line 42-52: Update GoalDispatchPrompt’s reject-if-busy flow so
turn.start is not left uncompensated when prompt admission fails with
ErrPromptInProgress. Move dispatchTurnStart until after successful slot
reacquisition, or emit the existing turn.end/hook-cancellation compensation
before returning the busy goal-draft result; preserve normal turn lifecycle
behavior for successful admissions.

In `@internal/session/manager_transition.go`:
- Around line 123-149: The catalog update path must persist runtime selection
state after non-register prompt-runtime binding. Update
sessionCatalogStateUpdate() and its caller in UpdateSessionState() to write
provider/model, reasoning effort, speed resolution, runtime status/transition,
and runtime failure, or replace that path with an equivalent full upsert using
the complete session data.

In `@internal/session/provider_lifecycle_test.go`:
- Around line 85-99: Protect the fakeDriver hook assignments in the test with
h.driver.mu, matching the mutex used when fakeDriver operations read stopHook
and startHook. Lock around each assignment, including the stopHook callback
setup before SendPrompt, and release the mutex immediately after assignment.

In `@internal/session/runtime.go`:
- Around line 62-66: Update NormalizeRuntimeSelection’s ReasoningEffort
validation branch to wrap the ValidateReasoningEffort error with
ErrInvalidRuntimeOverride using the requested dual-wrapping format, while
preserving the existing validation and early-return behavior.

In `@internal/session/session_test.go`:
- Around line 241-243: Extend the session fixture assertions in
internal/session/session_test.go at lines 241-243 and 286-286 to verify the
configured runtime metadata through Meta(), Info(), and ReadSessionMeta():
assert ready status, initial-bind transition, and ACP session ID for the first
fixture, and unbound status for the second. Keep these checks focused on the
existing state-management and projection tests.

In `@internal/store/meta_test.go`:
- Line 27: Update both metadata read-back tests in internal/store/meta_test.go
to explicitly compare readBack.RuntimeStatus with SessionRuntimeReady, alongside
the existing metadata assertions. Ensure the assertions validate the persisted
“ready” status for both fixtures and preserve outcome-based verification.

In `@internal/store/session_meta_types.go`:
- Around line 38-40: Update validateSessionRuntime or the SessionMeta read path
to normalize a missing or empty runtime_status from legacy session.json files to
the unbound runtime status before validation. Preserve validation of all
explicitly provided runtime status enum values.

In `@internal/tools/builtin/sessions.go`:
- Around line 127-133: Replace the positional `descriptors[1]` and
`descriptors[2]` mutations in `sessionDescriptors` with the same explicit
descriptor-construction pattern used by `sessionListDescriptor`, attaching each
output schema directly to its corresponding session tool. Ensure schemas remain
associated by tool identity rather than `sessionTools` ordering.

In `@sdk/go/contracts/types_011_gen.go`:
- Around line 10-37: Regenerate the Go SDK contracts so the stale
MemoryStoreParams and MessageAttachment types are removed, and update
HostAPIMemoryStoreContract to no longer reference them. Verify generated
declarations and related fields consistently reflect the intended contract
removal.

In `@web/src/systems/session/components/session-create-advanced-section.tsx`:
- Around line 49-67: Update WorkspaceCommandSelect to accept an accessibility
prop for the trigger, such as ariaLabelledby or triggerId, and pass the
corresponding FieldTitle id "session-create-workspace-label" from the session
create advanced section. Ensure the rendered workspace select trigger associates
with that label while preserving existing props and behavior.

In `@web/src/systems/session/mocks/__tests__/handlers.test.ts`:
- Around line 55-80: Update the createSession request in the session-create test
to remove the prompt field from the POST payload, while preserving agent_name,
workspace, headers, and the existing response assertions.

In `@web/src/systems/session/stores/session-prompt-runtime-store.ts`:
- Around line 84-102: Update sessionPromptRuntimeInput and remove the throwing
requireWorkspaceId path so missing or whitespace-only session.workspace_id
produces degraded input instead of an exception. Derive workspaceId only from
the valid outer session/workspace value, set canPrompt to false when no valid
workspace is available, and preserve normal prompting for valid workspaces;
defer adding an error boundary until this input path is safe.

---

Outside diff comments:
In `@internal/api/udsapi/transport_parity_integration_test.go`:
- Around line 686-696: Update the stopResp error-handling block in the UDS
stop-session test to check and handle errors from io.ReadAll and
stopResp.Body.Close instead of assigning them to _. Match the explicit
error-checking pattern already used by the nearby resumeResp handling, while
preserving the existing failure message and status validation.

In `@internal/cli/session_list.go`:
- Around line 123-132: Update the JSONL serialization path in sessionListBundle
so writeJSONLine receives a SessionRecord whose runtime provider matches
sessionRuntimeProvider(item), including the Runtime.Effective.Provider
projection. Preserve the existing displayed-row and other output-mode behavior
while applying this projection specifically to JSONL output.

In `@internal/session/manager_input_dispatch.go`:
- Around line 89-100: Update dispatchHumanQueuedInput so the !ok branch after
newQueuedInputPromptRequest handles the claimed entry consistently with
handleQueuedInputDispatchError: release or mark the entry failed as appropriate,
then call m.startNextQueuedInputPrompt(target) before returning. Ensure failed
prompt-request construction cannot leave the queue stalled.

In `@internal/store/globaldb/sqlcgen/session_core.sql.go`:
- Around line 265-315: The session upsert’s optimistic-concurrency guard must
not reject current runtime lifecycle updates because of stale network metadata.
Update the conflict handling around UpsertSessionParams and its ON CONFLICT
WHERE clause to include the relevant Runtime* fields in the comparison, or
separate runtime lifecycle updates from the network-guarded path; preserve the
existing network protections while ensuring runtime status and transition
changes are applied.

In `@sdk/go/contracts/types_019_gen.go`:
- Around line 248-289: Update the migration/upgrade notes for external Go SDK
consumers to document that Provider was removed from SessionStatus and
SessionSummary, while ACPSessionID is now accessed through
SessionRuntimePayload. Add this alongside the existing SDK incompatibility and
provider-name mapping guidance, without changing the generated types.

---

Nitpick comments:
In `@internal/api/httpapi/httpapi_integration_test.go`:
- Around line 712-719: Add t.Parallel() at the beginning of the closures for the
new stream cursor, stop reason, and crash stop reason subtests, including the
test invoking
exerciseHTTPSessionStreamReconnectPreservesCursorWhenNoNewEventsExistYet. Keep
each subtest’s existing body and helper calls unchanged.

In `@internal/daemon/native_tools_test.go`:
- Around line 5589-5599: Update the test flow around promptAccepted and
promptCalls so it does not unconditionally block on the acceptance signal.
Select between promptCalls and promptAccepted, immediately fail the test with
the returned error when Registry.Call(session_prompt) rejects before reaching
SendPromptFn, and retain the existing live-stream closure assertion for
successfully accepted prompts.

In `@internal/session/additional_test.go`:
- Around line 365-378: Wrap the unbound-runtime bind failure scenario in a
dedicated t.Run subtest named with the required “Should ...” pattern within
TestPromptErrorPaths. Keep the existing setup, startHook error injection, Prompt
invocation, and assertion together inside that subtest.

In `@internal/session/manager_prompt_event_storage.go`:
- Around line 67-71: Extract the duplicated prompt-runtime fallback sequence
from normalizeEvent and enrichRecordedAgentEvent into a shared helper that
preserves the existing snapshot check, cloning behavior, and
runtimeBindingSnapshot().selection fallback. Replace both call sites with the
helper so future changes apply consistently.

In `@internal/session/manager_start_meta.go`:
- Around line 11-32: Change sessionStartSpecFromMeta to accept
workspacepkg.ResolvedWorkspace by value instead of a pointer, and update its
caller in the session-start flow to pass resolvedWorkspace directly. Preserve
the existing spec.workspace copy assignment while eliminating the unconditional
pointer dereference.

In `@internal/session/manager_start_test.go`:
- Around line 161-192: Extend the “Should preserve an empty transcript when
initial binding fails” test to assert the rollback state after SendPrompt
returns startErr: the session must have RuntimeStatusUnbound with RuntimeFailure
populated, and the durable metadata must report the same runtime state. Use the
existing session/runtime inspection APIs and meta symbols used by
restoreRuntimeBinding, while preserving the transcript and cleanup assertions.

In `@internal/session/manager_start.go`:
- Around line 52-55: Update the error wrapping in the session-start flow around
sessionStartSpecFromMeta to use a neutral description of the helper failure
rather than specifically naming resume-speed validation, while preserving
meta.ID context and the wrapped error.

In `@internal/session/manager_test.go`:
- Around line 4488-4492: Strengthen the Shutdown() async-stop assertion by
checking the fake driver’s stopCalls counter alongside the proc.done check. In
the relevant test, fail when h.driver.stopCalls is nonzero and report the
observed count, ensuring the bound process was not stopped.

In `@internal/store/types_session.go`:
- Around line 59-70: Update validateSessionRuntime and its callers to also
receive RuntimeFailure, and require it to be non-empty whenever RuntimeStatus is
SessionRuntimeFailed, alongside the existing transition validation. Preserve
current validation for other statuses and ensure the Session struct validation
path passes its RuntimeFailure value through.

In `@web/src/components/assistant-ui/hooks/use-session-composer-state.ts`:
- Around line 56-65: Update the useLayoutEffect synchronization logic in the
session composer state hook so persisted draftText is not passed to
aui.composer().setText when it may be stale relative to the live composer text;
validate or synchronize the current composer text before applying persisted
state, while preserving the existing session hydration guard and normal draft
restoration behavior.

In `@web/src/components/assistant-ui/session-prompt-dispatch-store.ts`:
- Around line 47-52: Update requestStarted to abort any existing
context.controller before replacing it with event.controller, and increment
generation when superseding an in-flight request, mirroring pendingCanceled.
Preserve the new request’s pending state and controller while ensuring the prior
request is canceled and cannot leave stale work running.

In `@web/src/systems/network/mocks/fixtures.ts`:
- Around line 28-39: Remove the private sessionRuntime implementation from the
network mock fixtures and reuse the exported sessionRuntime helper from the
session mock fixtures via import or re-export. Update references in the network
fixtures to use that shared helper while preserving the existing provider and
acpCaps behavior.

In `@web/src/systems/session/contexts/session-prompt-runtime-context.tsx`:
- Around line 22-29: Memoize the input created by sessionPromptRuntimeInput in
the session prompt runtime context using useMemo, with its primitive source
values as dependencies. Keep the existing store binding and inputUpdated effect
unchanged so the effect only synchronizes when the underlying input values
change.

In `@web/src/systems/session/hooks/__tests__/use-session-create-dialog.test.tsx`:
- Around line 227-229: Move the activation assertion out of the mockNavigate
implementation in the session creation dialog test so failures cannot be
swallowed by the navigation execution path. After the existing waitFor, assert
mockActivateWorkspace was called with sessionInOtherWorkspace and explicitly
verify its first invocation order precedes mockNavigate’s first invocation using
their invocationCallOrder values.

In `@web/src/systems/session/hooks/__tests__/use-session-prompt-runtime.test.tsx`:
- Around line 43-50: Update the provider fixture helper’s authState parameter to
use the payload’s auth-state union type rather than string, and remove the broad
ProviderSummary cast so invalid states are rejected at compile time while
preserving the existing fixture fields.

In `@web/src/systems/session/hooks/use-session-create-dialog.ts`:
- Around line 114-118: Update submit in use-session-create-dialog so an empty
trimmed agentName calls validationFailed before returning, instead of silently
returning. Preserve the existing validation flow and feedback behavior used by
the participation and workspace-path checks.
- Around line 96-97: The selectedAgent lookup in the session creation dialog is
redundant because its name always equals draft.agentName when found. Remove
selectedAgent and derive selectedAgentName directly from draft.agentName, unless
the flow needs to validate membership in agentList; do not retain the current
no-op fallback.

In `@web/src/systems/session/hooks/use-session-prompt-runtime.ts`:
- Around line 18-20: Update the session prompt runtime type references to import
and reuse the exported SessionRuntimeEffective alias from ../types alongside the
existing type imports, then remove the locally re-derived alias definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

Comment thread internal/api/contract/prompt_runtime_selection.go
Comment thread internal/api/core/handlers_test.go
Comment thread internal/extension/host_api_test.go
Comment thread internal/observe/reconcile.go
Comment thread internal/session/manager_managed_input_submit.go
Comment thread internal/tools/builtin/sessions.go
Comment thread sdk/go/contracts/types_011_gen.go
Comment thread web/src/systems/session/mocks/__tests__/handlers.test.ts
Comment thread web/src/systems/session/stores/session-prompt-runtime-store.ts Outdated
@pedronauck

pedronauck commented Jul 31, 2026 •

Copy link
Copy Markdown
Member Author

Review remediation summary

Commit 3cfaf793 addresses the actionable CodeRabbit and React review findings for the session runtime implementation.

Correctness fixes

  • Normalized provider, model, reasoning-effort, and reconcile metadata at API/store projection boundaries.
  • Preserved negotiation and provider-auth diagnostics through HTTP responses, with code, evidence, and suggested-command assertions.
  • Preserved the admitted runtime snapshot across queued, steered, and managed-input dispatch; managed prompts now negotiate that snapshot before driver submission.
  • Corrected prompt lifecycle ordering so turn.start is emitted only after prompt-slot acquisition.
  • Made runtime rollback persistence independent of a canceled request context.
  • Persisted runtime-only catalog transitions without overwriting unrelated session state or weakening network participation guards.
  • Synchronized process-watch mutations and provider test hooks.
  • Removed duplicate runtime-validation wrapping while preserving the underlying cause.
  • Strengthened session, metadata, queue, transition, and runtime projection coverage.
  • Bound native-tool output schemas by tool identity instead of slice position.
  • Handled UDS response read/close failures and removed an impossible queued-prompt constructor branch.

Web fixes

  • Associated the advanced Workspace selector with its visible label.
  • Removed the obsolete prompt field from create-session mocks.
  • Degraded safely to canPrompt: false when workspace data is absent instead of throwing during render.
  • Normalized nullable runtime state and prevented stale drafts/requests from overwriting the active composer runtime.
  • Preserved create-session navigation ordering and strengthened selected-agent/auth typing.
  • Reused canonical runtime types and fixtures.

Findings verified as non-actionable

  • The React ref finding is stale: the pushed SHA passes the React Doctor workflow; a fresh React Doctor 0.9.2 diff scan reports 0 diagnostics and score 100.
  • Missing legacy runtime_status remains invalid by design under the repository zero-legacy hard-cut policy; new metadata always writes the field.
  • MemoryStoreParams and MessageAttachment remain active generated Host API contracts. Code generation is consistent; deleting them would break the current SDK surface.
  • Provider JSONL, SQL migration, and catalog-upsert observations were checked against the current contract and persistence paths and do not reproduce on this branch.

Validation

  • Web Turbo lint/typecheck/test: 5 tasks passed, 522 test files, 4,094 tests, zero lint warnings/errors.
  • Directed Go race suite: 10 affected packages, 4,803 tests passed.
  • Extension cancellation synchronization regression: 100/100 repeated runs passed.
  • React Doctor 0.9.2: 0 diagnostics, score 100.
  • UI capture reviewed at 1440x900: advanced create-session form has Agent, Workspace, Session name, Working path, and Network participation; the prompt box is absent.
  • git diff --check: passed.

Compozy Impact Audit

  • Native tools: session_create and session_prompt descriptors now bind output schemas by tool ID. Tool IDs, risk flags, input/output contract shapes, and capability gates are unchanged; native-tool tests pass.
  • Extensibility and hooks: prompt runtime negotiation and turn lifecycle ordering were corrected through existing hooks and registries. No new extension surface, hook, bundle, sidecar, or config key was introduced.
  • Workspace data isolation: runtime metadata remains session-scoped. Existing workspace_id propagation through HTTP, UDS, core, store, web, and events is unchanged and covered by projection/transport tests.
  • Official Compozy skill: no public CLI path, tool ID, config key, capability, bundle, or hook contract changed, so no bundled skill update is required.

All review threads are resolved. Fresh CI on 3cfaf793 is the merge criterion.

@pedronauck
pedronauck merged commit 546b182 into main Jul 31, 2026
13 of 18 checks passed
@pedronauck
pedronauck deleted the session-runtime branch July 31, 2026 01:04

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/store/session_runtime.go (1)

29-54: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add a test case for failed session runtime failure validation.

validateSessionRuntime rejects SessionRuntimeFailed when failure is empty, but there are no Go tests exercising this rule. Add a table-driven case for Status: SessionRuntimeFailed with an empty failure message.

🤖 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 `@internal/store/session_runtime.go` around lines 29 - 54, Add a table-driven
test case covering validateSessionRuntime with Status set to
SessionRuntimeFailed and an empty failure message, asserting validation returns
an error. Reuse the existing test structure and expected-error conventions,
while providing a valid transition so the failure-message rule is specifically
exercised.
🧹 Nitpick comments (6)
internal/store/store_helpers_test.go (1)

160-201: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Run the added table cases in parallel.

These cases execute serially because the shared t.Run body does not call t.Parallel(). Add it there; the validation closures only read immutable fixtures.

As per coding guidelines, “Go tests must use Should … subtest names, default to t.Parallel.”

🤖 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 `@internal/store/store_helpers_test.go` around lines 160 - 201, Add
t.Parallel() to the shared t.Run body that executes these validation table
cases, while preserving the existing “Should …” subtest names and validation
behavior.

Source: Coding guidelines

internal/store/globaldb/global_db_test.go (1)

998-1059: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wrap this scenario in a Should … subtest.

The updated integration scenario runs directly in TestGlobalDBRegisterUpdateAndListSessions; move its body into t.Run("Should persist runtime replacement fields", ...).

As per path instructions, “MUST use t.Run("Should...") pattern for ALL test cases.”

🤖 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 `@internal/store/globaldb/global_db_test.go` around lines 998 - 1059, Wrap the
runtime replacement persistence scenario in
TestGlobalDBRegisterUpdateAndListSessions inside a t.Run subtest named "Should
persist runtime replacement fields". Move the related setup, updates,
ListSessions call, and assertions into that subtest while preserving their
behavior and ensuring the required Should-prefixed pattern is used.

Source: Path instructions

internal/session/manager_transition_test.go (1)

52-58: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Exercise a bound runtime before asserting propagation.

createSession is promptless/unbound, and the catalog was registered from the same info before stopping. This equality check still passes if the update omits runtime fields. Bind non-default provider/model/status data first and assert a non-nil SpeedResolution too.

As per path instructions, “MUST test meaningful business logic” and “Verify tests can fail when business logic changes.”

🤖 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 `@internal/session/manager_transition_test.go` around lines 52 - 58, Strengthen
the propagation test around createSession and the stopping catalog by binding
non-default provider, model, and runtime status data before issuing the stop
request, rather than relying on the promptless unbound session. Extend the
assertion to require a non-nil SpeedResolution and verify its propagated value,
ensuring the test fails when runtime-field propagation is omitted.

Source: Path instructions

web/src/components/assistant-ui/hooks/use-session-composer-state.ts (1)

57-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Local composerText shadows the state selector value.

Line 59 declares composerText inside the effect while line 20 already binds a composerText from useAuiState. Renaming the local (e.g. currentComposerText) makes the hydration guard easier to read and avoids accidental use of the stale render-scoped value later.

♻️ Suggested rename
-    const composerText = aui.composer().getState().text;
-    if (sameSession && composerText !== hydratedDraftTextRef.current) {
+    const currentComposerText = aui.composer().getState().text;
+    if (sameSession && currentComposerText !== hydratedDraftTextRef.current) {
       return;
     }
🤖 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 `@web/src/components/assistant-ui/hooks/use-session-composer-state.ts` around
lines 57 - 66, Rename the local composer text variable inside the
useLayoutEffect in useSessionComposerState, such as to currentComposerText, and
update the sameSession hydration guard to use it; leave the composerText value
returned by useAuiState unchanged.
internal/api/httpapi/handlers_test.go (1)

2635-2638: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the redundant loop-variable copy.

Since Go 1.22 loop variables are per-iteration, so testCase := testCase is unnecessary.

♻️ Suggested cleanup
 	for _, testCase := range testCases {
-		testCase := testCase
 		t.Run(testCase.name, func(t *testing.T) {
🤖 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 `@internal/api/httpapi/handlers_test.go` around lines 2635 - 2638, Remove the
redundant testCase := testCase assignment in the testCases loop while keeping
the parallel subtest structure and existing test behavior unchanged.
internal/session/manager_prompt_event_storage.go (1)

74-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clarify the snapshot materialization.

WithPromptRuntime(event.PromptRuntimeSnapshot()) creates a new event with a cloned snapshot payload. If that cloning is intentional, add a short comment; otherwise this assignment can be omitted.

🤖 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 `@internal/session/manager_prompt_event_storage.go` around lines 74 - 80,
Clarify the intent of the initial assignment in promptRuntimeFallbackForEvent:
if cloning event.PromptRuntimeSnapshot() via WithPromptRuntime is required, add
a brief comment explaining the snapshot materialization; otherwise remove that
assignment while preserving the existing nil-session and fallback behavior.
🤖 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 `@internal/store/meta_test.go`:
- Around line 64-66: Update the metadata tests in internal/store/meta_test.go at
lines 64-66 and 161-163: wrap the round-trip scenario in t.Run("Should persist
ready runtime status", ...) and wrap the concurrent-write scenario in a
corresponding descriptive “Should …” subtest, preserving each scenario’s
existing assertions and behavior.

In `@web/src/systems/session/hooks/__tests__/use-session-create-dialog.test.tsx`:
- Around line 244-247: Update the test around mockActivateWorkspace and
mockNavigate to explicitly assert that mockNavigate was called before comparing
invocationCallOrder, then retain the ordering assertion to verify activation
precedes navigation without an invocation-order fallback.

---

Outside diff comments:
In `@internal/store/session_runtime.go`:
- Around line 29-54: Add a table-driven test case covering
validateSessionRuntime with Status set to SessionRuntimeFailed and an empty
failure message, asserting validation returns an error. Reuse the existing test
structure and expected-error conventions, while providing a valid transition so
the failure-message rule is specifically exercised.

---

Nitpick comments:
In `@internal/api/httpapi/handlers_test.go`:
- Around line 2635-2638: Remove the redundant testCase := testCase assignment in
the testCases loop while keeping the parallel subtest structure and existing
test behavior unchanged.

In `@internal/session/manager_prompt_event_storage.go`:
- Around line 74-80: Clarify the intent of the initial assignment in
promptRuntimeFallbackForEvent: if cloning event.PromptRuntimeSnapshot() via
WithPromptRuntime is required, add a brief comment explaining the snapshot
materialization; otherwise remove that assignment while preserving the existing
nil-session and fallback behavior.

In `@internal/session/manager_transition_test.go`:
- Around line 52-58: Strengthen the propagation test around createSession and
the stopping catalog by binding non-default provider, model, and runtime status
data before issuing the stop request, rather than relying on the promptless
unbound session. Extend the assertion to require a non-nil SpeedResolution and
verify its propagated value, ensuring the test fails when runtime-field
propagation is omitted.

In `@internal/store/globaldb/global_db_test.go`:
- Around line 998-1059: Wrap the runtime replacement persistence scenario in
TestGlobalDBRegisterUpdateAndListSessions inside a t.Run subtest named "Should
persist runtime replacement fields". Move the related setup, updates,
ListSessions call, and assertions into that subtest while preserving their
behavior and ensuring the required Should-prefixed pattern is used.

In `@internal/store/store_helpers_test.go`:
- Around line 160-201: Add t.Parallel() to the shared t.Run body that executes
these validation table cases, while preserving the existing “Should …” subtest
names and validation behavior.

In `@web/src/components/assistant-ui/hooks/use-session-composer-state.ts`:
- Around line 57-66: Rename the local composer text variable inside the
useLayoutEffect in useSessionComposerState, such as to currentComposerText, and
update the sameSession hydration guard to use it; leave the composerText value
returned by useAuiState unchanged.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e0ed0bfe-60f6-4613-86d4-b7ce53a362c7

📥 Commits

Reviewing files that changed from the base of the PR and between e7fdabc and 3cfaf79.

📒 Files selected for processing (56)
  • internal/api/contract/contract_test.go
  • internal/api/contract/prompt_runtime_selection.go
  • internal/api/httpapi/handlers_test.go
  • internal/api/httpapi/httpapi_integration_test.go
  • internal/api/udsapi/transport_parity_integration_test.go
  • internal/daemon/native_tools_test.go
  • internal/extension/build_runner_unix_test.go
  • internal/extension/host_api_test.go
  • internal/observe/reconcile.go
  • internal/observe/reconcile_test.go
  • internal/session/additional_test.go
  • internal/session/manager_busy_input_test.go
  • internal/session/manager_defaults.go
  • internal/session/manager_input_dispatch.go
  • internal/session/manager_managed_input_submit.go
  • internal/session/manager_process_watchers.go
  • internal/session/manager_prompt_event_storage.go
  • internal/session/manager_prompt_runtime.go
  • internal/session/manager_prompt_submit.go
  • internal/session/manager_start.go
  • internal/session/manager_start_meta.go
  • internal/session/manager_start_test.go
  • internal/session/manager_test.go
  • internal/session/manager_transition.go
  • internal/session/manager_transition_test.go
  • internal/session/provider_lifecycle_test.go
  • internal/session/runtime.go
  • internal/session/session_test.go
  • internal/store/globaldb/global_db_runtime_metadata_cut.go
  • internal/store/globaldb/global_db_session.go
  • internal/store/globaldb/global_db_session_scan.go
  • internal/store/globaldb/global_db_test.go
  • internal/store/meta_test.go
  • internal/store/session_meta_types.go
  • internal/store/session_runtime.go
  • internal/store/store_helpers_test.go
  • internal/store/types_session.go
  • internal/tools/builtin/sessions.go
  • web/src/components/assistant-ui/__tests__/session-thread.test.tsx
  • web/src/components/assistant-ui/hooks/use-session-composer-state.ts
  • web/src/components/assistant-ui/session-prompt-dispatch-store.ts
  • web/src/systems/network/mocks/fixtures.ts
  • web/src/systems/session/components/__tests__/session-chat-runtime-provider.test.tsx
  • web/src/systems/session/components/session-create-advanced-section.tsx
  • web/src/systems/session/contexts/session-prompt-runtime-context.tsx
  • web/src/systems/session/hooks/__tests__/use-session-create-dialog.test.tsx
  • web/src/systems/session/hooks/__tests__/use-session-prompt-runtime.test.tsx
  • web/src/systems/session/hooks/use-session-create-dialog.ts
  • web/src/systems/session/hooks/use-session-prompt-runtime.ts
  • web/src/systems/session/mocks/__tests__/handlers.test.ts
  • web/src/systems/session/mocks/fixtures.ts
  • web/src/systems/session/mocks/index.ts
  • web/src/systems/session/stores/__tests__/session-prompt-runtime-store.test.ts
  • web/src/systems/session/stores/session-prompt-runtime-store.ts
  • web/src/systems/workspace/components/__tests__/workspace-command-select.test.tsx
  • web/src/systems/workspace/components/workspace-command-select.tsx
💤 Files with no reviewable changes (1)
  • web/src/systems/session/mocks/tests/handlers.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/systems/session/hooks/use-session-prompt-runtime.ts

Comment on lines +64 to +66
if readBack.RuntimeStatus != SessionRuntimeReady {
t.Fatalf("ReadSessionMeta().RuntimeStatus = %q, want %q", readBack.RuntimeStatus, SessionRuntimeReady)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use required Should … subtests for these metadata cases.

  • internal/store/meta_test.go#L64-L66: wrap the round-trip scenario in t.Run("Should persist ready runtime status", ...).
  • internal/store/meta_test.go#L161-L163: wrap the concurrent-write scenario in a corresponding Should … subtest.

As per coding guidelines, “Go tests must use Should … subtest names.”

📍 Affects 1 file
  • internal/store/meta_test.go#L64-L66 (this comment)
  • internal/store/meta_test.go#L161-L163
🤖 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 `@internal/store/meta_test.go` around lines 64 - 66, Update the metadata tests
in internal/store/meta_test.go at lines 64-66 and 161-163: wrap the round-trip
scenario in t.Run("Should persist ready runtime status", ...) and wrap the
concurrent-write scenario in a corresponding descriptive “Should …” subtest,
preserving each scenario’s existing assertions and behavior.

Source: Coding guidelines

Comment on lines +244 to +247
expect(mockActivateWorkspace).toHaveBeenCalledWith(sessionInOtherWorkspace);
expect(mockActivateWorkspace.mock.invocationCallOrder[0]).toBeLessThan(
mockNavigate.mock.invocationCallOrder[0] ?? Number.POSITIVE_INFINITY
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Ordering assertion can pass when navigate never ran.

The ?? Number.POSITIVE_INFINITY fallback makes the comparison succeed if mockNavigate was never invoked, so a regression that drops navigation entirely would not fail here. Assert the navigation call explicitly before comparing invocation order.

💚 Proposed fix
     expect(mockActivateWorkspace).toHaveBeenCalledWith(sessionInOtherWorkspace);
+    expect(mockNavigate).toHaveBeenCalledTimes(1);
     expect(mockActivateWorkspace.mock.invocationCallOrder[0]).toBeLessThan(
-      mockNavigate.mock.invocationCallOrder[0] ?? Number.POSITIVE_INFINITY
+      mockNavigate.mock.invocationCallOrder[0]
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(mockActivateWorkspace).toHaveBeenCalledWith(sessionInOtherWorkspace);
expect(mockActivateWorkspace.mock.invocationCallOrder[0]).toBeLessThan(
mockNavigate.mock.invocationCallOrder[0] ?? Number.POSITIVE_INFINITY
);
expect(mockActivateWorkspace).toHaveBeenCalledWith(sessionInOtherWorkspace);
expect(mockNavigate).toHaveBeenCalledTimes(1);
expect(mockActivateWorkspace.mock.invocationCallOrder[0]).toBeLessThan(
mockNavigate.mock.invocationCallOrder[0]
);
🤖 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 `@web/src/systems/session/hooks/__tests__/use-session-create-dialog.test.tsx`
around lines 244 - 247, Update the test around mockActivateWorkspace and
mockNavigate to explicitly assert that mockNavigate was called before comparing
invocationCallOrder, then retain the ordering assertion to verify activation
precedes navigation without an invocation-order fallback.

@coderabbitai coderabbitai Bot mentioned this pull request Aug 20, 2026
1 of 3 tasks
This was referenced Sep 1, 2026
This was referenced Sep 27, 2026

This branch was successfully deployed

1 active deployment
Preview — 3cfaf793 Deployed Jul 31, 2026 by vercel[bot]
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