Skip to content

feat(agents): add provider-neutral orchestration - #5632

Open
megascan wants to merge 21 commits into
pingdotgg:mainfrom
megascan:agent/native-agent-orchestration
Open

feat(agents): add provider-neutral orchestration#5632
megascan wants to merge 21 commits into
pingdotgg:mainfrom
megascan:agent/native-agent-orchestration

Conversation

@megascan

@megascan megascan commented Aug 7, 2026

Copy link
Copy Markdown

What changed

This PR adds a provider-neutral Agents and Rules system to T3 Code. It is one end-to-end product feature spanning contracts, persistence, server orchestration, MCP, all five provider adapters, web/desktop, mobile, migrations, tests, and documentation.

Current size: 141 files, 18,461 additions, and 86 deletions. This exceeds automated approvability limits and requires careful human review; the passing correctness checks should not be mistaken for approval of a diff this large.

Suggested labels: enhancement, documentation, size:XXL, needs-triage, preview:web, and codex. The fork author does not have upstream triage permission, so GitHub only applied the labels available to its automation.

Agent profiles and Rules

  • Adds typed Agent profile and Rule contracts with environment/project scope and content-addressed revisions.
  • Profiles define instructions, preferred model, runtime and workspace policy, tools, hooks, delegation allowlists, provider requirements, and bounded budgets.
  • chatSelectable distinguishes direct-chat Agents from delegation-only specialists. Existing pinned threads retain their Agent even if it is later hidden from new chats.
  • Rules can always apply, match workspace-relative file globs, or attach explicitly to profiles.
  • Environment documents live in T3-owned state. Project documents are explicit t3.json references contained to the canonical project root; the catalog does not recursively scan repositories.
  • Saves use compare-and-swap revisions, atomic replacement, diagnostics, and reversible archive/restore rather than deletion.

Durable orchestration

  • Child work is represented as ordinary durable T3 threads with a pinned profile revision, not provider-native subagents.
  • Adds the event-sourced AgentRun domain, append-only events, transactional projections, immutable profile snapshots, lineage queries, revision waits, deadlines, usage, results, follow-ups, cancellation, and integration.
  • Adds migrations 41 and 42 for Agent run storage and the optional pinned Agent profile on thread projections, plus idempotent migration 43 for databases that recorded the earlier run projection before durable provider-turn binding.
  • Enforces lineage ceilings of depth 4, concurrency 8, 32 runs, and 120 minutes. Child budgets may reduce but cannot expand their parent budget, and descendants inherit the root wall-time origin rather than receiving a fresh deadline.
  • Wait subscribers attach before the durable revision read, avoiding polling and missed transitions.
  • Missing child-thread projections now fail closed with a typed error instead of returning an empty successful result.
  • Each run revision durably binds the canonical provider turn ID; stale completion, abort, and input events from an earlier follow-up cannot terminate or mutate the current turn.

T3-owned Agent tools

Providers receive the same portable MCP toolkit:

  • agent_list
  • agent_spawn
  • agent_status
  • agent_wait
  • agent_result
  • agent_send
  • agent_cancel
  • agent_integrate

Tools are scoped to the invoking T3 project, thread, lineage, and run ownership. Launch is asynchronous and returns a durable run ID; result reads are bounded and paginated.

Provider-neutral boundary

  • Codex, Claude, Cursor, Grok, and OpenCode declare explicit Agent runtime capabilities.
  • Provider-specific behavior stays at the adapter boundary. Catalog, persistence, orchestration, MCP handlers, and clients do not branch on provider type.
  • Compatibility is deny-by-default. Spawn is rejected when an adapter cannot honestly enforce a requested guarantee, such as exact native tool restriction, system instruction delivery, or usage accounting.
  • Future providers join the same orchestration path by declaring capabilities and supporting T3's existing MCP boundary.

Shared and isolated workspaces

  • Shared runs use the invoking workspace and obey profile write-concurrency policy.
  • Isolated runs use dedicated Git worktrees and require explicit integration.
  • Integration verifies canonical paths and Git common-directory identity, refuses dirty targets and untracked child files, generates a bounded tracked binary patch, preflights it with git apply --check --3way, and then applies it.
  • Conflicts remain visible rather than being resolved through guessed staging or merge decisions.

Web, desktop, mobile, and remote behavior

  • Adds Settings -> Agents for environment/project profiles, policies, Rules, diagnostics, and archive/restore.
  • Adds a searchable Agent picker beside the web/desktop model picker.
  • Projects native T3-managed AgentRun lifecycle into the existing parent-thread Agents panel, including model, profile ID, short run ID, follow-up turns, waiting, completion, failure, and cancellation. These activity rows are an explicitly best-effort view; durable AgentRun state remains authoritative.
  • Adds Agent selection to mobile new-task and existing-thread composer flows using native platform menus.
  • Explicit No agent selections are preserved rather than falling back to stale thread or draft state.
  • Profile and Rule editors validate id, scope, and revision before hydrating query results, preventing stale responses from overwriting another selection.
  • Typed WebSocket RPCs keep local, LAN, relay, and tunnel clients connected to the host-owned catalog and durable run state.

Why

ACP is a clean provider transport, but an ACP provider does not inherently know that it is running inside T3 and cannot reliably orchestrate another provider. Provider-native subagent systems also differ in naming, policy, lifetime, and availability.

This puts orchestration in the layer that has the required context: T3. A user can chat with an inexpensive coordinator, delegate architecture or implementation to specialized models, and keep every child run inspectable as an ordinary T3 thread without coupling core behavior to OpenGrok, GrokBuild, or any single provider.

UI evidence

Before: no Agent settings surface

Settings before Agents

After: first-party profile and Rule management

The selected specialist is marked delegation only. The host identifier is redacted from the public evidence image.

Settings with Agent profiles and direct-chat visibility

After: searchable picker beside the model picker

Search filters direct-chat profiles immediately. Delegation-only specialists remain available through orchestration but are absent from new top-level chat choices.

Searchable Agent picker

After: selected Agent shown in the composer

Selected Agent in the composer

After: native Agent run in the parent Agents panel

Native Agent run in the Agents panel

After: file-aware Rules

File-aware Rule editor

Short picker interaction recording

Evidence is published on a separate fork branch so binary review artifacts do not enter the product diff.

Verification

  • Original focused feature matrix: 185/185 tests passed.
  • First post-review regression matrix: 116/116 tests passed.
  • CodeRabbit response matrix: 105/105 tests passed.
  • Additional review-hardening matrices: 94/94, 57/57, 29/29, 43/43, and 29/29 passed.
  • Provider-capability regressions for OpenCode, Cursor, and Grok: 3/3 passed.
  • Final paginated review-response matrix covering mobile selection/settings, web mutation locking, and fail-closed result reads: 35/35 passed.
  • Native Agent panel projection, latest-main merge, and pinned mobile environment matrix: 104/104 focused server, client-runtime, migration, and mobile tests passed.
  • Provider-turn correlation review matrix: 55/55 focused AgentRun, repository, reactor, lifecycle, deadline, and migration tests passed.
  • Mobile post-save editor continuity matrix: 7/7 focused tests passed, plus the full mobile typecheck.
  • Latest lineage-deadline and migration-compatibility matrix: 58/58 focused AgentRun, repository, reactor, lifecycle, deadline, and migration tests passed.
  • Latest-main changed-test matrix: 318/320 passed. The two failures are unchanged Windows limitations: one POSIX path-separator assertion and one symlink test blocked by local EPERM.
  • Contracts, shared runtime, server, web, and mobile typechecks: passed.
  • Targeted lint and formatting: passed with zero diagnostics.
  • Web production build: passed.
  • Server executable bundle: passed.
  • git diff --check: passed.
  • Branch state: 21 commits ahead, 0 behind current upstream/main.
  • Macroscope correctness and Effect conventions: passed on 1062149af.
  • Macroscope and Cursor Bugbot findings through f445d85 are addressed in 502d128; the latest-head workflows are being monitored.
  • CodeRabbit: passed on 1062149af.
  • Review threads: 116/116 resolved.

Integrated browser coverage used an isolated .t3 environment and exercised:

  1. creating multiple environment Agent profiles and a file-aware Rule;
  2. preserving brace globs such as src/**/*.{ts,tsx} across save and reload;
  3. searching for and selecting a chat-selectable Agent beside the model picker;
  4. excluding delegation-only specialists from direct-chat search;
  5. applying preferred models when available and safely falling back when unavailable;
  6. archiving and restoring profiles and Rules;
  7. retaining an archived-but-pinned Agent label on an existing thread;
  8. opening a fresh draft without leaking the previous route's Agent selection;
  9. preserving a real Grok 4.5 turn and its selected profile across watcher restarts.

Honest scope and known limitations

  • This is a large architectural PR. Although it represents one product concern, it does not satisfy the repository preference for small contributions and is not eligible for automated approvability review.
  • Agents are T3-managed child threads, not wrappers around provider-native subagent APIs. Provider-native team/agent UIs are not surfaced here.
  • chatSelectable controls discovery, not authorization. Delegation policy and provider compatibility remain execution gates.
  • Web/desktop has text search; mobile currently uses the native platform menu without text search.
  • Token and monetary budgets can only be enforced when the provider adapter reports the required usage. Unsupported guarantees reject spawn rather than being approximated.
  • Isolated integration rejects untracked child files and dirty targets.
  • Profiles and Rules are environment-local or repository-referenced. There is no cloud marketplace, sync, or import/export workflow.
  • Catalog RPC responses cap profiles, Rules, and diagnostics at 100 each to bound WebSocket payloads.
  • Agents-panel activities are a best-effort projection of authoritative AgentRun state. A failed auxiliary append is logged and may temporarily omit or stale a row until a later lifecycle transition; it cannot roll back, fail, or strand the underlying run.
  • Mobile received focused tests and a full TypeScript check but was not exercised on a simulator or physical device.
  • No production-scale concurrency soak, relay/tunnel latency test, or screen-reader audit was performed.
  • The server typecheck emits existing non-failing Effect style suggestions; there are no type errors.
  • Complete Cursor/Grok adapter files hit existing Unix .sh mock-wrapper limitations on this Windows host. The complete OpenCode file hits the existing privileged-symlink EPERM limitation. The new provider assertions pass directly; Linux CI remains authoritative for those complete files.
  • Terminal hooks remain inline because afterResult participates in the durable success decision and provider events must stay ordered per thread. A future optimization requires a keyed bounded scheduler with explicit drain and shutdown semantics.
  • Agent MCP toolkit groups are discoverable by provider sessions, but authorization is enforced by selected profiles, delegation allowlists, project/thread lineage, and run ownership. t3McpCapabilities is compatibility metadata, not an ACL.
  • During an early contributor-machine startup, migration 40 was applied to the developer's live T3 home before startup failed. The process was stopped, no feature records were intentionally written there, and all subsequent runtime/UI testing used isolated state. This affected contributor-machine state only, not repository or production data.
  • Vercel's marketing deployment remains red because the fork requires Ping Labs deployment authorization; it is not a code failure.

Checklist

  • This PR is small and focused - it is one focused feature, but the end-to-end implementation is intentionally large.
  • I explained what changed and why.
  • I included before/after screenshots for the UI changes.
  • I included a short video for the picker interaction.
  • I documented validation gaps and external blockers honestly.

Implemented with GPT-5.6 Sol through the Codex harness in T3 Code.

Note

Add provider-neutral agent orchestration with profile/rule management and MCP tool surface

  • Introduces a full native agent orchestration system: AgentOrchestration service interface, AgentRunRepository for durable event storage, AgentRunReactor/AgentRunDeadlineReactor for state management and wall-time budget enforcement, and AgentPromptResolver for profile-aware prompt compilation.
  • Adds AgentProfileStore and AgentRuleStore for filesystem-backed, revision-checked persistence of agent profiles and rules in Markdown+YAML frontmatter format, coordinated via AgentProjectFileCoordinator to avoid races.
  • Exposes nine new WebSocket RPC endpoints (agents.catalog, agents.getProfile, agents.saveProfile, agents.archiveProfile, agents.restoreProfile, and rule equivalents) with scope-appropriate auth enforcement.
  • Registers a full suite of agent_* MCP tools (agent_spawn, agent_wait, agent_result, agent_send, agent_cancel, agent_integrate, etc.) gated on a new 'agents' MCP capability granted by default to all sessions.
  • Adds agent profile/rule settings UIs to both the web (/settings/agents) and mobile (SettingsAgents) apps, including catalog browsing, draft editing, save/archive/restore, and environment/project scoping.
  • Integrates agent profile selection into the chat composer on web and mobile; selecting a profile may auto-apply the profile's default model selection.
  • Adds three database migrations (041–043) creating agent_profile_snapshots, projection_agent_runs, agent_run_events tables and adding agent_profile_json to projection_threads.
  • Risk: MCP sessions now receive the 'agents' capability by default; existing credential checks for 'preview' are unaffected but all sessions gain access to agent tools without explicit opt-in.

Macroscope summarized 502d128.


Note

Medium Risk
Changes thread bootstrap and outbox schema (agent profile pinning) and adds large new settings/orchestration surfaces; incorrect revision or selection merging could mis-pin agents on turns, though logic is covered by focused tests.

Overview
Mobile adds an Agents entry under Settings (stack route agents) with a new screen to pick environment/project context, browse profile and rule catalogs, create and edit drafts, and save/archive/restore via existing agentEnvironment RPC atoms. Supporting modules (agentProfile.logic, agentRule.logic, agentSettings.logic) handle document building, revision-safe hydration, and optimistic selection after saves.

Composer flows now expose an Agent toolbar menu on new-task and thread composers. Choices are limited to chat-selectable profiles (with delegation-only profiles still visible when already pinned). Selecting a profile can apply its default model; AgentProfileRef is stored on composer drafts, queued outbox messages, and thread.turn.start / thread-creation bootstrap payloads, with explicit “No agent” preserved via resolveAgentProfileSelection.

Server (partial in this diff) introduces read-only AgentCatalog (environment Markdown dirs + explicit t3.json project refs, bounded RPC lists), AgentHookRunner for staged context/shell hooks, the AgentOrchestration service boundary, and orchestration lifecycle/integration test coverage; the orchestration integration harness stubs AgentPromptResolver when profiles are unused.

Reviewed by Cursor Bugbot for commit 502d128. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Added Agents settings on web and mobile for creating, editing, archiving, and restoring agent profiles and rules.
    • Added agent selection to chat and new-task composers, including profile-based model defaults.
    • Added agent orchestration for delegated runs, follow-ups, monitoring, cancellation, results, and integration.
    • Added support for reusable rules, hooks, workspace context, budgets, and provider capability checks.
  • Bug Fixes
    • Improved validation, revision-conflict handling, persistence, queued-task recovery, and diagnostics.
  • Documentation
    • Added user and internal documentation for agents, runs, profiles, and rules.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds native agent profiles, rules, prompt compilation, durable orchestration, provider compatibility checks, MCP and WebSocket APIs, web and mobile settings, chat selection, thread persistence, and supporting documentation.

Changes

Native agent platform

Layer / File(s) Summary
Contracts, catalog, and storage
packages/contracts/*, apps/server/src/agents/AgentCatalog.ts, apps/server/src/agents/*Store.ts, packages/client-runtime/src/state/agents.ts
Adds validated agent profile, rule, run, reference, RPC, catalog, and persistence contracts. Catalogs discover Markdown documents and stores save, archive, restore, revision, rollback, and project-reference updates.
Prompt preparation and durable runs
apps/server/src/agents/prompt/*, apps/server/src/agents/run/*, apps/server/src/agents/AgentHookRunner.ts
Adds path-safe rule matching, prompt compilation, hook execution, event-sourced run state, SQL persistence, deadlines, retries, budgets, follow-ups, cancellation, and integration state.
Orchestration, provider, and transport
apps/server/src/agents/AgentOrchestration*, apps/server/src/provider/*, apps/server/src/mcp/*, apps/server/src/ws.ts
Adds agent lifecycle orchestration, workspace integration, provider capability validation, MCP tools, authorization, WebSocket catalog and mutation operations, and server layer wiring.
Client profile selection and settings
apps/web/src/components/settings/*, apps/web/src/components/chat/*, apps/mobile/src/features/settings/*, apps/mobile/src/features/threads/*, apps/mobile/src/state/*
Adds web and mobile settings editors, profile and rule selection, draft and outbox persistence, thread propagation, default model handling, and navigation routes.
Documentation and runtime presentation
docs/*, packages/client-runtime/src/state/subagentRuntime.ts, apps/web/src/components/AgentsPanel.tsx
Documents agent profiles, rules, runs, safety constraints, provider requirements, and user workflows. Runtime subagent metadata includes profile IDs and cancelled status handling.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant WebSocket
  participant AgentCatalog
  participant AgentOrchestration
  participant Provider
  Client->>WebSocket: Select profile or invoke agent operation
  WebSocket->>AgentCatalog: List or load profile and rule documents
  WebSocket->>AgentOrchestration: Spawn, inspect, message, cancel, or integrate run
  AgentOrchestration->>Provider: Resolve prompt and start provider turn
  Provider-->>AgentOrchestration: Runtime events and usage
  AgentOrchestration-->>WebSocket: Run state or result
  WebSocket-->>Client: Catalog, mutation, or run response
Loading

Possibly related PRs

  • pingdotgg/t3code#5219: Adds related native agent profiles and orchestration data used by subagent observability features.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.25% 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
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.
Title check ✅ Passed The title clearly identifies the main change: adding provider-neutral agent orchestration.
Description check ✅ Passed The description explains the broad implementation, rationale, UI evidence, verification results, limitations, and checklist status.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 7, 2026

@macroscopeapp macroscopeapp 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.

Reviewed the new Effect services under apps/server/src/agents/**, their MCP/WS call sites, and the layer wiring. Findings are limited to Effect service conventions in the new code: standalone *Shape interfaces instead of inline Context.Service interfaces, error classes whose only payload is a free-form detail (with the underlying failure discarded rather than kept as cause), and a duplicated agent-services layer in the WebSocket route.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/agents/AgentOrchestration.ts Outdated
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/server/src/agents/AgentPromptResolver.ts Outdated
Comment thread apps/server/src/agents/run/AgentRunRepository.ts Outdated
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
Comment thread apps/server/src/agents/AgentRuleStore.ts Outdated
Comment thread apps/server/src/agents/AgentHookRunner.ts Outdated
Comment thread apps/server/src/agents/AgentHookRunner.ts Outdated
Comment thread apps/server/src/agents/AgentRuleStore.ts
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts
Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts
Comment thread apps/server/src/agents/AgentCatalog.ts Outdated
Comment thread apps/server/src/agents/AgentPromptResolver.ts Outdated
Comment thread apps/server/src/agents/AgentProfileStore.ts Outdated
Comment thread apps/server/src/agents/AgentCatalog.ts
Comment thread apps/server/src/agents/AgentHookRunner.ts Outdated
@megascan
megascan force-pushed the agent/native-agent-orchestration branch from 86d41ba to 091b57d Compare August 7, 2026 18:53
@megascan

megascan commented Aug 7, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (14)
apps/server/src/orchestration/Layers/ProjectionPipeline.ts-813-816 (1)

813-816: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Update the thread projection timestamp.

Line 815 changes agentProfile but leaves updatedAt unchanged. Clients can miss this state transition when they order or reconcile threads by updatedAt, especially if no later session event is emitted. Set updatedAt: event.occurredAt in this upsert.

🤖 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/Layers/ProjectionPipeline.ts` around lines 813
- 816, Update the projectionThreadRepository.upsert call in the projection event
handler to set updatedAt to event.occurredAt alongside agentProfile, preserving
the existing row fields and ensuring the thread timestamp reflects this state
transition.
docs/internals/glossary.md-95-97 (1)

95-97: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use standard adverb placement.

Change “applies always” to “always applies.”

🤖 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 `@docs/internals/glossary.md` around lines 95 - 97, Update the glossary’s Rule
definition to change the wording from “applies always” to “always applies,”
preserving the rest of the definition and its reference unchanged.

Source: Linters/SAST tools

apps/web/src/components/settings/AgentsSettings.logic.ts-128-132 (1)

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

Reject empty and blank input in parseInteger.

Number("") and Number(" ") return 0, and Number.isInteger(0) is true. If the user clears "Maximum runs" or "Maximum concurrency", the document is built with 0. The schema then rejects the document with a decode error instead of the field-specific message.

🛡️ Proposed fix
 function parseInteger(value: string, label: string): number {
+  if (value.trim().length === 0) throw new Error(`${label} is required.`);
   const parsed = Number(value);
   if (!Number.isInteger(parsed)) throw new Error(`${label} must be a whole number.`);
   return parsed;
 }
🤖 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/web/src/components/settings/AgentsSettings.logic.ts` around lines 128 -
132, Update parseInteger to explicitly reject empty or whitespace-only value
before converting it with Number, so cleared “Maximum runs” and “Maximum
concurrency” fields produce the existing label-specific whole-number error
instead of being interpreted as zero.
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx-718-730 (1)

718-730: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Reflect the off state in the "Always apply" toggle.

The toggle keeps bg-primary for both states, so the off state looks active. The profile toggle at lines 597-609 switches the background. Also add an accessibilityLabel, because the row label is a sibling element.

🎨 Proposed fix
         <Pressable
           accessibilityRole="switch"
+          accessibilityLabel="Always apply"
           accessibilityState={{ checked: props.draft.alwaysApply }}
           onPress={() => props.onChange("alwaysApply", !props.draft.alwaysApply)}
-          className="rounded-full bg-primary px-3 py-1"
+          className={`rounded-full px-3 py-1 ${props.draft.alwaysApply ? "bg-primary" : "bg-subtle-strong"}`}
         >
-          <Text className="text-sm font-t3-bold text-primary-foreground">
+          <Text
+            className={`text-sm font-t3-bold ${props.draft.alwaysApply ? "text-primary-foreground" : "text-foreground-muted"}`}
+          >
             {props.draft.alwaysApply ? "On" : "Off"}
           </Text>
         </Pressable>
🤖 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/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines
718 - 730, Update the Always apply toggle in the surrounding settings component
to use the inactive background styling when props.draft.alwaysApply is false,
matching the profile toggle’s state-dependent styling. Add an accessibilityLabel
to the Pressable so the control is identified independently of its sibling text
label.
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx-457-476 (1)

457-476: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add loading and error states to the rules list.

The profile list handles catalog.isPending and catalog.error. The rules list does not. During the first load the user sees "No rules yet." If the catalog request fails, the rules section shows the same empty message and hides the failure.

🩹 Proposed fix
           <View className="overflow-hidden rounded-2xl bg-subtle">
-            {rules.length === 0 ? (
+            {catalog.isPending && catalog.data === null ? (
+              <Text className="p-4 text-sm text-foreground-muted">Loading rules…</Text>
+            ) : null}
+            {catalog.error ? (
+              <Text accessibilityRole="alert" className="p-4 text-sm text-danger">
+                {catalog.error}
+              </Text>
+            ) : null}
+            {!catalog.isPending && !catalog.error && rules.length === 0 ? (
               <Text className="p-4 text-sm text-foreground-muted">
                 No rules yet. Create one to apply reusable instructions by path.
               </Text>
             ) : null}
🤖 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/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines
457 - 476, Update the rules list rendering around rules.length and rules.map to
handle the catalog loading and error states consistently with the profile list:
show a loading state while catalog.isPending, show the catalog error when
catalog.error is present, and only show “No rules yet” after a successful load
with no rules. Preserve the existing RuleRow rendering for loaded rules.
packages/contracts/src/agents.ts-445-452 (1)

445-452: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Rule failures report profile error messages.

The rule error aliases point at AgentProfileError. A missing or conflicting rule therefore produces AgentProfileNotFoundError with the text "Agent profile '/' was not found." That text reaches the Rules settings UI and MCP clients and names the wrong entity. Add rule-specific AgentRuleNotFoundError and AgentRuleRevisionConflictError variants, or parameterize the message with the document kind.

🤖 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 `@packages/contracts/src/agents.ts` around lines 445 - 452, Update the rule
error definitions near AgentRuleGetError, AgentRuleSaveError,
AgentRuleArchiveError, and AgentRuleRestoreError so rule failures no longer
alias AgentProfileError. Add rule-specific not-found and revision-conflict
variants, or parameterize the shared error with the rule document kind, ensuring
exposed messages identify a rule rather than an agent profile.
packages/client-runtime/src/state/agents.ts-37-72 (1)

37-72: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Add command-level caching after agent mutations.

The web and mobile settings screens call catalog.refresh() inline, so this library does not prevent the same mutation from showing stale catalog/profile/rule data from another atomQuery. Add registry.refresh(...) to onSuccess in these commands so consumers do not have to duplicate the same invalidation.

🤖 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 `@packages/client-runtime/src/state/agents.ts` around lines 37 - 72, Update the
agent mutation commands in the command registry to invalidate related catalog,
profile, and rule queries through registry.refresh(...) in each command’s
onSuccess handler. Apply this to saveProfile, archiveProfile, restoreProfile,
saveRule, archiveRule, and restoreRule, using the existing query identifiers and
preserving their current scheduler and concurrency configuration.
apps/server/src/agents/prompt/RuleMatcher.ts-116-116 (1)

116-116: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

escapeRegex does not escape *, so wildcards inside {...} become regex quantifiers.

Line 153 escapes each alternation branch with escapeRegex. The escape set at line 116 omits *. A glob such as {*.ts,*.tsx} therefore compiles to (?:\*?\.ts|...)-style output where * acts as a quantifier on the preceding character instead of matching a path segment. The rule then matches the wrong files, silently and with no diagnostic.

The single-character path at line 156 is not affected, because * and ? are handled by earlier branches. Only alternation contents reach escapeRegex as multi-character strings.

Either reject * and ? inside alternations with a thrown Error, which surfaces as an invalid-glob diagnostic, or compile each alternative through the same character loop.

This also relates to the static analysis hint on line 158. Glob text reaches new RegExp after this incomplete escaping. Rule globs are author-supplied and the candidate list is bounded to the context files, so the practical backtracking risk is low, but the escaping gap should still be closed.

Also applies to: 146-154

🤖 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/agents/prompt/RuleMatcher.ts` at line 116, The escapeRegex
function must escape `*` so wildcard characters inside alternation branches
cannot become regex quantifiers. Update its character class while preserving the
existing alternation compilation flow in the matcher around the branch handling
that calls `escapeRegex`.

Source: Linters/SAST tools

apps/server/src/agents/prompt/RuleMatcher.ts-238-259 (1)

238-259: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

contentBytes does not measure content.

The loop accumulates only rule.body bytes. The emitted content also contains a <!-- t3-agent-rule: scope/id -->\n header for each non-empty rule and a \n\n joiner between chunks. The returned contentBytes is therefore always lower than the byte length of content, and the 64 KiB cap does not bound what the prompt actually carries.

The loop also charges bytes for rules whose body is empty, which are then skipped at line 252 and contribute nothing to content.

Measure the chunk that is appended.

🐛 Proposed fix
   for (const rule of matched.rules) {
-    const bodyBytes = textEncoder.encode(rule.body).byteLength;
-    const nextBytes = contentBytes + bodyBytes;
+    if (rule.body.length === 0) continue;
+    const chunk = `<!-- t3-agent-rule: ${rule.scope}/${rule.id} -->\n${rule.body}`;
+    const separatorBytes = chunks.length === 0 ? 0 : 2;
+    const nextBytes = contentBytes + separatorBytes + textEncoder.encode(chunk).byteLength;
     if (nextBytes > maxBytes) {
       throw new AgentRuleContentOverflowError({
         limitBytes: maxBytes,
         actualBytes: nextBytes,
         ruleId: rule.id,
         scope: rule.scope,
       });
     }
     contentBytes = nextBytes;
-    if (rule.body.length > 0) {
-      chunks.push(`<!-- t3-agent-rule: ${rule.scope}/${rule.id} -->\n${rule.body}`);
-    }
+    chunks.push(chunk);
   }

Note that the test at apps/server/src/agents/prompt/prompt.test.ts line 94 uses maxBytes = 4 with a 5-byte body, so it still overflows after this change.

🤖 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/agents/prompt/RuleMatcher.ts` around lines 238 - 259, Update
the content assembly loop around matched.rules so byte accounting measures each
emitted chunk, including its rule header and newline, and skips empty bodies
before charging bytes. Use the encoded byte length of the exact chunk appended
to chunks, accumulate that value for contentBytes, and retain the overflow check
against maxBytes so the existing 5-byte body test still throws.
apps/server/src/ws.ts-1111-1127 (1)

1111-1127: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Return catalog diagnostics in agentsCatalog.

agentCatalog.list() is a success-only effect that collects malformed profile and rule entries in AgentCatalogSnapshot.diagnostics. agentsCatalog currently returns only profiles and rules, so a user cannot see why a catalog entry is missing.

🤖 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/ws.ts` around lines 1111 - 1127, Update the
WS_METHODS.agentsCatalog handler to include catalog.diagnostics in its returned
object alongside the filtered profiles and rules, preserving the existing
includeArchived filtering behavior.
apps/server/src/agents/run/AgentRunReactor.ts-47-53 (1)

47-53: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Log when the terminal hook is skipped.

If getProfileSnapshot returns None, or the workspace root cannot be resolved, Line 52 returns without any signal. The configured afterResult and onError hooks then never run, and the operator sees nothing. putProfileSnapshot should have persisted the snapshot at launch, so a missing snapshot indicates a real defect upstream. Emit a warning with run.id and run.profile.revision.

🤖 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/agents/run/AgentRunReactor.ts` around lines 47 - 53, Update
the early-return guard in the hook execution flow to emit a warning when profile
is null or workspaceRoot is null, including run.id and run.profile.revision in
the log; preserve the existing return behavior and do not alter hook execution
for valid values.
apps/server/src/agents/run/AgentRunDeadlineReactor.ts-26-33 (1)

26-33: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Guard against an unparsable timestamp, and correct the comment.

Two problems exist in this segment.

Date.parse returns NaN for a malformed timestamp. AgentRun.requestedAt and AgentRun.startedAt are plain string fields, so a corrupt persisted value produces NaN. isDeadlineExpired then always returns false, and schedule computes Math.max(0, NaN - nowMillis), which is NaN, and passes it to Duration.millis. The run then never reaches its wall-time budget.

The comment states the budget starts when a run is requested. The code prefers run.startedAt, so a run that stays queued receives a later deadline. Align the comment with the code.

🐛 Proposed guard
-/** The wall-time budget starts when a run is requested until it finishes. */
-export const deadlineAtMillis = (run: AgentRun): number => {
-  const origin = Date.parse(run.startedAt ?? run.requestedAt);
-  return origin + run.budget.maxWallTimeMinutes * 60_000;
-};
+/**
+ * The wall-time budget starts when a run starts, and falls back to the
+ * request time while the run is still queued.
+ */
+export const deadlineAtMillis = (run: AgentRun): number => {
+  const parsed = Date.parse(run.startedAt ?? run.requestedAt);
+  const origin = Number.isNaN(parsed) ? Date.parse(run.requestedAt) : parsed;
+  return (Number.isNaN(origin) ? 0 : origin) + run.budget.maxWallTimeMinutes * 60_000;
+};
🤖 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/agents/run/AgentRunDeadlineReactor.ts` around lines 26 - 33,
Update deadlineAtMillis to handle an unparsable run.startedAt or run.requestedAt
without returning NaN, preserving a valid deadline calculation for deadline
expiration and scheduling; use the existing AgentRun timestamp context and
choose an appropriate safe fallback for invalid persisted timestamps. Correct
the comment above deadlineAtMillis to state that the wall-time budget starts
from the timestamp selected by the implementation, including the startedAt
preference.
apps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.ts-4-16 (1)

4-16: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Add a migration test for agent_profile_json.

ProjectionThreadRepository reads and writes agent_profile_json through agentProfile in upsert, getById, and listByProjectId, but 040_ProjectionThreadsAgentProfile has no test cover. Add a migration test that runs up to migration 040 and asserts the column exists, following the existing migration test 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 `@apps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.ts`
around lines 4 - 16, Add a migration test following the existing migration test
pattern that runs migrations through 040_ProjectionThreadsAgentProfile and
verifies projection_threads contains the agent_profile_json column. Cover the
schema assertion only; do not alter ProjectionThreadRepository or migration
behavior.
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts-774-798 (1)

774-798: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Move getCapabilities into the profile branch.

resolvedPrompt.profile !== null is the only consumer of requestedCapabilities, so fetching it on every turn start adds an unneeded provider lookup. Calling it before ensureSessionForThread also runs capabilities lookup after unknown-instance failures, which can return provider capabilities errors instead of the descriptive unknown-instance errors from getInstanceInfo.

♻️ Proposed refactor
-    const requestedModelSelection =
-      input.modelSelection ?? threadModelSelections.get(input.threadId) ?? thread.modelSelection;
-    const requestedCapabilities = yield* providerService.getCapabilities(
-      requestedModelSelection.instanceId,
-    );
-    if (resolvedPrompt.profile !== null) {
-      const profile = resolvedPrompt.profile;
+    const requestedModelSelection =
+      input.modelSelection ?? threadModelSelections.get(input.threadId) ?? thread.modelSelection;
+    if (resolvedPrompt.profile !== null) {
+      const profile = resolvedPrompt.profile;
+      const requestedCapabilities = yield* providerService.getCapabilities(
+        requestedModelSelection.instanceId,
+      );
🤖 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/Layers/ProviderCommandReactor.ts` around lines
774 - 798, Move the providerService.getCapabilities call into the
resolvedPrompt.profile !== null branch, immediately before
resolveAgentRuntimeCompatibility, and keep requestedModelSelection available
there. Avoid fetching capabilities when no profile is present, while preserving
ensureSessionForThread/getInstanceInfo validation ordering so unknown instances
produce their existing descriptive errors first.
🤖 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/mobile/src/features/settings/agentProfile.logic.ts`:
- Around line 51-56: Extract a shared numeric parsing helper that rejects blank
or whitespace-only input before conversion, then apply range validation at each
caller. In apps/mobile/src/features/settings/agentProfile.logic.ts lines 51-56,
update integer so maxRuns and maxWallTimeMinutes reject blank values; in
apps/mobile/src/features/settings/agentRule.logic.ts lines 44-50, make
parseInteger reuse the shared helper instead of duplicating conversion and
integer checks. In apps/mobile/src/features/settings/agentProfile.logic.test.ts
lines 11-29, add negative cases asserting blank budget input and blank priority
cause buildAgentProfileDocument and buildAgentRuleDocument to throw.
- Around line 40-43: Update the runtimeMode default in draftFromProfile so a new
profile uses the least-permissive intended default, “auto,” instead of
“full-access”; leave the other profile defaults unchanged and preserve explicit
existing runtimeMode values.

In `@apps/mobile/src/features/settings/agentRule.logic.ts`:
- Around line 59-65: Update the mapping logic around the scope and id extraction
to split each colon-delimited target only at its first colon, preserving all
remaining text in id. Keep the environment default for targets without a colon,
then validate the preserved remainder with the existing scope and id checks so
malformed multi-colon targets are rejected rather than truncated.

In `@apps/mobile/src/state/use-thread-composer-state.ts`:
- Around line 110-113: Update the draft persistence and empty-draft handling
around updateComposerDraftSettings and isEmptyDraft so an explicit agentProfile:
null is preserved rather than causing the draft to be deleted. Ensure clearing a
thread-locked profile remains effective for the next turn while retaining
existing removal behavior for genuinely empty drafts.

In `@apps/server/src/agents/AgentCatalog.ts`:
- Around line 702-715: Update validate to reuse the single discovered source
collection returned by list/discovery instead of calling find, getProfile, or
getRule for each entry. Extract the post-find loading logic into
loadProfile(source) and loadRule(source), have getProfile and getRule call these
helpers after find, and have validate resolve each entry’s source from the
already discovered collection before invoking the corresponding loader.

In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 979-982: Update the follow-up turn construction in send to load
the pinned profile snapshot via runs.getProfileSnapshot(run.profile.revision),
then derive runtimeMode and interactionMode from that profile using the same
logic as spawn rather than hardcoding "full-access" and "default". Preserve the
profile’s approval requirements for every subsequent turn.

In `@apps/server/src/agents/AgentPromptResolver.ts`:
- Around line 24-45: Reject URI schemes in both normalizeCandidate in
apps/server/src/agents/AgentPromptResolver.ts lines 24-45 and
normalizeWorkspaceRelativePath in apps/server/src/agents/prompt/RuleMatcher.ts
lines 74-98 by adding the same scheme-prefix validation after the drive-letter
check; preserve existing rejection behavior and ensure values such as
https://example.com/x are rejected rather than normalized as relative paths.
Consider sharing the validator to prevent the rules from diverging.

In `@apps/server/src/agents/prompt/PromptCompiler.ts`:
- Around line 180-188: Update compileAgentPrompt and the compileAgentRules
integration to handle AgentRuleContentOverflowError without letting it escape as
a defect. Use isAgentRuleContentOverflowError to convert the overflow into the
established AgentPromptDiagnostic/result path, preserving the declared
AgentPromptCompilation and AgentPromptResolutionError flow for user-facing
handling.

In `@apps/server/src/agents/run/AgentRunDeadlineReactor.ts`:
- Around line 56-67: In apps/server/src/agents/run/AgentRunDeadlineReactor.ts
lines 56-67, update the cancellation handling in the deadline reactor to
distinguish Result failure from an empty successful event list: log the failure
details before returning false, while preserving the existing no-op behavior for
empty events. In apps/server/src/agents/run/AgentRunReactor.ts lines 47-53, add
a warning before the early return that includes run.id, run.profile.revision,
and the specific missing value.

In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 162-175: Update the event handling flow around handle and
runTerminalHook so terminal hooks execute on forked fibers or through a bounded
per-thread concurrent consumer instead of blocking Stream.runForEach’s single
consumer fiber. Preserve sequential event processing for each thread while
allowing hooks from different runs or threads to proceed independently, and
retain the existing error logging context.
- Around line 87-100: The budget-exhaustion branch in AgentRunReactor must stop
matching the human-readable detail text. Add a structured discriminator such as
reason to AgentRunCommandInvariantError, set it for budget-exhaustion failures,
and update the completion check to compare that discriminator while preserving
the existing dispatch behavior.

In `@apps/server/src/mcp/McpSessionRegistry.ts`:
- Line 131: Update McpSessionRegistry.issue() so the "agents" capability is
granted only when the thread’s attached target profile declares the
corresponding MCP tools, rather than unconditionally for every credential;
preserve "preview" access and ensure delegation uses the profile-specific
capability set.

In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts`:
- Around line 303-308: Update the thread.turn.start test setup around
resolveAgentPrompt and the dispatch at line 577 to provide a valid agentProfile
with a known profileRef, then assert the resolveAgentPrompt mock receives that
matching profileRef alongside the message. Ensure the test specifically
exercises the pinned-profile path rather than allowing a null profile reference.
- Around line 2917-2926: Replace the local waitFor polling block in the test
with yield* Effect.promise(() => harness.drain()) after the relevant dispatches.
Use harness.drain to wait for both reactor workers before reading the model,
while preserving the subsequent failure-activity assertions.

In `@apps/server/src/persistence/Migrations/039_AgentRuns.ts`:
- Around line 69-72: Update the migration near the existing
idx_projection_agent_runs_lineage definition to add a dedicated index on
projection_agent_runs(root_run_id), using an IF NOT EXISTS guard and a clear
root-run index name. Leave the existing parent_run_id, status, and created_at
index unchanged.

In `@apps/server/src/ws.ts`:
- Around line 2384-2391: Update websocketRpcRouteLayer and the server
route-layer wiring to construct one shared AgentProfileServices.layer per server
instance and provide that same layer to both HTTP routes and WebSocket RPC,
rather than extracting and rebuilding AgentCatalog, AgentProfileStore, and
AgentRuleStore inside websocketRpcRouteLayer. Ensure AgentRuleStore and
AgentProfileStore retain shared mutex and revision state across both transports.

In `@apps/web/src/components/settings/AgentsSettings.tsx`:
- Around line 506-518: Update the scope select in the AgentsSettings profile
form to use the existing-profile rule from RulesSettings: disable it when the
profile is not new by applying the equivalent isNew-based disabled condition,
while preserving the current value and change handling.

In `@apps/web/src/components/settings/RulesSettings.tsx`:
- Around line 149-158: Update the catch block in the rule restore flow to handle
caught values with the same instanceof Error check used near line 132, passing
plain Error instances directly to setError and using failureMessage only for
Cause values. Remove the unsafe Cause.Cause cast while preserving the existing
error-state behavior.
- Around line 108-113: Update save in RulesSettings so buildAgentRuleDocument
receives no baseline when creating a new rule: use the selectedKey/new-rule
state to pass null for new rules, matching the isNew guard in AgentsSettings,
and retain ruleQuery.data?.rule only when editing an existing selected rule.

---

Minor comments:
In `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 718-730: Update the Always apply toggle in the surrounding
settings component to use the inactive background styling when
props.draft.alwaysApply is false, matching the profile toggle’s state-dependent
styling. Add an accessibilityLabel to the Pressable so the control is identified
independently of its sibling text label.
- Around line 457-476: Update the rules list rendering around rules.length and
rules.map to handle the catalog loading and error states consistently with the
profile list: show a loading state while catalog.isPending, show the catalog
error when catalog.error is present, and only show “No rules yet” after a
successful load with no rules. Preserve the existing RuleRow rendering for
loaded rules.

In `@apps/server/src/agents/prompt/RuleMatcher.ts`:
- Line 116: The escapeRegex function must escape `*` so wildcard characters
inside alternation branches cannot become regex quantifiers. Update its
character class while preserving the existing alternation compilation flow in
the matcher around the branch handling that calls `escapeRegex`.
- Around line 238-259: Update the content assembly loop around matched.rules so
byte accounting measures each emitted chunk, including its rule header and
newline, and skips empty bodies before charging bytes. Use the encoded byte
length of the exact chunk appended to chunks, accumulate that value for
contentBytes, and retain the overflow check against maxBytes so the existing
5-byte body test still throws.

In `@apps/server/src/agents/run/AgentRunDeadlineReactor.ts`:
- Around line 26-33: Update deadlineAtMillis to handle an unparsable
run.startedAt or run.requestedAt without returning NaN, preserving a valid
deadline calculation for deadline expiration and scheduling; use the existing
AgentRun timestamp context and choose an appropriate safe fallback for invalid
persisted timestamps. Correct the comment above deadlineAtMillis to state that
the wall-time budget starts from the timestamp selected by the implementation,
including the startedAt preference.

In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 47-53: Update the early-return guard in the hook execution flow to
emit a warning when profile is null or workspaceRoot is null, including run.id
and run.profile.revision in the log; preserve the existing return behavior and
do not alter hook execution for valid values.

In `@apps/server/src/orchestration/Layers/ProjectionPipeline.ts`:
- Around line 813-816: Update the projectionThreadRepository.upsert call in the
projection event handler to set updatedAt to event.occurredAt alongside
agentProfile, preserving the existing row fields and ensuring the thread
timestamp reflects this state transition.

In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.ts`:
- Around line 774-798: Move the providerService.getCapabilities call into the
resolvedPrompt.profile !== null branch, immediately before
resolveAgentRuntimeCompatibility, and keep requestedModelSelection available
there. Avoid fetching capabilities when no profile is present, while preserving
ensureSessionForThread/getInstanceInfo validation ordering so unknown instances
produce their existing descriptive errors first.

In `@apps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.ts`:
- Around line 4-16: Add a migration test following the existing migration test
pattern that runs migrations through 040_ProjectionThreadsAgentProfile and
verifies projection_threads contains the agent_profile_json column. Cover the
schema assertion only; do not alter ProjectionThreadRepository or migration
behavior.

In `@apps/server/src/ws.ts`:
- Around line 1111-1127: Update the WS_METHODS.agentsCatalog handler to include
catalog.diagnostics in its returned object alongside the filtered profiles and
rules, preserving the existing includeArchived filtering behavior.

In `@apps/web/src/components/settings/AgentsSettings.logic.ts`:
- Around line 128-132: Update parseInteger to explicitly reject empty or
whitespace-only value before converting it with Number, so cleared “Maximum
runs” and “Maximum concurrency” fields produce the existing label-specific
whole-number error instead of being interpreted as zero.

In `@docs/internals/glossary.md`:
- Around line 95-97: Update the glossary’s Rule definition to change the wording
from “applies always” to “always applies,” preserving the rest of the definition
and its reference unchanged.

In `@packages/client-runtime/src/state/agents.ts`:
- Around line 37-72: Update the agent mutation commands in the command registry
to invalidate related catalog, profile, and rule queries through
registry.refresh(...) in each command’s onSuccess handler. Apply this to
saveProfile, archiveProfile, restoreProfile, saveRule, archiveRule, and
restoreRule, using the existing query identifiers and preserving their current
scheduler and concurrency configuration.

In `@packages/contracts/src/agents.ts`:
- Around line 445-452: Update the rule error definitions near AgentRuleGetError,
AgentRuleSaveError, AgentRuleArchiveError, and AgentRuleRestoreError so rule
failures no longer alias AgentProfileError. Add rule-specific not-found and
revision-conflict variants, or parameterize the shared error with the rule
document kind, ensuring exposed messages identify a rule rather than an agent
profile.

---

Nitpick comments:
In `@apps/mobile/src/state/thread-outbox.test.ts`:
- Around line 131-149: Extend the test around resolveQueuedThreadSettings to
cover both remaining agentProfile branches: assert that an explicit
agentProfile: null clears the thread profile, and assert that omitting the
agentProfile property preserves the existing thread profile. Keep the current
queued-profile round-trip assertion unchanged so all three hasOwnProperty-based
behaviors are verified.

In `@apps/mobile/src/state/use-thread-composer-state.test.ts`:
- Around line 1-4: Rename the test file from use-thread-composer-state.test.ts
to agentProfileSelection.test.ts so its filename matches the
resolveAgentProfileSelection module under test.

In `@apps/server/src/agents/AgentCatalog.test.ts`:
- Around line 33-298: Add a focused test in the AgentCatalog suite that calls
the public validate method with one valid profile and one Markdown document
lacking frontmatter, then assert validation aggregates the expected
missing-frontmatter diagnostic alongside the valid document results. Exercise
both discovery/list diagnostics and per-document loading through validate,
without using arbitrary timeouts.

In `@apps/server/src/agents/AgentCatalog.ts`:
- Around line 193-200: Introduce a scope-neutral AgentLocator (or
AgentRuleLocator) alias in packages/contracts and use it for rule references,
including getRule and RuleFrontmatter.profiles where applicable, while retaining
AgentProfileLocator for profile references. Rename decodeProfileLocator at the
rule-decoding call sites around lines 493 and 825 to reflect that it decodes
rule identifiers, and update all related imports and usages.

In `@apps/server/src/agents/AgentHookRunner.ts`:
- Around line 113-127: Update the POSIX invocation in the shell-hook runner to
use /bin/sh with only the -c argument, removing the login-shell behavior while
preserving the existing command, working directory, timeout, and output
handling.
- Around line 15-45: Extract the identical field definitions from
AgentHookBlockedError and AgentHookExecutionError into a shared schema field-set
constant, then reuse it when declaring both TaggedErrorClass schemas. Keep both
error tags and their existing message getters distinct and unchanged.

In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 699-709: In the isolated-worktree branch of the prepareThread
setup, capture the already-validated context.thread.branch in a local constant
immediately after its guard, then use that constant for refName and baseRefName
instead of non-null assertions. Preserve the existing branch validation and
worktree creation behavior.

In `@apps/server/src/agents/AgentProfileStore.test.ts`:
- Around line 172-173: Update the test around projectFile in AgentProfileStore
tests to parse t3.json as JSON and inspect its decoded agents array. Assert that
exactly one agent has id "project-reviewer", replacing the raw project-reviewer
substring count while preserving the existing file-read flow.
- Around line 93-104: The compare-and-swap failure paths are untested. In
apps/server/src/agents/AgentProfileStore.test.ts:93-104, add a second save using
the already-consumed saved.revision and assert
AgentProfileStoreRevisionConflictError; in
apps/server/src/agents/AgentRuleStore.test.ts:62-75, add the equivalent
stale-revision save and assert the rule store’s typed revision-conflict error.
Also add, in either test file, coverage for supplying expectedRevision when the
profile or rule does not exist, asserting the appropriate failure.

In `@apps/server/src/agents/AgentProfileStore.ts`:
- Around line 388-397: Resolve the `documentPath` behavior for new environment
profiles in the surrounding profile creation flow: either honor
`input.profile.sourcePath` consistently, or validate and reject conflicting
values while retaining `defaultPath`. If always using the environment default is
intentional, add a concise comment documenting that rule; preserve
existing-project behavior.

In `@apps/server/src/agents/AgentPromptResolver.test.ts`:
- Around line 87-89: Strengthen the test assertion in the resolver test so it
verifies the compiled prelude appears before the user-supplied marker, using the
ordering of “## T3 runtime” and malicious in resolved.message. Keep the existing
assertions unchanged; do not address duplicate markers here, as that belongs to
PromptCompiler.

In `@apps/server/src/agents/AgentPromptResolver.ts`:
- Around line 183-199: Update the Effect.forEach call that loads snapshot.rules
in resolve to use bounded concurrency greater than one, preserving the existing
resolutionError mapping and result ordering. Do not alter rule lookup behavior;
only configure concurrency to avoid sequential catalog.getRule calls.
- Around line 156-166: Extract the child-run turn command ID construction into a
shared helper, then reuse it in AgentOrchestrationLive when sending
thread.turn.start and in AgentPromptResolver.isCompiledAgentTurn when comparing
commandId. Ensure the helper consistently formats the agent-spawn prefix and run
ID.

In `@apps/server/src/agents/AgentRuleStore.test.ts`:
- Around line 137-138: Update the assertion in the t3.json test to decode
projectFile as JSON, inspect its rules array, and assert exactly one entry whose
id is "project-typescript"; remove the substring-count assertion so the test
validates structured data rather than JSON text layout.

In `@apps/server/src/agents/AgentRuleStore.ts`:
- Around line 365-372: Update the documentPath selection in the relevant
AgentRuleStore function to use Result.isSuccess(current), matching the existing
check earlier in the same function, while preserving the current success and
fallback sourcePath behavior.

In `@apps/server/src/agents/prompt/prompt.test.ts`:
- Around line 72-88: The matching tests around matchAgentRules need coverage for
advanced and malformed glob patterns. Add focused cases for alternation and
character-class patterns, including src/**/*.{ts,tsx} matching src/a.tsx and
{*.ts,*.tsx} matching the appropriate files, plus an unclosed src/[ts pattern
that yields an invalid-glob diagnostic and never matches.

In `@apps/server/src/agents/prompt/PromptCompiler.ts`:
- Around line 83-84: Remove the redundant compatibility aliases from
PromptCompiler and RuleMatcher: delete portablePromptEnvelope, compilePrompt,
matchRules, compileRules, and RuleContentOverflowError exports or properties,
retaining only the canonical names and updating index re-exports and internal
references accordingly.
- Around line 144-148: Document in renderPortablePrompt that envelope.task is
intentionally preserved verbatim and may contain duplicate prompt markers or
section headings, so future consumers must not treat them as trust signals;
update the existing AgentPromptResolver test expectation only if needed to
preserve this documented behavior.

In `@apps/server/src/agents/run/AgentRun.test.ts`:
- Around line 165-292: Update the test title for “enforces inherited depth,
run-count, concurrency, and token budgets” to also identify the estimated-cost
budget assertion, so all five covered budget dimensions are visible when the
test fails.
- Around line 294-405: Add tests in the AgentRun transition/decision coverage
for both missing branches: advance the command occurredAt beyond the configured
maxWallTimeMinutes and assert the relevant succeed or follow-up decision is
rejected, then request a child run with maxRuns or maxTotalTokens greater than
its parent and assert budgetDoesNotExpand rejects it. Reuse the existing
request, start, transition, decide, and fixture helpers and preserve current
assertions.

In `@apps/server/src/agents/run/AgentRun.ts`:
- Around line 365-475: Update the second event switch in evolve to add a default
branch that passes the event to a never assertion, making any newly added
AgentRunEvent variant a compile-time error while preserving all existing cases.

In `@apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts`:
- Around line 90-165: Add focused coverage for the make scheduling flow: use a
fake change stream and TestClock to verify layer startup recovers an active run
from listActive and schedules expiration, then emits a completed-status change
and verifies cancelScheduled removes the timer. Exercise the behavior
deterministically without arbitrary timeouts, anchoring the test around make,
listActive recovery, change-event handling, and TestClock.
- Around line 67-85: Replace the trailing `as AgentRunRepository["Service"]`
cast in `repositoryFor` with a `satisfies AgentRunRepository["Service"]` check
on the object literal, preserving the existing inferred object type while
ensuring missing or incompatible service members fail typechecking.

In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 22-26: Import the exported AgentRun type from ./AgentRun.ts and
use it directly for the run parameter in both hookWorkspace and the other helper
around lines 41–45. Remove the duplicated
NonNullable<Effect.Success<ReturnType<typeof repository.get>>…> type derivation
while preserving the existing helper behavior.

In `@apps/server/src/agents/run/AgentRunRepository.test.ts`:
- Around line 66-206: Extend the AgentRunRepository test suite with focused
coverage for putProfileSnapshot and getProfileSnapshot, including the persisted
profile data used by recovery or hook execution. Add assertions for
getByChildThread and listActive, including expected child-thread lookup and
active-run filtering. Keep the tests isolated within
testLayer("AgentRunRepository", ...) and use the existing migration, repository,
and fixture helpers.

In `@apps/server/src/agents/run/AgentRunRepository.ts`:
- Around line 159-163: The unfiltered SQL branch in the event retrieval method
should not remain available. Make the where/filter parameter required and remove
the fallback query that selects all events, updating the method signature and
callers as needed while preserving the existing filtered queries.
- Around line 218-236: Update the AgentRunRepository projection and migration
039 so result_json is populated from the run’s result data rather than a
constant null, and consumedEstimatedCostUsd is stored alongside consumedTokens.
Ensure the INSERT column/value lists and conflict-update clause consistently
include both fields, using the existing serialization pattern for result data
and the AgentRun property for cost.

In `@apps/server/src/mcp/McpHttpServer.ts`:
- Around line 217-227: Rename ToolkitRegistrations to a preview-specific name
that reflects its use by PreviewToolkitRegistrationLive. Wrap
AgentToolkitHandlersLive and AgentToolkit with
McpToolkit.makeMcpToolkitRegistration using the agents capability, then expose
AgentToolkitRegistrationLive from that registration so it follows the same
capability-aware path as the preview toolkits.

In `@apps/server/src/orchestration/agentProfile.test.ts`:
- Around line 17-30: Update the event helper so it is generic over a specific
OrchestrationEvent member, deriving the type and payload parameters from that
member instead of accepting an arbitrary type and unknown payload. Construct the
shared envelope with the generic event type and retain only any necessary cast
for fields whose union narrowing requires it, ensuring each event call validates
its payload against the selected event type.
- Around line 61-72: Add a second turn-start test case alongside the existing
null-agentProfile case, using a non-null agentProfile in
thread.turn-start-requested and asserting that the projected thread preserves
that exact value. Keep the existing null case unchanged so both clearing and
applying event values are verified.

In `@apps/server/src/orchestration/projector.ts`:
- Around line 547-563: Update the "thread.turn-start-requested" handler so that
when payload.agentProfile is undefined it returns nextBase unchanged, avoiding
the updateThread call; retain the existing agentProfile patch through
updateThread when the value is present.

In `@apps/server/src/persistence/Migrations/039_AgentRuns.test.ts`:
- Around line 48-59: Replace the name-based index assertions in the migration
test with behavioral checks: insert duplicate agent_run_events rows sharing the
same agent_run_id and revision, and duplicate projection_agent_runs rows sharing
child_thread_id, asserting both inserts fail. Preserve any setup required by the
schema and ensure the event constraint test covers the duplicate-revision
behavior used by AgentRunRepository.dispatch.

In `@apps/server/src/persistence/Migrations/039_AgentRuns.ts`:
- Around line 74-82: Remove the redundant CREATE INDEX statements for
idx_agent_run_events_run_revision and idx_projection_agent_runs_child_thread
from the migration, relying on the existing UNIQUE constraints. Update the
corresponding assertions in the migration test to no longer expect either index
name.

In `@apps/server/src/provider/AgentRuntimeCompatibility.test.ts`:
- Around line 29-46: Add coverage in AgentRuntimeCompatibility tests for the
delegation path in resolveAgentRuntimeCompatibility, ensuring the portable
fixture’s mcpServerInjection capability does not mask it. Add assertions for
both mcp-server-injection-unsupported and token-accounting-unsupported while
preserving the existing unsupported-issue expectations.

In `@apps/server/src/ws.ts`:
- Around line 470-482: Replace the derived ref type in mapAgentCatalogError with
AgentProfileLocator, and add AgentProfileLocator to the existing
`@t3tools/contracts` import. Preserve the current error mapping behavior and ref
values.

In `@apps/web/src/components/chat/ChatComposer.tsx`:
- Around line 680-682: Update the selectedAgentProfile useState declaration in
ChatComposer to remove the explicit AgentProfileRef | null generic parameter and
rely on the lazy initializer’s inferred type, preserving the existing initial
value and state behavior.

In `@apps/web/src/components/settings/AgentsSettings.logic.test.ts`:
- Around line 24-44: Update the revision assertion in the “preserves a revision
and parses structured policy fields” test to compare document.revision with the
exact baseline revision, "a".repeat(64), instead of only validating its
hexadecimal format. Keep the existing structured-field assertions unchanged.

In `@apps/web/src/components/settings/AgentsSettings.logic.ts`:
- Around line 177-204: In the configuration-building logic, compute each
optional numeric value once before constructing the result object, then reuse it
for both the undefined check and assigned property. Update the handling around
sharedWriteConcurrency, maxTotalTokens, and maxEstimatedCostUsd while preserving
omission of undefined fields and existing parse behavior.

In `@apps/web/src/components/settings/RulesSettings.logic.ts`:
- Around line 74-89: Extract the shared archived/scope/name/id comparator into
one generic catalog sort helper, then re-export that helper under both
sortAgentRules and sortAgentProfiles. Update the existing sortAgentRules
implementation and the corresponding sortAgentProfiles implementation to use the
shared symbol while preserving their current signatures and ordering.

In `@apps/web/src/components/settings/settingsSearch.ts`:
- Around line 171-175: Add a separate search entry titled “Rules” alongside the
existing “Agents” entry in the settings search configuration, using the same
/settings/agents route so RulesSettingsPanel is discoverable when users search
for “rules”.

In `@apps/web/src/routeTree.gen.ts`:
- Around line 100-104: Remove the `as any` cast from the
`SettingsAgentsRouteImport.update` call in the generated route definition by
regenerating it with typed output. If the generator cannot avoid the cast, add a
narrowly scoped lint exception for this generated route file and document the
generated-code justification.

In `@packages/contracts/src/agents.ts`:
- Around line 351-382: Introduce or reuse a dedicated AgentRuleId brand from
agentRefs.ts, then update AgentRuleSummary.id and all rule operation
identifiers, including AgentRuleGetInput.id, AgentRuleArchiveInput.id, and the
id carried by AgentRuleDocument used in AgentRuleSaveInput, to use AgentRuleId
consistently instead of AgentProfileId or AgentSlug.
- Around line 674-746: Remove the compatibility alias export blocks for
AgentMcpStart/Get/Submit, AgentMcpAgent*, and McpAgentRun* so each codec has one
canonical exported name. Update any call sites referencing those aliases to use
the corresponding canonical
AgentMcpList/Spawn/Status/Wait/Result/Send/Cancel/Integrate symbols.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 121fbec2-7b16-4a78-9116-a9cc89a98d48

📥 Commits

Reviewing files that changed from the base of the PR and between 72d673a and 091b57d.

📒 Files selected for processing (121)
  • apps/mobile/src/Stack.tsx
  • apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
  • apps/mobile/src/features/settings/SettingsRouteScreen.tsx
  • apps/mobile/src/features/settings/agentProfile.logic.test.ts
  • apps/mobile/src/features/settings/agentProfile.logic.ts
  • apps/mobile/src/features/settings/agentRule.logic.test.ts
  • apps/mobile/src/features/settings/agentRule.logic.ts
  • apps/mobile/src/features/settings/components/settings-sheet-targets.ts
  • apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
  • apps/mobile/src/features/threads/ThreadComposer.tsx
  • apps/mobile/src/features/threads/ThreadDetailScreen.tsx
  • apps/mobile/src/features/threads/ThreadRouteScreen.tsx
  • apps/mobile/src/features/threads/new-task-flow-provider.tsx
  • apps/mobile/src/features/threads/use-project-actions.ts
  • apps/mobile/src/lib/projectThreadStartTurn.ts
  • apps/mobile/src/state/agentProfileSelection.ts
  • apps/mobile/src/state/agents.ts
  • apps/mobile/src/state/thread-outbox-model.ts
  • apps/mobile/src/state/thread-outbox.test.ts
  • apps/mobile/src/state/use-composer-drafts.ts
  • apps/mobile/src/state/use-thread-composer-state.test.ts
  • apps/mobile/src/state/use-thread-composer-state.ts
  • apps/mobile/src/state/use-thread-outbox-drain.ts
  • apps/server/integration/OrchestrationEngineHarness.integration.ts
  • apps/server/src/agents/AgentCatalog.test.ts
  • apps/server/src/agents/AgentCatalog.ts
  • apps/server/src/agents/AgentHookRunner.test.ts
  • apps/server/src/agents/AgentHookRunner.ts
  • apps/server/src/agents/AgentOrchestration.ts
  • apps/server/src/agents/AgentOrchestrationLive.test.ts
  • apps/server/src/agents/AgentOrchestrationLive.ts
  • apps/server/src/agents/AgentProfileServices.ts
  • apps/server/src/agents/AgentProfileStore.test.ts
  • apps/server/src/agents/AgentProfileStore.ts
  • apps/server/src/agents/AgentPromptResolver.test.ts
  • apps/server/src/agents/AgentPromptResolver.ts
  • apps/server/src/agents/AgentRuleStore.test.ts
  • apps/server/src/agents/AgentRuleStore.ts
  • apps/server/src/agents/prompt/PromptCompiler.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • apps/server/src/agents/prompt/index.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/server/src/agents/run/AgentRun.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/agents/run/AgentRunReactor.ts
  • apps/server/src/agents/run/AgentRunRepository.test.ts
  • apps/server/src/agents/run/AgentRunRepository.ts
  • apps/server/src/auth/RpcAuthorization.ts
  • apps/server/src/mcp/McpHttpServer.test.ts
  • apps/server/src/mcp/McpHttpServer.ts
  • apps/server/src/mcp/McpInvocationContext.test.ts
  • apps/server/src/mcp/McpInvocationContext.ts
  • apps/server/src/mcp/McpSessionRegistry.test.ts
  • apps/server/src/mcp/McpSessionRegistry.ts
  • apps/server/src/mcp/McpToolkit.ts
  • apps/server/src/mcp/toolkits/agents/handlers.ts
  • apps/server/src/mcp/toolkits/agents/tools.ts
  • apps/server/src/orchestration/Layers/ProjectionPipeline.ts
  • apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • apps/server/src/orchestration/agentProfile.test.ts
  • apps/server/src/orchestration/decider.ts
  • apps/server/src/orchestration/projector.ts
  • apps/server/src/persistence/Layers/ProjectionThreads.ts
  • apps/server/src/persistence/Migrations.ts
  • apps/server/src/persistence/Migrations/039_AgentRuns.test.ts
  • apps/server/src/persistence/Migrations/039_AgentRuns.ts
  • apps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.ts
  • apps/server/src/persistence/Services/ProjectionThreads.ts
  • apps/server/src/provider/AgentRuntimeCompatibility.test.ts
  • apps/server/src/provider/AgentRuntimeCompatibility.ts
  • apps/server/src/provider/Layers/ClaudeAdapter.ts
  • apps/server/src/provider/Layers/CodexAdapter.ts
  • apps/server/src/provider/Layers/CursorAdapter.ts
  • apps/server/src/provider/Layers/GrokAdapter.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.ts
  • apps/server/src/provider/Services/ProviderAdapter.ts
  • apps/server/src/server.test.ts
  • apps/server/src/server.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/ChatView.logic.test.ts
  • apps/web/src/components/ChatView.logic.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/chat/AgentProfilePicker.logic.ts
  • apps/web/src/components/chat/AgentProfilePicker.test.ts
  • apps/web/src/components/chat/AgentProfilePicker.tsx
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/web/src/components/settings/AgentsSettings.logic.test.ts
  • apps/web/src/components/settings/AgentsSettings.logic.ts
  • apps/web/src/components/settings/AgentsSettings.test.tsx
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/web/src/components/settings/RulesSettings.logic.test.ts
  • apps/web/src/components/settings/RulesSettings.logic.ts
  • apps/web/src/components/settings/RulesSettings.test.tsx
  • apps/web/src/components/settings/RulesSettings.tsx
  • apps/web/src/components/settings/SettingsSidebarNav.tsx
  • apps/web/src/components/settings/settingsSearch.ts
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/settings.agents.tsx
  • apps/web/src/state/agents.ts
  • docs/internals/agents.md
  • docs/internals/glossary.md
  • docs/user/agents.md
  • packages/client-runtime/package.json
  • packages/client-runtime/src/state/agents.ts
  • packages/contracts/src/agentRefs.test.ts
  • packages/contracts/src/agentRefs.ts
  • packages/contracts/src/agents.test.ts
  • packages/contracts/src/agents.ts
  • packages/contracts/src/index.ts
  • packages/contracts/src/orchestration.ts
  • packages/contracts/src/providerRuntime.ts
  • packages/contracts/src/rpc.ts
  • packages/contracts/src/t3ProjectFile.test.ts
  • packages/contracts/src/t3ProjectFile.ts
  • packages/shared/package.json
  • packages/shared/src/agentRuleGlobs.test.ts
  • packages/shared/src/agentRuleGlobs.ts

Comment thread apps/mobile/src/features/settings/agentProfile.logic.ts Outdated
Comment thread apps/mobile/src/features/settings/agentProfile.logic.ts
Comment thread apps/mobile/src/features/settings/agentRule.logic.ts
Comment thread apps/mobile/src/state/use-thread-composer-state.ts
Comment thread apps/server/src/agents/AgentCatalog.ts
Comment thread apps/server/src/persistence/Migrations/041_AgentRuns.ts
Comment thread apps/server/src/ws.ts
Comment thread apps/web/src/components/settings/AgentsSettings.tsx
Comment thread apps/web/src/components/settings/RulesSettings.tsx Outdated
Comment thread apps/web/src/components/settings/RulesSettings.tsx
@megascan
megascan force-pushed the agent/native-agent-orchestration branch from 091b57d to 4195a0b Compare August 7, 2026 19:19
@megascan

megascan commented Aug 7, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@megascan
megascan force-pushed the agent/native-agent-orchestration branch from 4195a0b to 2ec33ac Compare August 7, 2026 19:42
Comment thread apps/mobile/src/features/settings/agentProfile.logic.ts
@megascan

megascan commented Aug 7, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@macroscopeapp macroscopeapp 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.

Follow-up on the earlier findings: the standalone *Shape interfaces, the AgentRuleStore message getters, and the duplicated AgentProfileServices.layer in ws.ts are all resolved, and the hook/prompt/orchestration errors now carry structural attributes plus a real cause. One retained issue: several pure validation failures now manufacture an Error (or default one) purely to satisfy a cause field that is required, which the conventions call out explicitly. Three inline notes below.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
Comment thread apps/server/src/agents/AgentHookRunner.ts Outdated
Comment thread apps/server/src/agents/AgentPromptResolver.ts Outdated
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
Comment thread apps/server/src/agents/AgentHookRunner.ts Outdated
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
Comment thread apps/server/src/agents/AgentPromptResolver.ts Outdated
Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx Outdated
Comment thread apps/server/src/agents/run/AgentRunReactor.ts
Comment thread apps/server/src/agents/run/AgentRun.ts
Comment thread apps/server/src/agents/run/AgentRunDeadlineReactor.ts Outdated

@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: 6

🧹 Nitpick comments (2)
apps/server/src/agents/prompt/RuleMatcher.ts (1)

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

Remove redundant return type annotations.

TypeScript infers these return types from the implementations. Keep annotations where they define required parameter or public data shapes.

As per coding guidelines, “Prefer inferred types over explicit annotations and do not use any.”

Also applies to: 33-33, 74-74, 101-101, 117-117, 120-120, 163-166, 173-176, 184-184

🤖 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/agents/prompt/RuleMatcher.ts` at line 21, Remove redundant
explicit return type annotations from the identified getters and methods in
RuleMatcher, including get message and the additional referenced locations,
while preserving annotations that define required parameter or public data
shapes. Keep the implementations and inferred return behavior unchanged, and do
not introduce any.

Source: Coding guidelines

apps/server/src/agents/AgentOrchestrationLive.ts (1)

822-831: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Replace Option.getOrThrow with a typed failure.

Line 830 converts a missing run into an unhandled defect. Every other failure in spawn maps into AgentProfileInvalidError. An MCP caller receives a typed error in all other paths and an untyped defect here.

♻️ Proposed refactor
       const run = yield* runs.get(runId).pipe(
         Effect.mapError((cause) =>
           invalid("Could not reload the Agent run.", {
             operation: "run-reload",
             cause,
             runId,
           }),
         ),
-        Effect.map(Option.getOrThrow),
+        Effect.flatMap(
+          Option.match({
+            onNone: () =>
+              Effect.fail(
+                invalid("The Agent run disappeared after it started.", {
+                  operation: "run-reload",
+                  runId,
+                }),
+              ),
+            onSome: Effect.succeed,
+          }),
+        ),
       );
🤖 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/agents/AgentOrchestrationLive.ts` around lines 822 - 831,
Update the run reload flow in spawn around runs.get and Option.getOrThrow so a
missing run produces the same typed AgentProfileInvalidError path as other
failures instead of an unhandled defect. Map the empty Option to invalid with
the existing run-reload operation context and runId, while preserving the
current successful run value and runs.get error mapping.
🤖 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/mobile/src/features/settings/agentSettings.logic.ts`:
- Around line 1-5: Update parseRequiredNumber to parse the trimmed input once,
reject results that are not finite—including NaN—alongside the existing
required-value validation, and remove its explicit number return type so
TypeScript infers it.

In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 693-718: Update failSpawn to clean up isolated Git worktrees
before deleting the child thread when target.workspace.mode is
"isolated-worktree". Track successful createWorktree completion in prepareThread
and only invoke the worktree-removal operation from failSpawn when that creation
succeeded, preserving existing failure dispatch behavior.
- Around line 584-607: Update the agent-run.request handling around the visible
compileAgentPrompt flow to apply the same lineage token and estimated-cost
budget checks already used for follow-up handling. Validate the requested child
run before it is spawned, reject over-budget requests through the existing error
path, and preserve the current depth, run-count, and concurrency checks.
- Around line 52-58: Update the AgentProfileInvalidError construction to omit
the cause property when context?.cause is undefined, using the same
conditional-spread pattern as profileId and runId; preserve the existing cause
value whenever one is provided.

In `@apps/server/src/agents/prompt/RuleMatcher.ts`:
- Around line 241-259: Update the rule-content accumulation in the loop over
matched.rules to measure the serialized output rather than only rule.body:
construct each non-empty rule’s header/body chunk, include its "\n\n" separator
as emitted by chunks.join("\n\n"), and check the resulting byte count against
maxBytes before appending. Preserve empty-rule behavior and report the
serialized size in AgentRuleContentOverflowError.
- Around line 120-160: Replace the backtracking RegExp construction in globRegex
with a linear-time glob-matching implementation, ensuring patterns containing
repeated or overlapping wildcards such as **a cannot cause exponential work when
tested against a non-matching path. Preserve the existing glob semantics for *,
**, ?, character classes, and alternations, and update callers to use the safe
matcher instead of expression.test.

---

Nitpick comments:
In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 822-831: Update the run reload flow in spawn around runs.get and
Option.getOrThrow so a missing run produces the same typed
AgentProfileInvalidError path as other failures instead of an unhandled defect.
Map the empty Option to invalid with the existing run-reload operation context
and runId, while preserving the current successful run value and runs.get error
mapping.

In `@apps/server/src/agents/prompt/RuleMatcher.ts`:
- Line 21: Remove redundant explicit return type annotations from the identified
getters and methods in RuleMatcher, including get message and the additional
referenced locations, while preserving annotations that define required
parameter or public data shapes. Keep the implementations and inferred return
behavior unchanged, and do not introduce any.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5daa31b4-9fa3-4d8e-96b2-d0b2d7a434ce

📥 Commits

Reviewing files that changed from the base of the PR and between 4195a0b and 2ec33ac.

📒 Files selected for processing (31)
  • apps/mobile/src/features/settings/agentProfile.logic.test.ts
  • apps/mobile/src/features/settings/agentProfile.logic.ts
  • apps/mobile/src/features/settings/agentRule.logic.test.ts
  • apps/mobile/src/features/settings/agentRule.logic.ts
  • apps/mobile/src/features/settings/agentSettings.logic.ts
  • apps/mobile/src/state/use-composer-drafts.test.ts
  • apps/server/src/agents/AgentCatalog.test.ts
  • apps/server/src/agents/AgentCatalog.ts
  • apps/server/src/agents/AgentOrchestrationLive.test.ts
  • apps/server/src/agents/AgentOrchestrationLive.ts
  • apps/server/src/agents/AgentPromptResolver.test.ts
  • apps/server/src/agents/AgentPromptResolver.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/server/src/agents/run/AgentRun.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/agents/run/AgentRunReactor.ts
  • apps/server/src/mcp/McpSessionRegistry.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • apps/server/src/persistence/Migrations/039_AgentRuns.test.ts
  • apps/server/src/persistence/Migrations/039_AgentRuns.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/settings/AgentsSettings.logic.test.ts
  • apps/web/src/components/settings/AgentsSettings.logic.ts
  • apps/web/src/components/settings/AgentsSettings.test.tsx
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/web/src/components/settings/RulesSettings.logic.test.ts
  • apps/web/src/components/settings/RulesSettings.logic.ts
  • apps/web/src/components/settings/RulesSettings.tsx
  • docs/user/agents.md
🚧 Files skipped from review as they are similar to previous changes (24)
  • apps/mobile/src/features/settings/agentProfile.logic.test.ts
  • apps/web/src/components/settings/AgentsSettings.logic.test.ts
  • apps/server/src/persistence/Migrations/039_AgentRuns.test.ts
  • apps/web/src/components/settings/RulesSettings.logic.test.ts
  • apps/server/src/agents/AgentPromptResolver.test.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/mobile/src/features/settings/agentRule.logic.test.ts
  • apps/web/src/components/settings/AgentsSettings.logic.ts
  • apps/server/src/mcp/McpSessionRegistry.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/ws.ts
  • docs/user/agents.md
  • apps/web/src/components/settings/RulesSettings.tsx
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • apps/mobile/src/features/settings/agentProfile.logic.ts
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/mobile/src/features/settings/agentRule.logic.ts
  • apps/server/src/agents/AgentCatalog.test.ts
  • apps/server/src/persistence/Migrations/039_AgentRuns.ts
  • apps/web/src/components/settings/RulesSettings.logic.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/server/src/agents/run/AgentRun.ts
  • apps/server/src/agents/AgentPromptResolver.ts
  • apps/server/src/agents/run/AgentRunReactor.ts

Comment thread apps/mobile/src/features/settings/agentSettings.logic.ts Outdated
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts
Comment thread apps/server/src/agents/prompt/RuleMatcher.ts Outdated
Comment thread apps/server/src/agents/prompt/RuleMatcher.ts

@macroscopeapp macroscopeapp 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.

One finding on error attribute safety in the new Agent orchestration code. Earlier rounds' findings (standalone *Shape interfaces, missing message getters, invented cause values, duplicated AgentProfileServices layer) look addressed.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
@megascan

megascan commented Aug 7, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread apps/server/src/agents/AgentOrchestrationLive.ts
Comment thread apps/web/src/components/settings/RulesSettings.tsx
Comment thread apps/server/src/agents/run/AgentRunReactor.ts
Comment thread packages/contracts/src/agents.ts
Comment thread apps/server/src/agents/AgentOrchestrationLive.ts Outdated
Comment thread apps/server/src/ws.ts Outdated

@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.

Caution

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

⚠️ Outside diff range comments (1)
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx (1)

257-295: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add failure handling to the archive and restore handlers.

saveRuleDocument and save wrap their awaited command in try/catch/finally and show an error message. archiveRestoreRule and archiveRestore use try/finally only. If command(...) rejects instead of returning a failure result, the rejection escapes through () => void archiveRestoreRule(), the user sees no message, and an unhandled promise rejection is produced.

Add a catch branch that sets the corresponding error state.

🛠️ Proposed fix for `archiveRestoreRule`
       setRuleNotice(selectedRuleSummary.archivedAt ? "Rule restored." : "Rule archived.");
       catalog.refresh();
+    } catch (caught) {
+      setRuleError(caught instanceof Error ? caught.message : "The rule could not be updated.");
     } finally {
       ruleCommandInFlight.current = false;
       setRuleCommandPending(false);
     }

Also applies to: 349-387

🤖 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/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines
257 - 295, Update archiveRestoreRule and the corresponding archiveRestore
handler to add catch branches around their awaited command calls, setting the
appropriate rule error state when a rejection occurs. Preserve the existing
early returns, success handling, and finally blocks so command-in-flight state
is always cleared.
🧹 Nitpick comments (2)
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx (1)

43-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share diagnosticLabel instead of copying it.

The same helper exists in apps/web/src/components/settings/AgentsSettings.tsx and apps/web/src/components/settings/RulesSettings.tsx. Three copies of one formatting rule will drift. Move the helper into shared client code and import it in all three screens.

As per coding guidelines, "shared logic belongs in packages/client-runtime when appropriate."

🤖 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/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines
43 - 44, Move the diagnosticLabel helper into shared client code under
packages/client-runtime, then import and reuse it in SettingsAgentsRouteScreen,
AgentsSettings, and RulesSettings. Remove each local duplicate while preserving
the existing diagnostic formatting behavior.

Source: Coding guidelines

apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts (1)

672-678: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the user-visible failure for the rejected profile.

The test verifies that no prompt resolution, session start, or turn send occurs. It does not verify what the user sees. Add a read-model assertion after harness.drain() that the thread records the incompatibility failure. That protects against a silent-drop regression where the turn is rejected without any activity.

As per coding guidelines, "Backend behavior changes must include focused tests for that 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/Layers/ProviderCommandReactor.test.ts` around
lines 672 - 678, Extend the test after harness.drain() to read the thread’s
activity/read model and assert it records the user-visible incompatibility
failure for the rejected profile. Keep the existing no-op assertions for
resolveAgentPrompt, startSession, and sendTurn, and use the test harness’s
established read-model accessors and failure representation.

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.

Outside diff comments:
In `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 257-295: Update archiveRestoreRule and the corresponding
archiveRestore handler to add catch branches around their awaited command calls,
setting the appropriate rule error state when a rejection occurs. Preserve the
existing early returns, success handling, and finally blocks so
command-in-flight state is always cleared.

---

Nitpick comments:
In `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 43-44: Move the diagnosticLabel helper into shared client code
under packages/client-runtime, then import and reuse it in
SettingsAgentsRouteScreen, AgentsSettings, and RulesSettings. Remove each local
duplicate while preserving the existing diagnostic formatting behavior.

In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts`:
- Around line 672-678: Extend the test after harness.drain() to read the
thread’s activity/read model and assert it records the user-visible
incompatibility failure for the rejected profile. Keep the existing no-op
assertions for resolveAgentPrompt, startSession, and sendTurn, and use the test
harness’s established read-model accessors and failure representation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7370eb5b-bfc2-4cbe-9684-93259c1edb44

📥 Commits

Reviewing files that changed from the base of the PR and between 2ec33ac and 372874b.

📒 Files selected for processing (24)
  • apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
  • apps/server/integration/OrchestrationEngineHarness.integration.ts
  • apps/server/src/agents/AgentCatalog.ts
  • apps/server/src/agents/AgentHookRunner.test.ts
  • apps/server/src/agents/AgentHookRunner.ts
  • apps/server/src/agents/AgentOrchestrationLive.test.ts
  • apps/server/src/agents/AgentOrchestrationLive.ts
  • apps/server/src/agents/AgentPromptResolver.test.ts
  • apps/server/src/agents/AgentPromptResolver.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/server/src/agents/run/AgentRun.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/agents/run/AgentRunReactor.test.ts
  • apps/server/src/agents/run/AgentRunReactor.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/web/src/components/settings/RulesSettings.tsx
  • packages/contracts/src/agents.test.ts
  • packages/contracts/src/agents.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • apps/server/integration/OrchestrationEngineHarness.integration.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/server/src/agents/AgentHookRunner.test.ts
  • apps/server/src/ws.ts
  • apps/server/src/agents/run/AgentRun.ts
  • apps/server/src/agents/AgentHookRunner.ts
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/server/src/agents/AgentPromptResolver.ts
  • apps/web/src/components/settings/RulesSettings.tsx
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • packages/contracts/src/agents.ts

Comment thread apps/server/src/agents/AgentOrchestrationLive.ts
Comment thread apps/server/src/agents/run/AgentRun.ts Outdated
Comment thread apps/server/src/agents/run/AgentRunReactor.ts Outdated
Comment thread apps/server/src/ws.ts
Comment thread apps/server/src/agents/prompt/RuleMatcher.ts
@megascan

megascan commented Aug 7, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 4

🤖 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/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 569-574: Update the rules catalog rendering near the rules.length
check in SettingsAgentsRouteScreen to handle loading and error states before
showing the empty-catalog message. Mirror the existing profile catalog guards
around lines 509-521, using the rules catalog’s loading and error state symbols,
and preserve “No rules yet” only for a successfully loaded empty result.
- Around line 839-851: Update the Always apply Pressable to use an off-state
background when props.draft.alwaysApply is false, while preserving the primary
background when enabled. Add an accessibilityLabel identifying it as the “Always
apply” switch, matching the accessible labeling used by the nearby Show in chat
Agent picker switch.
- Around line 287-320: Add a catch handler to archiveRestoreRule, and likewise
to archiveRestore, so rejected command promises set an appropriate user-facing
rule error while preserving the existing context-key guard. Keep the finally
blocks responsible for clearing ruleCommandInFlight and ruleCommandPending,
ensuring void archiveRestoreRule() and the corresponding archiveRestore call do
not produce unhandled rejections.

In `@apps/web/src/components/settings/RulesSettings.tsx`:
- Around line 127-182: In the RulesSettings mutation flow, add shared isMutating
state used by both save and archiveRestore, set it before either command starts,
and clear it in a finally block so it resets on success or failure. Pass
isMutating to RuleEditor and disable both Save and Archive/Restore actions while
it is true, preventing concurrent submissions with stale expectedRevision
values.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 778bef4d-4da2-4296-a024-f1fdc2fefd7c

📥 Commits

Reviewing files that changed from the base of the PR and between 372874b and c2bc3bf.

📒 Files selected for processing (52)
  • apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
  • apps/mobile/src/features/settings/agentProfile.logic.test.ts
  • apps/mobile/src/features/settings/agentProfile.logic.ts
  • apps/mobile/src/features/settings/agentRule.logic.test.ts
  • apps/mobile/src/features/settings/agentRule.logic.ts
  • apps/mobile/src/features/settings/agentSettings.logic.test.ts
  • apps/mobile/src/features/settings/agentSettings.logic.ts
  • apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
  • apps/mobile/src/features/threads/ThreadComposer.tsx
  • apps/server/src/agents/AgentCatalog.test.ts
  • apps/server/src/agents/AgentCatalog.ts
  • apps/server/src/agents/AgentOrchestrationLive.test.ts
  • apps/server/src/agents/AgentOrchestrationLive.ts
  • apps/server/src/agents/AgentProfileServices.ts
  • apps/server/src/agents/AgentProfileStore.test.ts
  • apps/server/src/agents/AgentProfileStore.ts
  • apps/server/src/agents/AgentProjectFileCoordinator.ts
  • apps/server/src/agents/AgentRuleStore.test.ts
  • apps/server/src/agents/AgentRuleStore.ts
  • apps/server/src/agents/AgentStoreErrorMapping.test.ts
  • apps/server/src/agents/AgentStoreErrorMapping.ts
  • apps/server/src/agents/AgentWorkspaceRoot.test.ts
  • apps/server/src/agents/AgentWorkspaceRoot.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/server/src/agents/run/AgentRun.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/agents/run/AgentRunReactor.test.ts
  • apps/server/src/agents/run/AgentRunReactor.ts
  • apps/server/src/provider/Layers/CursorAdapter.test.ts
  • apps/server/src/provider/Layers/CursorAdapter.ts
  • apps/server/src/provider/Layers/GrokAdapter.test.ts
  • apps/server/src/provider/Layers/GrokAdapter.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/chat/AgentProfilePicker.logic.ts
  • apps/web/src/components/chat/AgentProfilePicker.test.ts
  • apps/web/src/components/chat/AgentProfilePicker.tsx
  • apps/web/src/components/chat/ChatComposer.logic.test.ts
  • apps/web/src/components/chat/ChatComposer.logic.ts
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/web/src/components/settings/AgentsSettings.logic.test.ts
  • apps/web/src/components/settings/AgentsSettings.logic.ts
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/web/src/components/settings/RulesSettings.logic.test.ts
  • apps/web/src/components/settings/RulesSettings.logic.ts
  • apps/web/src/components/settings/RulesSettings.test.tsx
  • apps/web/src/components/settings/RulesSettings.tsx
  • packages/contracts/src/agents.ts
🚧 Files skipped from review as they are similar to previous changes (41)
  • apps/server/src/agents/AgentWorkspaceRoot.test.ts
  • apps/server/src/agents/AgentStoreErrorMapping.test.ts
  • apps/server/src/provider/Layers/CursorAdapter.test.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
  • apps/server/src/provider/Layers/CursorAdapter.ts
  • apps/mobile/src/features/settings/agentRule.logic.test.ts
  • apps/web/src/components/chat/ChatComposer.logic.test.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
  • apps/server/src/agents/AgentWorkspaceRoot.ts
  • apps/server/src/agents/AgentProfileStore.test.ts
  • apps/server/src/provider/Layers/GrokAdapter.test.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
  • apps/server/src/agents/AgentCatalog.test.ts
  • apps/mobile/src/features/settings/agentRule.logic.ts
  • apps/server/src/agents/AgentStoreErrorMapping.ts
  • apps/web/src/components/settings/RulesSettings.test.tsx
  • apps/server/src/provider/Layers/GrokAdapter.ts
  • apps/server/src/agents/AgentProjectFileCoordinator.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/mobile/src/features/threads/ThreadComposer.tsx
  • apps/server/src/provider/Layers/OpenCodeAdapter.ts
  • apps/web/src/components/chat/AgentProfilePicker.logic.ts
  • apps/server/src/agents/AgentProfileStore.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/settings/RulesSettings.logic.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • apps/server/src/agents/AgentRuleStore.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/mobile/src/features/settings/agentProfile.logic.ts
  • apps/server/src/agents/AgentRuleStore.test.ts
  • apps/web/src/components/chat/AgentProfilePicker.test.ts
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/server/src/agents/run/AgentRun.ts
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/server/src/agents/AgentProfileServices.ts
  • packages/contracts/src/agents.ts
  • apps/web/src/components/chat/AgentProfilePicker.tsx
  • apps/server/src/agents/AgentOrchestrationLive.test.ts
  • apps/server/src/agents/run/AgentRunReactor.ts
  • apps/server/src/agents/AgentOrchestrationLive.ts

Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
Comment thread apps/web/src/components/settings/RulesSettings.tsx
@megascan
megascan marked this pull request as ready for review August 7, 2026 22:15
Comment thread apps/mobile/src/features/threads/NewTaskDraftScreen.tsx Outdated
Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
@LordMerc

Copy link
Copy Markdown

Opened megascan#1 with the Agents panel bridge plus model, profile, and run identity display. It targets the contributor branch directly, so merging it will update this PR. The stacked PR includes before/after UI evidence and focused validation.

Comment thread apps/server/src/agents/AgentOrchestrationLive.ts
…orchestration

# Conflicts:
#	apps/mobile/src/Stack.tsx
#	apps/mobile/src/features/settings/components/settings-sheet-targets.ts
#	apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
#	apps/mobile/src/features/threads/ThreadComposer.tsx
#	apps/mobile/src/features/threads/new-task-flow-provider.tsx
#	apps/server/src/persistence/Migrations.ts
#	apps/server/src/ws.ts
#	apps/web/src/routeTree.gen.ts
Comment thread apps/server/src/ws.ts
Comment thread apps/server/src/agents/run/AgentRunReactor.ts
Comment thread apps/server/src/provider/Layers/ClaudeAdapter.ts
Comment thread apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
Comment thread apps/server/src/agents/run/AgentRun.ts
Comment thread apps/server/src/persistence/Migrations/041_AgentRuns.ts
@megascan

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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 (17)
apps/server/src/ws.ts (2)

484-496: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import the ref type instead of deriving it from Parameters<...>.

Lines 486-491 rebuild the profile-ref shape through Parameters<AgentCatalog.AgentCatalog["Service"]["getProfile"]>[0]["ref"]. The contracts package already exports the profile reference type used by getProfile. A direct import states the intent and survives a signature change to getProfile.

🤖 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/ws.ts` around lines 484 - 496, Update mapAgentCatalogError to
use the contracts package’s exported profile reference type for ref instead of
deriving its id and scope through
Parameters<AgentCatalog.AgentCatalog["Service"]["getProfile"]>. Import and apply
that existing type while preserving the current error mapping behavior.

456-478: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Preserve the underlying cause when project lookup fails.

The Effect.mapError at lines 471-477 converts every non-AgentProfileInvalidError failure into AgentProfileInvalidError with the text Could not resolve project '<id>'.. A projection read failure is an infrastructure error, not invalid input. The original cause is discarded, so the server logs keep no record of why the read failed. Log the cause before mapping.

♻️ Proposed change
               Effect.mapError((error) =>
                 isAgentProfileInvalidError(error)
                   ? error
                   : new AgentProfileInvalidError({
                       detail: `Could not resolve project '${projectId}'.`,
                     }),
               ),
+              Effect.tapError((error) =>
+                isAgentProfileInvalidError(error)
+                  ? Effect.void
+                  : Effect.logWarning("agent workspace root resolution failed", {
+                      projectId,
+                      error,
+                    }),
+              ),

Place the tapError before the mapError so it observes the original failure.

🤖 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/ws.ts` around lines 456 - 478, Update agentWorkspaceRoot to
add an Effect.tapError before the existing Effect.mapError, logging the original
project lookup failure and its cause before non-AgentProfileInvalidError values
are wrapped. Preserve the current AgentProfileInvalidError mapping and
successful workspaceRoot behavior.
apps/server/src/persistence/Migrations/041_AgentRuns.ts (1)

61-61: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Two indexes duplicate implicit indexes created by UNIQUE constraints.

SQLite creates an implicit index for every UNIQUE constraint.

  • Line 61 declares UNIQUE (agent_run_id, revision) on agent_run_events. The index at lines 80-83 uses the same columns in the same order, so it adds no new access path. It doubles index write cost on the event-append path, which runs on every dispatch.
  • Line 26 declares child_thread_id TEXT UNIQUE. The index at lines 85-88 duplicates that implicit index.

Drop both explicit indexes and update the assertions in apps/server/src/persistence/Migrations/041_AgentRuns.test.ts at lines 56 and 61.

♻️ Proposed removal
-  yield* sql`
-    CREATE INDEX IF NOT EXISTS idx_agent_run_events_run_revision
-    ON agent_run_events(agent_run_id, revision)
-  `;
-
-  yield* sql`
-    CREATE INDEX IF NOT EXISTS idx_projection_agent_runs_child_thread
-    ON projection_agent_runs(child_thread_id)
-  `;

Also applies to: 80-88

🤖 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/persistence/Migrations/041_AgentRuns.ts` at line 61, Remove
the explicit indexes duplicating the UNIQUE constraints: the agent_run_events
index on (agent_run_id, revision) and the child_thread_id index. Update the
corresponding assertions in AgentRuns migration tests to expect both indexes to
be absent, while preserving all other migration indexes and constraints.
apps/server/src/persistence/Migrations/041_AgentRuns.test.ts (1)

31-47: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the full column set used by the repository upsert.

AgentRunRepository inserts into projection_agent_runs with 28 columns, including consumed_tokens, detached, integration_target_thread_id, last_error, workspace_mode, and project_id. The test asserts only 13 of them. A future migration edit that drops one of the unasserted columns breaks the repository at runtime, and this regression test stays green. Add the remaining columns to the expected list.

🤖 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/persistence/Migrations/041_AgentRuns.test.ts` around lines 31
- 47, Expand the expected column list in the migration test’s columnNames
assertion to include every column inserted by AgentRunRepository’s
projection_agent_runs upsert, including consumed_tokens, detached,
integration_target_thread_id, last_error, workspace_mode, project_id, and all
other omitted columns, so the test covers the complete 28-column contract.
apps/server/src/agents/AgentRuleStore.test.ts (1)

139-146: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider asserting the decoded t3.json reference instead of a substring count.

The assertion counts project-typescript occurrences and expects exactly 2. That count depends on the id also appearing inside the default path. A change to the default path layout breaks this test with an unclear failure. Decode t3.json and assert one rules entry with the expected id and path.

🤖 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/agents/AgentRuleStore.test.ts` around lines 139 - 146,
Replace the substring-count assertion on projectFile in the AgentRuleStore test
with a decoded t3.json assertion. Parse the JSON and verify that the rules
collection contains exactly one entry with id project-typescript and the
expected path, while preserving the existing generated markdown content
assertion.
apps/server/src/agents/AgentRuleStore.ts (1)

404-411: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use Result.isSuccess for consistency.

Line 409 inspects current._tag directly. Lines 386 and 437 use the Result predicates. Use Result.isSuccess(current) here so the file uses one style, and so the narrowing stays valid if the Result representation 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 `@apps/server/src/agents/AgentRuleStore.ts` around lines 404 - 411, Update the
documentPath selection in the surrounding rule-loading method to use
Result.isSuccess(current) instead of checking current._tag directly, while
preserving the existing sourcePath fallback behavior for both success and
non-success results.
apps/server/src/agents/run/AgentRunReactor.ts (1)

235-293: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated failure dispatch.

completeSuccessfulRun builds the same agent-run.fail command and the same return shape three times, at Lines 240-252, 259-271, and 280-292. Extract one local helper that takes the failure detail and returns the failed result. This removes the risk of the three copies drifting apart.

♻️ Proposed refactor
+    const failRun = (failure: string) =>
+      retryDurable(
+        input.repository.dispatch({
+          type: "agent-run.fail",
+          runId: input.run.id,
+          failure,
+          ...(input.usage === undefined ? {} : { usage: input.usage }),
+          occurredAt: input.occurredAt,
+        }),
+      ).pipe(
+        Effect.map((failed) => ({
+          status: "failed" as const,
+          revision: revisionAfterAgentRunTransition(input.run, failed),
+        })),
+      );
🤖 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/agents/run/AgentRunReactor.ts` around lines 235 - 293, In
completeSuccessfulRun, extract the repeated agent-run.fail dispatch and
failed-status/revision construction into one local helper accepting the failure
detail. Replace the budget-exhausted preflight path, afterResult hook failure
path, and completion budget-exhausted path with calls to that helper, preserving
usage, occurredAt, retryDurable, and revisionAfterAgentRunTransition behavior.
apps/server/src/agents/run/AgentRunReactor.test.ts (1)

235-248: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant continued flag.

The assertion on continued cannot fail. If appendAgentRunTaskActivity propagated the interruption, the test fiber would already be interrupted. Drop the flag and let the successful completion of the effect be the assertion, or assert an observable effect such as a follow-up dispatch.

🤖 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/agents/run/AgentRunReactor.test.ts` around lines 235 - 248,
Remove the redundant continued variable and its assertion from the test around
appendAgentRunTaskActivity. Let successful completion of the Effect.gen block
verify that the interruption was not propagated, without adding unrelated
assertions or behavior.
apps/server/src/agents/AgentCatalog.ts (1)

732-832: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated AgentCatalogDocumentError construction.

The same eight-line Effect.mapError((cause) => new AgentCatalogDocumentError({...})) block appears eight times across profileSummary, ruleSummary, profileDocument, ruleDocument, revisionOf, and readSource. Only kind and code vary. A small helper reduces the repetition and keeps the diagnostic fields consistent when the error shape changes.

♻️ Sketch: shared error mapper
+  const documentError =
+    (source: Source, code: "invalid-document" | "missing-frontmatter" | "read-failed") =>
+    (cause: unknown) =>
+      new AgentCatalogDocumentError({
+        kind: source.kind,
+        scope: source.ref.scope,
+        id: source.ref.id,
+        sourcePath: source.sourcePath,
+        code,
+        cause,
+      });

Each site then becomes Effect.mapError(documentError(source, "invalid-document")). source.kind already carries "profile" or "rule", so the hardcoded kind literals are redundant.

🤖 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/agents/AgentCatalog.ts` around lines 732 - 832, Extract the
repeated AgentCatalogDocumentError construction into a shared documentError
helper that accepts the Source and error code, derives kind from source.kind,
and preserves scope, id, sourcePath, and cause. Replace the Effect.mapError
callbacks in profileSummary, ruleSummary, profileDocument, ruleDocument,
revisionOf, and readSource with this helper, passing "invalid-document" or the
existing code as appropriate.
apps/mobile/src/state/agentProfileSelection.test.ts (1)

6-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the explicit-selection branch of the tri-state resolver.

resolveAgentProfileSelection accepts three draft states: null, undefined, and an explicit selection. The tests cover null and undefined only. The explicit-selection branch is the path a user takes when picking an agent in the composer, and it is untested.

💚 Proposed test
+  it("returns an explicit draft selection over the thread selection", () => {
+    const draft = {
+      id: AgentProfileId.make("chosen"),
+      scope: "environment" as const,
+      revision: AgentProfileRevision.make("b".repeat(64)),
+    };
+    const fallback = {
+      id: AgentProfileId.make("fallback"),
+      scope: "environment" as const,
+      revision: AgentProfileRevision.make("a".repeat(64)),
+    };
+    expect(resolveAgentProfileSelection(draft, fallback)).toEqual(draft);
+    expect(resolveAgentProfileSelection(undefined, null)).toBeNull();
+  });
🤖 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/mobile/src/state/agentProfileSelection.test.ts` around lines 6 - 25, Add
a test in the “agent profile selection” suite for resolveAgentProfileSelection
that passes an explicit agent profile selection and verifies the same selection
is returned unchanged, covering the composer’s user-selected branch alongside
the existing null and undefined cases.
apps/mobile/src/features/settings/agentSettings.logic.ts (1)

14-21: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

The context key delimiter is not escaped.

agentSettingsContextKey joins four values with :. selectionKey already contains a colon, because a selection key is built as ${scope}:${id}. If any component contains a colon, two different inputs can produce the same key, and a superseded save then passes the context-key guard in SettingsAgentsRouteScreen.

Serialize the tuple instead of concatenating. Also drop the explicit string return type.

♻️ Proposed change
 export function agentSettingsContextKey(input: {
   readonly environmentId: string | null;
   readonly projectId: string | null;
   readonly selectionKey: string | null;
   readonly generation: number;
-}): string {
-  return `${input.environmentId ?? ""}:${input.projectId ?? ""}:${input.selectionKey ?? ""}:${input.generation}`;
+}) {
+  return JSON.stringify([
+    input.environmentId,
+    input.projectId,
+    input.selectionKey,
+    input.generation,
+  ]);
 }

As per coding guidelines: "Prefer inferred types over explicit annotations and do not use any."

🤖 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/mobile/src/features/settings/agentSettings.logic.ts` around lines 14 -
21, Update agentSettingsContextKey to serialize the four input values as an
unambiguous tuple rather than concatenating them with colons, preserving null
values distinctly so embedded delimiters cannot collide. Remove the explicit
string return type and let TypeScript infer it.

Source: Coding guidelines

apps/server/src/agents/AgentProfileStore.ts (1)

374-380: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Preserve the existing entry position in t3.json.

The filter-then-append pattern moves a re-saved profile to the end of the agents array. t3.json is a checked-in file, so every save of an existing profile produces an ordering diff.

Replace the entry in place when it already exists, and append only for a new id.

♻️ Proposed change
-          const agents = [
-            ...(current.agents ?? []).filter((entry) => entry.id !== input.ref.id),
-            {
-              id: input.ref.id,
-              path: input.documentPath,
-            },
-          ];
+          const existing = current.agents ?? [];
+          const entry = { id: input.ref.id, path: input.documentPath };
+          const agents = existing.some((item) => item.id === input.ref.id)
+            ? existing.map((item) => (item.id === input.ref.id ? entry : item))
+            : [...existing, entry];
🤖 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/agents/AgentProfileStore.ts` around lines 374 - 380, Update
the agents construction in the profile save flow to preserve an existing
profile’s array position: locate the entry matching input.ref.id and replace it
with the new id/path object, while appending only when no matching entry exists.
Keep the current.agents fallback behavior unchanged.
apps/mobile/src/features/settings/agentRule.logic.ts (1)

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

Drop the explicit return type on parseInteger.

parseInteger declares : number, but the value is fully inferable from parseRequiredNumber. integer in agentProfile.logic.ts has the same annotation.

As per coding guidelines: "Prefer inferred types over explicit annotations and do not use any."

🤖 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/mobile/src/features/settings/agentRule.logic.ts` around lines 55 - 61,
Remove the explicit : number return annotation from parseInteger in
agentRule.logic.ts, allowing its return type to be inferred from
parseRequiredNumber while preserving the existing validation and return
behavior.

Source: Coding guidelines

packages/contracts/src/agents.ts (2)

378-411: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use a rule-specific id type for rule operations.

AgentRule.id is AgentSlug, but AgentRuleGetInput.id and AgentRuleArchiveInput.id are AgentProfileId. Callers must brand a rule identifier with the profile brand. If the two identifier rules diverge later, the rule endpoints will silently follow the profile constraints.

Add an AgentRuleId export and use it for both the document field and the rule operation inputs.

🤖 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 `@packages/contracts/src/agents.ts` around lines 378 - 411, Define and export
an AgentRuleId type based on AgentSlug, then update AgentRuleDocument.id,
AgentRuleGetInput.id, and AgentRuleArchiveInput.id to use AgentRuleId instead of
AgentProfileId. Ensure restore operations inherit the updated archive identifier
type through AgentRuleArchiveInput.

703-775: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reduce the number of alias layers for one contract.

Each MCP operation now has up to three exported names: the compact name, AgentMcpAgent*, and McpAgentRun*. New code can pick any of them, so the canonical name is not enforced by the type system.

Keep one canonical name plus at most one deprecated compatibility alias, and mark the compatibility aliases with @deprecated so editors steer callers to the canonical export.

🤖 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 `@packages/contracts/src/agents.ts` around lines 703 - 775, Reduce the alias
layers around the AgentMcp operation contracts to one canonical export and at
most one compatibility alias per operation. Remove the redundant AgentMcpAgent*
or McpAgentRun* layer, retain the intended legacy aliases, and annotate every
retained compatibility export with `@deprecated` pointing callers to the canonical
name. Preserve each alias as a reference to the same underlying codec and type.
apps/mobile/src/features/settings/agentRule.logic.test.ts (1)

62-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the thrown message for the multi-colon target case.

.toThrow() with no argument passes for any error. This test guards the fix that stopped parseProfiles from truncating "environment:reviewer:truncated" to id "reviewer". The distinguishing evidence is which error is raised: schema rejection of the preserved id "reviewer:truncated".

💚 Proposed assertion
-    ).toThrow();
+    ).toThrow("Rule settings contain an invalid value.");
🤖 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/mobile/src/features/settings/agentRule.logic.test.ts` around lines 62 -
72, Update the buildAgentRuleDocument test for the multi-colon profiles value to
assert the specific schema-rejection message for the preserved id
“reviewer:truncated,” rather than accepting any thrown error. Keep the existing
input and verify the error distinguishes this case from truncation to
“reviewer.”
packages/client-runtime/src/state/subagentRuntime.test.ts (1)

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

Add coverage for the agentProfileId title fallback.

getOrCreate in subagentRuntime.ts now falls back to agentProfileId for the title when title and detail are absent. This test supplies agentProfileId but never asserts the resulting title, so the new fallback branch is untested.

💚 Proposed assertion
     expect(agents[0]).toMatchObject({
       id: "6a31f0ba-1111-2222-3333-444444444444",
       model: "gpt-5.6-sol",
       profileId: "sol-smoke-test",
+      title: "sol-smoke-test",
       status: "completed",
     });
🤖 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 `@packages/client-runtime/src/state/subagentRuntime.test.ts` around lines 99 -
121, Extend the “preserves native Agent run model, profile ID, and run ID” test
to assert that the folded agent’s title falls back to the supplied
agentProfileId when title and detail are absent. Keep the existing identity,
model, profileId, and status assertions unchanged.
🤖 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/mobile/src/features/settings/agentProfile.logic.ts`:
- Around line 62-67: Update integer to accept minimum and maximum bounds, remove
its explicit number return annotation, and include the field label and limits in
range errors. Import the contract limits and validate maxRuns, maxConcurrency,
maxDepth, and maxWallTimeMinutes with their respective bounds, using a minimum
of 1 for the PositiveInt fields and 0 for maxDepth. Apply the same integer
validation and messages in the web profile editor so decodeAgentProfileDocument
behaves identically across mobile and web.

In `@apps/mobile/src/features/settings/agentRule.logic.ts`:
- Around line 88-104: In apps/mobile/src/features/settings/agentRule.logic.ts
lines 88-104, pass parseProfiles(draft.profiles) directly to
decodeAgentRuleDocument instead of calling decodeAgentProfileLocators, so
validation occurs inside the guarded path. In
apps/mobile/src/features/settings/agentRule.logic.test.ts lines 62-72, replace
the broad .toThrow() assertion with one requiring "Rule settings contain an
invalid value.".

In `@apps/server/src/agents/AgentProfileStore.ts`:
- Around line 399-453: Protect the project-scoped compare-and-swap sequence in
saveUnlocked with projectFileCoordinator.withWorkspaceLock, covering the
current-profile read, revision validation, existing-file snapshot, and
writeContained call. Preserve the existing process-local mutex behavior and
avoid locking environment-scoped profiles unless required by the coordinator’s
API.

In `@apps/server/src/agents/AgentRuleStore.test.ts`:
- Around line 193-213: Update apps/server/src/agents/AgentRuleStore.test.ts
lines 193-213 by passing expectedRevision: saved.revision to the second
store.save call, then assert the failure has tag AgentRuleStoreError and
operation "write-project-file" so the test reaches and verifies rollback. No
direct code change is required in apps/server/src/agents/AgentRuleStore.ts lines
386-394; confirm its expectedRevision contract remains unchanged.

In `@packages/contracts/src/agents.ts`:
- Around line 436-449: Remove the optional cause field from the
AgentProfileInvalidError transport schema while preserving detail, operation,
profileId, and runId. Keep underlying causes server-side or expose only an
explicitly redacted replacement field, and leave the message getter returning
detail.

---

Nitpick comments:
In `@apps/mobile/src/features/settings/agentRule.logic.test.ts`:
- Around line 62-72: Update the buildAgentRuleDocument test for the multi-colon
profiles value to assert the specific schema-rejection message for the preserved
id “reviewer:truncated,” rather than accepting any thrown error. Keep the
existing input and verify the error distinguishes this case from truncation to
“reviewer.”

In `@apps/mobile/src/features/settings/agentRule.logic.ts`:
- Around line 55-61: Remove the explicit : number return annotation from
parseInteger in agentRule.logic.ts, allowing its return type to be inferred from
parseRequiredNumber while preserving the existing validation and return
behavior.

In `@apps/mobile/src/features/settings/agentSettings.logic.ts`:
- Around line 14-21: Update agentSettingsContextKey to serialize the four input
values as an unambiguous tuple rather than concatenating them with colons,
preserving null values distinctly so embedded delimiters cannot collide. Remove
the explicit string return type and let TypeScript infer it.

In `@apps/mobile/src/state/agentProfileSelection.test.ts`:
- Around line 6-25: Add a test in the “agent profile selection” suite for
resolveAgentProfileSelection that passes an explicit agent profile selection and
verifies the same selection is returned unchanged, covering the composer’s
user-selected branch alongside the existing null and undefined cases.

In `@apps/server/src/agents/AgentCatalog.ts`:
- Around line 732-832: Extract the repeated AgentCatalogDocumentError
construction into a shared documentError helper that accepts the Source and
error code, derives kind from source.kind, and preserves scope, id, sourcePath,
and cause. Replace the Effect.mapError callbacks in profileSummary, ruleSummary,
profileDocument, ruleDocument, revisionOf, and readSource with this helper,
passing "invalid-document" or the existing code as appropriate.

In `@apps/server/src/agents/AgentProfileStore.ts`:
- Around line 374-380: Update the agents construction in the profile save flow
to preserve an existing profile’s array position: locate the entry matching
input.ref.id and replace it with the new id/path object, while appending only
when no matching entry exists. Keep the current.agents fallback behavior
unchanged.

In `@apps/server/src/agents/AgentRuleStore.test.ts`:
- Around line 139-146: Replace the substring-count assertion on projectFile in
the AgentRuleStore test with a decoded t3.json assertion. Parse the JSON and
verify that the rules collection contains exactly one entry with id
project-typescript and the expected path, while preserving the existing
generated markdown content assertion.

In `@apps/server/src/agents/AgentRuleStore.ts`:
- Around line 404-411: Update the documentPath selection in the surrounding
rule-loading method to use Result.isSuccess(current) instead of checking
current._tag directly, while preserving the existing sourcePath fallback
behavior for both success and non-success results.

In `@apps/server/src/agents/run/AgentRunReactor.test.ts`:
- Around line 235-248: Remove the redundant continued variable and its assertion
from the test around appendAgentRunTaskActivity. Let successful completion of
the Effect.gen block verify that the interruption was not propagated, without
adding unrelated assertions or behavior.

In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 235-293: In completeSuccessfulRun, extract the repeated
agent-run.fail dispatch and failed-status/revision construction into one local
helper accepting the failure detail. Replace the budget-exhausted preflight
path, afterResult hook failure path, and completion budget-exhausted path with
calls to that helper, preserving usage, occurredAt, retryDurable, and
revisionAfterAgentRunTransition behavior.

In `@apps/server/src/persistence/Migrations/041_AgentRuns.test.ts`:
- Around line 31-47: Expand the expected column list in the migration test’s
columnNames assertion to include every column inserted by AgentRunRepository’s
projection_agent_runs upsert, including consumed_tokens, detached,
integration_target_thread_id, last_error, workspace_mode, project_id, and all
other omitted columns, so the test covers the complete 28-column contract.

In `@apps/server/src/persistence/Migrations/041_AgentRuns.ts`:
- Line 61: Remove the explicit indexes duplicating the UNIQUE constraints: the
agent_run_events index on (agent_run_id, revision) and the child_thread_id
index. Update the corresponding assertions in AgentRuns migration tests to
expect both indexes to be absent, while preserving all other migration indexes
and constraints.

In `@apps/server/src/ws.ts`:
- Around line 484-496: Update mapAgentCatalogError to use the contracts
package’s exported profile reference type for ref instead of deriving its id and
scope through Parameters<AgentCatalog.AgentCatalog["Service"]["getProfile"]>.
Import and apply that existing type while preserving the current error mapping
behavior.
- Around line 456-478: Update agentWorkspaceRoot to add an Effect.tapError
before the existing Effect.mapError, logging the original project lookup failure
and its cause before non-AgentProfileInvalidError values are wrapped. Preserve
the current AgentProfileInvalidError mapping and successful workspaceRoot
behavior.

In `@packages/client-runtime/src/state/subagentRuntime.test.ts`:
- Around line 99-121: Extend the “preserves native Agent run model, profile ID,
and run ID” test to assert that the folded agent’s title falls back to the
supplied agentProfileId when title and detail are absent. Keep the existing
identity, model, profileId, and status assertions unchanged.

In `@packages/contracts/src/agents.ts`:
- Around line 378-411: Define and export an AgentRuleId type based on AgentSlug,
then update AgentRuleDocument.id, AgentRuleGetInput.id, and
AgentRuleArchiveInput.id to use AgentRuleId instead of AgentProfileId. Ensure
restore operations inherit the updated archive identifier type through
AgentRuleArchiveInput.
- Around line 703-775: Reduce the alias layers around the AgentMcp operation
contracts to one canonical export and at most one compatibility alias per
operation. Remove the redundant AgentMcpAgent* or McpAgentRun* layer, retain the
intended legacy aliases, and annotate every retained compatibility export with
`@deprecated` pointing callers to the canonical name. Preserve each alias as a
reference to the same underlying codec and type.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9538eafe-c167-4a9f-bfb9-0f610a408644

📥 Commits

Reviewing files that changed from the base of the PR and between 9afef94 and f445d85.

📒 Files selected for processing (139)
  • apps/mobile/src/Stack.tsx
  • apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
  • apps/mobile/src/features/settings/SettingsRouteScreen.tsx
  • apps/mobile/src/features/settings/agentProfile.logic.test.ts
  • apps/mobile/src/features/settings/agentProfile.logic.ts
  • apps/mobile/src/features/settings/agentRule.logic.test.ts
  • apps/mobile/src/features/settings/agentRule.logic.ts
  • apps/mobile/src/features/settings/agentSettings.logic.test.ts
  • apps/mobile/src/features/settings/agentSettings.logic.ts
  • apps/mobile/src/features/settings/components/settings-sheet-targets.ts
  • apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
  • apps/mobile/src/features/threads/ThreadComposer.tsx
  • apps/mobile/src/features/threads/ThreadDetailScreen.tsx
  • apps/mobile/src/features/threads/ThreadRouteScreen.tsx
  • apps/mobile/src/features/threads/new-task-flow-provider.tsx
  • apps/mobile/src/features/threads/use-project-actions.ts
  • apps/mobile/src/lib/projectThreadStartTurn.ts
  • apps/mobile/src/state/agentProfileSelection.test.ts
  • apps/mobile/src/state/agentProfileSelection.ts
  • apps/mobile/src/state/agents.ts
  • apps/mobile/src/state/thread-outbox-model.ts
  • apps/mobile/src/state/thread-outbox.test.ts
  • apps/mobile/src/state/use-composer-drafts.test.ts
  • apps/mobile/src/state/use-composer-drafts.ts
  • apps/mobile/src/state/use-thread-composer-state.test.ts
  • apps/mobile/src/state/use-thread-composer-state.ts
  • apps/mobile/src/state/use-thread-outbox-drain.ts
  • apps/server/integration/OrchestrationEngineHarness.integration.ts
  • apps/server/src/agents/AgentCatalog.test.ts
  • apps/server/src/agents/AgentCatalog.ts
  • apps/server/src/agents/AgentHookRunner.test.ts
  • apps/server/src/agents/AgentHookRunner.ts
  • apps/server/src/agents/AgentOrchestration.ts
  • apps/server/src/agents/AgentOrchestrationLive.test.ts
  • apps/server/src/agents/AgentOrchestrationLive.ts
  • apps/server/src/agents/AgentProfileServices.ts
  • apps/server/src/agents/AgentProfileStore.test.ts
  • apps/server/src/agents/AgentProfileStore.ts
  • apps/server/src/agents/AgentProjectFileCoordinator.ts
  • apps/server/src/agents/AgentPromptResolver.test.ts
  • apps/server/src/agents/AgentPromptResolver.ts
  • apps/server/src/agents/AgentRuleStore.test.ts
  • apps/server/src/agents/AgentRuleStore.ts
  • apps/server/src/agents/AgentStoreErrorMapping.test.ts
  • apps/server/src/agents/AgentStoreErrorMapping.ts
  • apps/server/src/agents/AgentWorkspaceRoot.test.ts
  • apps/server/src/agents/AgentWorkspaceRoot.ts
  • apps/server/src/agents/prompt/PromptCompiler.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • apps/server/src/agents/prompt/index.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/server/src/agents/run/AgentRun.test.ts
  • apps/server/src/agents/run/AgentRun.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/agents/run/AgentRunReactor.test.ts
  • apps/server/src/agents/run/AgentRunReactor.ts
  • apps/server/src/agents/run/AgentRunRepository.test.ts
  • apps/server/src/agents/run/AgentRunRepository.ts
  • apps/server/src/auth/RpcAuthorization.ts
  • apps/server/src/mcp/McpHttpServer.test.ts
  • apps/server/src/mcp/McpHttpServer.ts
  • apps/server/src/mcp/McpInvocationContext.test.ts
  • apps/server/src/mcp/McpInvocationContext.ts
  • apps/server/src/mcp/McpSessionRegistry.test.ts
  • apps/server/src/mcp/McpSessionRegistry.ts
  • apps/server/src/mcp/McpToolkit.ts
  • apps/server/src/mcp/toolkits/agents/handlers.ts
  • apps/server/src/mcp/toolkits/agents/tools.ts
  • apps/server/src/orchestration/Layers/ProjectionPipeline.ts
  • apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • apps/server/src/orchestration/agentProfile.test.ts
  • apps/server/src/orchestration/decider.ts
  • apps/server/src/orchestration/projector.ts
  • apps/server/src/persistence/Layers/ProjectionThreads.ts
  • apps/server/src/persistence/Migrations.ts
  • apps/server/src/persistence/Migrations/041_AgentRuns.test.ts
  • apps/server/src/persistence/Migrations/041_AgentRuns.ts
  • apps/server/src/persistence/Migrations/042_ProjectionThreadsAgentProfile.ts
  • apps/server/src/persistence/Services/ProjectionThreads.ts
  • apps/server/src/provider/AgentRuntimeCompatibility.test.ts
  • apps/server/src/provider/AgentRuntimeCompatibility.ts
  • apps/server/src/provider/Layers/ClaudeAdapter.ts
  • apps/server/src/provider/Layers/CodexAdapter.ts
  • apps/server/src/provider/Layers/CursorAdapter.test.ts
  • apps/server/src/provider/Layers/CursorAdapter.ts
  • apps/server/src/provider/Layers/GrokAdapter.test.ts
  • apps/server/src/provider/Layers/GrokAdapter.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.ts
  • apps/server/src/provider/Services/ProviderAdapter.ts
  • apps/server/src/server.test.ts
  • apps/server/src/server.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/AgentsPanel.tsx
  • apps/web/src/components/ChatView.logic.test.ts
  • apps/web/src/components/ChatView.logic.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/chat/AgentProfilePicker.logic.ts
  • apps/web/src/components/chat/AgentProfilePicker.test.ts
  • apps/web/src/components/chat/AgentProfilePicker.tsx
  • apps/web/src/components/chat/ChatComposer.logic.test.ts
  • apps/web/src/components/chat/ChatComposer.logic.ts
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/web/src/components/settings/AgentsSettings.logic.test.ts
  • apps/web/src/components/settings/AgentsSettings.logic.ts
  • apps/web/src/components/settings/AgentsSettings.test.tsx
  • apps/web/src/components/settings/AgentsSettings.tsx
  • apps/web/src/components/settings/RulesSettings.logic.test.ts
  • apps/web/src/components/settings/RulesSettings.logic.ts
  • apps/web/src/components/settings/RulesSettings.test.tsx
  • apps/web/src/components/settings/RulesSettings.tsx
  • apps/web/src/components/settings/SettingsSidebarNav.tsx
  • apps/web/src/components/settings/settingsSearch.ts
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/settings.agents.tsx
  • apps/web/src/state/agents.ts
  • docs/internals/agents.md
  • docs/internals/glossary.md
  • docs/user/agents.md
  • packages/client-runtime/package.json
  • packages/client-runtime/src/state/agents.ts
  • packages/client-runtime/src/state/subagentRuntime.test.ts
  • packages/client-runtime/src/state/subagentRuntime.ts
  • packages/contracts/src/agentRefs.test.ts
  • packages/contracts/src/agentRefs.ts
  • packages/contracts/src/agents.test.ts
  • packages/contracts/src/agents.ts
  • packages/contracts/src/index.ts
  • packages/contracts/src/orchestration.ts
  • packages/contracts/src/providerRuntime.ts
  • packages/contracts/src/rpc.ts
  • packages/contracts/src/t3ProjectFile.test.ts
  • packages/contracts/src/t3ProjectFile.ts
  • packages/shared/package.json
  • packages/shared/src/agentRuleGlobs.test.ts
  • packages/shared/src/agentRuleGlobs.ts
🚧 Files skipped from review as they are similar to previous changes (116)
  • apps/server/src/auth/RpcAuthorization.ts
  • apps/mobile/src/features/threads/use-project-actions.ts
  • apps/server/src/provider/Layers/GrokAdapter.test.ts
  • packages/client-runtime/package.json
  • apps/mobile/src/state/use-composer-drafts.test.ts
  • apps/server/src/server.test.ts
  • apps/mobile/src/features/threads/ThreadRouteScreen.tsx
  • apps/server/integration/OrchestrationEngineHarness.integration.ts
  • apps/server/src/persistence/Migrations.ts
  • apps/mobile/src/features/settings/SettingsRouteScreen.tsx
  • apps/mobile/src/state/use-thread-outbox-drain.ts
  • apps/server/src/agents/prompt/index.ts
  • apps/server/src/mcp/McpSessionRegistry.ts
  • apps/mobile/src/lib/projectThreadStartTurn.ts
  • apps/server/src/agents/AgentStoreErrorMapping.test.ts
  • apps/web/src/components/settings/AgentsSettings.test.tsx
  • apps/web/src/components/settings/AgentsSettings.logic.test.ts
  • apps/server/src/persistence/Layers/ProjectionThreads.ts
  • packages/shared/src/agentRuleGlobs.test.ts
  • apps/server/src/provider/AgentRuntimeCompatibility.test.ts
  • apps/mobile/src/features/settings/components/settings-sheet-targets.ts
  • apps/mobile/src/state/use-composer-drafts.ts
  • apps/web/src/components/ChatView.logic.ts
  • apps/server/src/agents/AgentProfileServices.ts
  • apps/server/src/mcp/McpInvocationContext.ts
  • apps/mobile/src/features/settings/agentProfile.logic.test.ts
  • apps/server/src/mcp/McpSessionRegistry.test.ts
  • apps/server/src/provider/AgentRuntimeCompatibility.ts
  • apps/web/src/components/ChatView.logic.test.ts
  • apps/web/src/components/chat/AgentProfilePicker.logic.ts
  • apps/mobile/src/state/use-thread-composer-state.ts
  • apps/mobile/src/features/threads/ThreadComposer.tsx
  • apps/server/src/agents/AgentOrchestrationLive.test.ts
  • apps/server/src/mcp/McpToolkit.ts
  • apps/web/src/components/settings/settingsSearch.ts
  • apps/server/src/orchestration/agentProfile.test.ts
  • apps/server/src/agents/AgentWorkspaceRoot.ts
  • apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
  • apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
  • apps/server/src/persistence/Services/ProjectionThreads.ts
  • apps/web/src/components/chat/ChatComposer.logic.test.ts
  • apps/web/src/components/chat/ChatComposer.logic.ts
  • apps/server/src/agents/AgentProjectFileCoordinator.ts
  • apps/web/src/state/agents.ts
  • apps/server/src/agents/AgentWorkspaceRoot.test.ts
  • apps/server/src/agents/run/AgentRunRepository.test.ts
  • apps/mobile/src/state/thread-outbox.test.ts
  • apps/server/src/mcp/toolkits/agents/handlers.ts
  • apps/mobile/src/state/thread-outbox-model.ts
  • apps/server/src/mcp/McpHttpServer.test.ts
  • apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
  • packages/contracts/src/providerRuntime.ts
  • packages/shared/package.json
  • apps/server/src/agents/AgentProfileStore.test.ts
  • docs/internals/agents.md
  • packages/contracts/src/agentRefs.test.ts
  • apps/server/src/provider/Layers/CursorAdapter.test.ts
  • apps/server/src/provider/Layers/GrokAdapter.ts
  • packages/contracts/src/agentRefs.ts
  • apps/server/src/provider/Services/ProviderAdapter.ts
  • apps/server/src/provider/Layers/CursorAdapter.ts
  • apps/server/src/agents/AgentStoreErrorMapping.ts
  • apps/server/src/provider/Layers/ClaudeAdapter.ts
  • apps/server/src/server.ts
  • apps/server/src/agents/AgentPromptResolver.test.ts
  • apps/server/src/mcp/McpInvocationContext.test.ts
  • apps/web/src/components/settings/SettingsSidebarNav.tsx
  • packages/contracts/src/index.ts
  • apps/mobile/src/state/use-thread-composer-state.test.ts
  • apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts
  • packages/contracts/src/t3ProjectFile.ts
  • apps/web/src/components/settings/RulesSettings.logic.test.ts
  • apps/server/src/orchestration/decider.ts
  • apps/server/src/orchestration/projector.ts
  • apps/mobile/src/state/agents.ts
  • apps/server/src/mcp/McpHttpServer.ts
  • apps/mobile/src/features/threads/ThreadDetailScreen.tsx
  • apps/server/src/agents/AgentHookRunner.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.ts
  • apps/server/src/agents/prompt/PromptCompiler.ts
  • apps/web/src/components/settings/AgentsSettings.logic.ts
  • apps/mobile/src/Stack.tsx
  • apps/server/src/agents/AgentOrchestration.ts
  • apps/server/src/mcp/toolkits/agents/tools.ts
  • apps/web/src/components/settings/RulesSettings.logic.ts
  • apps/server/src/agents/run/AgentRunRepository.ts
  • apps/web/src/components/settings/RulesSettings.test.tsx
  • apps/web/src/routes/settings.agents.tsx
  • packages/shared/src/agentRuleGlobs.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
  • apps/web/src/components/chat/AgentProfilePicker.tsx
  • apps/server/src/agents/AgentPromptResolver.ts
  • apps/web/src/components/settings/RulesSettings.tsx
  • apps/web/src/components/chat/AgentProfilePicker.test.ts
  • apps/server/src/agents/prompt/RuleMatcher.ts
  • packages/contracts/src/orchestration.ts
  • apps/server/src/agents/AgentCatalog.test.ts
  • apps/mobile/src/features/threads/new-task-flow-provider.tsx
  • apps/web/src/components/settings/AgentsSettings.tsx
  • packages/contracts/src/agents.test.ts
  • apps/server/src/provider/Layers/CodexAdapter.ts
  • apps/server/src/agents/AgentOrchestrationLive.ts
  • apps/mobile/src/state/agentProfileSelection.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/server/src/agents/AgentHookRunner.test.ts
  • apps/server/src/agents/run/AgentRunDeadlineReactor.ts
  • apps/server/src/agents/prompt/prompt.test.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • docs/user/agents.md
  • packages/contracts/src/t3ProjectFile.test.ts
  • apps/server/src/orchestration/Layers/ProjectionPipeline.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • packages/contracts/src/rpc.ts
  • apps/server/src/agents/run/AgentRun.ts

Comment on lines +62 to +67
function integer(value: string, label: string): number {
const parsed = parseRequiredNumber(value, label);
if (!Number.isInteger(parsed) || parsed < 0)
throw new Error(`${label} must be a non-negative whole number.`);
return parsed;
}

Copy link
Copy Markdown

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

Budget fields report contract range violations with a generic message.

integer accepts any non-negative whole number. The contract bounds each budget field: maxRuns at most 32, maxConcurrency at most 8, maxDepth at most 4, and maxWallTimeMinutes at most 120. maxRuns, maxConcurrency, and maxWallTimeMinutes are also PositiveInt, so 0 is invalid.

An out-of-range entry therefore reaches decodeAgentProfileDocument and surfaces as "Profile settings contain an invalid value." The user does not learn which field failed or what the limit is. parseInteger in agentRule.logic.ts reports its range precisely, so the two editors behave differently.

Pass the minimum and maximum into integer and report them. Also drop the explicit number return type to follow the inferred-types guideline.

🐛 Proposed fix
-function integer(value: string, label: string): number {
+function integer(value: string, label: string, minimum: number, maximum: number) {
   const parsed = parseRequiredNumber(value, label);
-  if (!Number.isInteger(parsed) || parsed < 0)
-    throw new Error(`${label} must be a non-negative whole number.`);
+  if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) {
+    throw new Error(`${label} must be a whole number from ${minimum} to ${maximum}.`);
+  }
   return parsed;
 }

Then import the contract limits and apply them per field:

     budgets: {
-      maxRuns: integer(draft.maxRuns, "Maximum runs"),
-      maxConcurrency: integer(draft.maxConcurrency, "Maximum concurrency"),
-      maxDepth: integer(draft.maxDepth, "Maximum delegation depth"),
-      maxWallTimeMinutes: integer(draft.maxWallTimeMinutes, "Maximum wall time"),
+      maxRuns: integer(draft.maxRuns, "Maximum runs", 1, AGENT_RUN_MAX_RUNS),
+      maxConcurrency: integer(
+        draft.maxConcurrency,
+        "Maximum concurrency",
+        1,
+        AGENT_RUN_MAX_CONCURRENCY,
+      ),
+      maxDepth: integer(
+        draft.maxDepth,
+        "Maximum delegation depth",
+        0,
+        AGENT_RUN_MAX_DELEGATION_DEPTH,
+      ),
+      maxWallTimeMinutes: integer(
+        draft.maxWallTimeMinutes,
+        "Maximum wall time",
+        1,
+        AGENT_RUN_MAX_WALL_TIME_MINUTES,
+      ),
     },

Apply the same change to the web profile editor so both surfaces report identical messages.

As per coding guidelines: "Prefer inferred types over explicit annotations" and "Frontend behavior must support all applicable clients: web, desktop/Electron, and mobile/React Native".

Also applies to: 98-109

🤖 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/mobile/src/features/settings/agentProfile.logic.ts` around lines 62 -
67, Update integer to accept minimum and maximum bounds, remove its explicit
number return annotation, and include the field label and limits in range
errors. Import the contract limits and validate maxRuns, maxConcurrency,
maxDepth, and maxWallTimeMinutes with their respective bounds, using a minimum
of 1 for the PositiveInt fields and 0 for maxDepth. Apply the same integer
validation and messages in the web profile editor so decodeAgentProfileDocument
behaves identically across mobile and web.

Source: Coding guidelines

Comment on lines +88 to +104
return decodeAgentRuleDocument({
id: draft.id.trim(),
scope: draft.scope,
revision: baseline?.revision ?? "a".repeat(64),
name: draft.name.trim(),
...(draft.description.trim() ? { description: draft.description.trim() } : {}),
globs: parseAgentRuleGlobs(draft.globs),
alwaysApply: draft.alwaysApply,
priority: parseInteger(draft.priority),
sourcePath: baseline?.sourcePath ?? null,
archivedAt: baseline?.archivedAt ?? null,
updatedAt: now,
body: draft.body,
profiles: decodeAgentProfileLocators(parseProfiles(draft.profiles)),
createdAt: baseline?.createdAt ?? now,
});
}

Copy link
Copy Markdown

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

An invalid profile target surfaces a raw Effect ParseError to the user, and the test does not detect it. decodeAgentProfileLocators runs while the argument object is built, so it executes before control enters decodeAgentRuleDocument and its try/catch. The escaping error is rendered directly by the settings screen. The existing test uses a bare .toThrow(), which passes for any error and therefore does not distinguish the raw schema dump from the intended message.

  • apps/mobile/src/features/settings/agentRule.logic.ts#L88-L104: remove the separate decodeAgentProfileLocators call and pass parseProfiles(draft.profiles) straight into decodeAgentRuleDocument, so AgentRuleDocumentSchema validates the locators inside the guarded path.
  • apps/mobile/src/features/settings/agentRule.logic.test.ts#L62-L72: change .toThrow() to .toThrow("Rule settings contain an invalid value.") so the test pins the user-visible message.
📍 Affects 2 files
  • apps/mobile/src/features/settings/agentRule.logic.ts#L88-L104 (this comment)
  • apps/mobile/src/features/settings/agentRule.logic.test.ts#L62-L72
🤖 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/mobile/src/features/settings/agentRule.logic.ts` around lines 88 - 104,
In apps/mobile/src/features/settings/agentRule.logic.ts lines 88-104, pass
parseProfiles(draft.profiles) directly to decodeAgentRuleDocument instead of
calling decodeAgentProfileLocators, so validation occurs inside the guarded
path. In apps/mobile/src/features/settings/agentRule.logic.test.ts lines 62-72,
replace the broad .toThrow() assertion with one requiring "Rule settings contain
an invalid value.".

Comment on lines +399 to +453
const saveUnlocked = Effect.fn("AgentProfileStore.saveUnlocked")(function* (input: {
readonly profile: AgentProfileDocument;
readonly expectedRevision?: AgentProfileRevision | undefined;
readonly workspaceRoot?: string | undefined;
}) {
const ref: AgentProfileLocator = { id: input.profile.id, scope: input.profile.scope };
const current = yield* catalog
.getProfile({ ref, workspaceRoot: input.workspaceRoot })
.pipe(Effect.result);
if (Result.isSuccess(current)) {
if (input.expectedRevision !== current.success.revision) {
return yield* new AgentProfileStoreRevisionConflictError({
scope: ref.scope,
id: ref.id,
...(input.expectedRevision ? { expectedRevision: input.expectedRevision } : {}),
actualRevision: current.success.revision,
});
}
} else if (current.failure._tag !== "AgentCatalogNotFoundError") {
return yield* storeError("load", ref, "Could not load current profile.", current.failure);
} else if (input.expectedRevision !== undefined) {
return yield* new AgentProfileStoreRevisionConflictError({
scope: ref.scope,
id: ref.id,
expectedRevision: input.expectedRevision,
});
}

const defaultPath =
ref.scope === "environment"
? path.join("agents", `${ref.id}.md`)
: `.t3code/agents/${ref.id}.md`;
const documentPath =
current._tag === "Success"
? (current.success.sourcePath ?? defaultPath)
: ref.scope === "environment"
? defaultPath
: (input.profile.sourcePath ?? defaultPath);
const target = yield* resolveWritePath({
ref,
workspaceRoot: input.workspaceRoot,
documentPath,
});
const previous = yield* existingFile(target.filePath).pipe(
Effect.mapError((cause) =>
storeError("write-document", ref, "Could not snapshot profile Markdown.", cause),
),
);
yield* writeContained({
ref,
root: target.root,
filePath: target.filePath,
contents: renderProfile(input.profile),
operation: "write-document",
});

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 | 🟠 Major | 🏗️ Heavy lift

The compare-and-swap window is not protected for the Markdown document.

saveUnlocked reads the current revision through catalog.getProfile, then writes the file. Only the process-local mutex serializes this. writeProjectReference takes projectFileCoordinator.withWorkspaceLock, but the profile Markdown write at Line 447 runs outside any workspace lock.

An external editor or a second server process can change the document between the read and the write. The write then overwrites that change without a conflict error.

If a single server process is the only writer by design, record that assumption in the module comment. Otherwise, take the workspace lock around the read-check-write sequence for project-scoped profiles.

🤖 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/agents/AgentProfileStore.ts` around lines 399 - 453, Protect
the project-scoped compare-and-swap sequence in saveUnlocked with
projectFileCoordinator.withWorkspaceLock, covering the current-profile read,
revision validation, existing-file snapshot, and writeContained call. Preserve
the existing process-local mutex behavior and avoid locking environment-scoped
profiles unless required by the coordinator’s API.

Comment on lines +193 to +213
const result = yield* withStore(
workspace,
tempDir,
Effect.service(AgentRuleStore.AgentRuleStore).pipe(
Effect.flatMap((store) =>
store.save({
rule: { ...saved, body: "This write must be rolled back." },
workspaceRoot: workspace,
}),
),
Effect.result,
),
);

assert.isTrue(Result.isFailure(result));
assert.match(
yield* fileSystem.readFileString(
path.join(workspace, ".t3code", "rules", "restore-typescript.md"),
),
/Use strict types\./,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The rollback test for an existing project rule does not reach the rollback path. saveUnlocked compares input.expectedRevision with the current revision and fails with AgentRuleStoreRevisionConflictError when they differ. The test saves an existing rule without expectedRevision, so the save fails at the compare-and-swap check before any Markdown write. The assertions still pass, but they prove only that nothing was written.

  • apps/server/src/agents/AgentRuleStore.test.ts#L193-L213: pass expectedRevision: saved.revision to the second store.save call so the save reaches writeContained and then fails on the invalid t3.json. Also assert the returned failure tag is AgentRuleStoreError with operation: "write-project-file" so a future compare-and-swap regression cannot pass this test silently.
  • apps/server/src/agents/AgentRuleStore.ts#L386-L394: no code change required; confirm that requiring expectedRevision for every update of an existing rule is the intended contract, because the callers must always supply it.
📍 Affects 2 files
  • apps/server/src/agents/AgentRuleStore.test.ts#L193-L213 (this comment)
  • apps/server/src/agents/AgentRuleStore.ts#L386-L394
🤖 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/agents/AgentRuleStore.test.ts` around lines 193 - 213, Update
apps/server/src/agents/AgentRuleStore.test.ts lines 193-213 by passing
expectedRevision: saved.revision to the second store.save call, then assert the
failure has tag AgentRuleStoreError and operation "write-project-file" so the
test reaches and verifies rollback. No direct code change is required in
apps/server/src/agents/AgentRuleStore.ts lines 386-394; confirm its
expectedRevision contract remains unchanged.

Comment on lines +436 to +449
export class AgentProfileInvalidError extends Schema.TaggedErrorClass<AgentProfileInvalidError>()(
"AgentProfileInvalidError",
{
detail: TrimmedNonEmptyString,
operation: Schema.optional(Schema.String),
profileId: Schema.optional(AgentProfileId),
runId: Schema.optional(AgentRunId),
cause: Schema.optional(Schema.Defect()),
},
) {
override get message(): string {
return this.detail;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how AgentProfileInvalidError and its cause are produced and encoded.
rg -n -C 4 'AgentProfileInvalidError' --type=ts
rg -n -C 3 'Schema\.Defect\(' --type=ts

Repository: pingdotgg/t3code

Length of output: 154


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target file ---'
git ls-files 'packages/contracts/src/agents.ts'
printf '%s\n' '--- error references ---'
rg -n -C 5 'AgentProfileInvalidError|AgentProfileInvalid' .
printf '%s\n' '--- Defect schemas ---'
rg -n -C 5 'Defect\s*\(' packages --glob '*.{ts,tsx,js,jsx}' || true
printf '%s\n' '--- transport encoding and error handling ---'
rg -n -C 4 'encode.*Error|Schema\.encode|TaggedErrorClass|Cause|cause' packages apps --glob '*.{ts,tsx,js,jsx}' 2>/dev/null | head -n 500 || true

Repository: pingdotgg/t3code

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- websocket error flow ---'
sed -n '480,560p' apps/server/src/ws.ts
printf '%s\n' '--- AgentProfileError transport references ---'
rg -n -C 5 'AgentProfileError|AgentProfileInvalidError|observeRpcEffect|Schema\.encode|encode.*error|error.*encode' apps/server/src packages/contracts/src --glob '*.{ts,tsx}'
printf '%s\n' '--- RPC and HTTP error response helpers ---'
rg -n -C 5 'Rpc.*Error|mapError|TaggedError|JSON|response.*error|error:' apps/server/src/ws.ts apps/server/src/mcp --glob '*.{ts,tsx}' | head -n 400
printf '%s\n' '--- Effect dependency metadata ---'
rg -n -C 3 '"effect"|effect' package.json packages/*/package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null | head -n 120

Repository: pingdotgg/t3code

Length of output: 50373


🌐 Web query:

Effect Schema Defect serialization Schema.Defect JSON encode official documentation

💡 Result:

In the Effect library, Schema.Defect is a specialized schema designed to handle the serialization of JavaScript Error instances and other unrecoverable defects, which typically do not serialize correctly to JSON because their properties (like message, stack, and name) are non-enumerable [1][2][3]. When used with Schema.encodeSync or other encoding functions, Schema.Defect transforms these Error objects into plain, JSON-serializable objects that retain essential diagnostic information [1][2][3]. Key behaviors of Schema.Defect include: - Encoding: It converts Error instances into objects containing properties such as name and message [2][3]. - Decoding: If the input contains the expected properties (e.g., message and optionally name or stack), it reconstructs an Error instance [2]. - Use Case: It is primarily used for cross-boundary communication (such as RPC calls) or persistence, where standard JSON serialization would otherwise result in an empty object ({}) for Error types [1][2]. Example usage with Schema.encodeSync: ts import { Schema, Exit } from "effect" // Define a schema using Schema.Defect const schema = Schema.Exit({ failure: Schema.String, success: Schema.NumberFromString, defect: Schema.Defect, }) const encode = Schema.encodeSync(schema) // Encoding a die exit (defect) console.log(encode(Exit.die(new Error("Example message")))) /* Output: { _tag: 'Failure', cause: { _tag: 'Die', defect: { name: 'Error', message: 'Example message' } } } */ By default, various Effect internal mechanisms (like @effect/rpc) utilize Schema.Defect to ensure that errors can be safely transmitted across network boundaries without losing their descriptive content [1][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- RPC group and server construction ---'
rg -n -C 6 'WsRpcGroup|RpcGroup|RpcServer|RpcClient|WebSocket.*Server|WebSocket.*Client|errorSchema|errors:' apps/server/src packages --glob '*.{ts,tsx}' | head -n 500
printf '%s\n' '--- instrumentation implementation ---'
sed -n '1,260p' apps/server/src/observability/RpcInstrumentation.ts
printf '%s\n' '--- package files and lockfile version ---'
rg -n -C 4 '"effect"|"`@effect/rpc`"|"`@effect/platform`"' package.json apps/server/package.json packages/*/package.json pnpm-lock.yaml 2>/dev/null | head -n 180
printf '%s\n' '--- local Effect sources, if present ---'
find . -path '*/node_modules/effect*' -o -path '*/node_modules/@effect/rpc*' | head -n 30

Repository: pingdotgg/t3code

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- WsRpcGroup definition ---'
rg -n -C 12 'WsRpcGroup' packages/contracts/src apps/server/src --glob '*.{ts,tsx}'
printf '%s\n' '--- RPC server setup in ws.ts ---'
rg -n -C 12 'RpcServer\.|RpcGroup|WebSocket|wsRpc|WsRpc' apps/server/src/ws.ts
printf '%s\n' '--- effect catalog version ---'
rg -n -C 5 'effect:' pnpm-workspace.yaml package.json pnpm-lock.yaml 2>/dev/null | head -n 100
printf '%s\n' '--- RPC request declarations ---'
rg -n -C 8 'Rpc\.|Request<|RpcGroup\.make|RpcGroup\.of|RpcGroup\.add' packages/contracts/src --glob '*.{ts,tsx}' | head -n 300

Repository: pingdotgg/t3code

Length of output: 50372


🌐 Web query:

site:github.com/Effect-TS/effect "Schema.Defect" "stack"

💡 Result:

In the context of the Effect-TS ecosystem, Schema.Defect is a specific schema used to handle the serialization of defects (unexpected errors, such as runtime exceptions) when they cross boundaries, most notably in RPC (Remote Procedure Call) communications [1][2]. Key details regarding Schema.Defect: 1. Purpose: When an Effect application encounters a defect, it typically carries non-enumerable properties like message and stack trace. Standard serialization (such as JSON.stringify) fails to preserve these, causing the diagnostic information to be lost [1]. Schema.Defect provides a structured way to encode these defects so that they can be transmitted and reconstructed accurately [1]. 2. Stack Traces: By default, Schema.Defect is designed to handle defect serialization for external boundaries, which sometimes involves stripping sensitive or internal diagnostic details like stack traces [2]. However, developers can control this behavior. For instance, when defining RPC methods using Rpc.make, one can provide a defect schema option (e.g., Schema.Defect({ includeStack: true })) to explicitly include stack traces and other diagnostic data, which is useful for internal service boundaries where debugging visibility is required [3][2]. 3. Integration: Schema.Defect is integrated into the broader Effect schema system [4][5]. In RPC implementations, it serves as the default mechanism for encoding exits (the result of an effect execution) that have failed due to a defect, ensuring that the error state is communicated across the wire [1][2]. Developers can override this default on a per-RPC basis if they need to pass more complex or custom error structures [2][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
tarball="$(jq -r '.dist.tarball' "$tmpdir/metadata.json")"
curl -fsSL "$tarball" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
printf '%s\n' '--- Schema.Defect implementation and nearby helpers ---'
rg -n -C 12 'Defect|defect' "$tmpdir/package/src" "$tmpdir/package/dist" 2>/dev/null | head -n 300

Repository: pingdotgg/t3code

Length of output: 26714


🏁 Script executed:

#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
curl -fsSL "$(jq -r '.dist.tarball' "$tmpdir/metadata.json")" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
printf '%s\n' '--- files defining or exporting Defect ---'
rg -l 'Defect' "$tmpdir/package/src" | grep -E '/(Schema|schema|ParseResult|Effect|Rpc)' | head -n 40
printf '%s\n' '--- exact Defect definitions and encoders ---'
rg -n -C 20 'export const Defect|const Defect|function Defect|includeStack|defect.*encode|encode.*defect' "$tmpdir/package/src/Schema.ts" "$tmpdir/package/src/Schema" "$tmpdir/package/src/unstable/rpc" 2>/dev/null | head -n 500

Repository: pingdotgg/t3code

Length of output: 33199


🏁 Script executed:

#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
curl -fsSL "$(jq -r '.dist.tarball' "$tmpdir/metadata.json")" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
node --input-type=module - "$tmpdir/package/dist" <<'JS'
import * as Schema from `${process.argv[2]}/Schema.js`;

const CauseSchema = Schema.Defect();
const encodeCause = Schema.encodeUnknownSync(Schema.toCodecJson(CauseSchema));

const samples = {
  error: new Error("failed to read /home/user/.t3/userdata/config.json"),
  errorWithCause: Object.assign(
    new Error("provider failed"),
    { cause: new Error("token at /run/secrets/provider-token") },
  ),
  object: { message: "filesystem path: /srv/private/project", stack: "secret stack" },
};

for (const [label, value] of Object.entries(samples)) {
  console.log(label, JSON.stringify(encodeCause(value)));
}
JS

Repository: pingdotgg/t3code

Length of output: 972


🏁 Script executed:

#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
curl -fsSL "$(jq -r '.dist.tarball' "$tmpdir/metadata.json")" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
node --input-type=module - "$tmpdir/package/dist" <<'JS'
const Schema = await import(`file://${process.argv[2]}/Schema.js`);

const CauseSchema = Schema.Defect();
const encodeCause = Schema.encodeUnknownSync(Schema.toCodecJson(CauseSchema));

const samples = {
  error: new Error("failed to read /home/user/.t3/userdata/config.json"),
  errorWithCause: Object.assign(
    new Error("provider failed"),
    { cause: new Error("token at /run/secrets/provider-token") },
  ),
  object: { message: "filesystem path: /srv/private/project", stack: "secret stack" },
};

for (const [label, value] of Object.entries(samples)) {
  console.log(label, JSON.stringify(encodeCause(value)));
}
JS

Repository: pingdotgg/t3code

Length of output: 1188


Remove cause from the transport schema. Agent WebSocket RPC errors include AgentProfileInvalidError. Schema.Defect() encodes error messages and nested causes, which can disclose filesystem paths or other server details. Keep cause server-side or map it to a redacted field.

🤖 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 `@packages/contracts/src/agents.ts` around lines 436 - 449, Remove the optional
cause field from the AgentProfileInvalidError transport schema while preserving
detail, operation, profileId, and runId. Keep underlying causes server-side or
expose only an explicitly redacted replacement field, and leave the message
getter returning detail.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 6 total unresolved issues (including 5 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 502d128. Configure here.

revision: 0,
occurredAt: command.occurredAt,
wallTimeOriginAt:
state.runs.get(parent.rootRunId)?.wallTimeOriginAt ?? parent.wallTimeOriginAt,

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.

Spawn ignores exhausted wall budget

Medium Severity

Child agent-run.request now inherits wallTimeOriginAt from the root, so a late spawn can already be past maxWallTimeMinutes, but request/start still succeed. Token and cost budgets fail closed before creating work; wall time only blocks later succeed/follow-up, so orchestration can still create threads and worktrees that the deadline reactor immediately cancels.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 502d128. Configure here.

athik13 added a commit to athik13/t3code that referenced this pull request Aug 11, 2026
Port the reusable profile and Rule layer from upstream PR pingdotgg#5632 onto the V2 orchestration model.

Profiles and matching Rules are stored through authorized environment RPCs, selected on web and mobile, pinned through tri-state V2 thread commands, snapshotted immutably in migration 052, and compiled into every provider turn. Existing fork delegation rows remain available as typed read-only V2 history.

This intentionally does not import the donor AgentRun repository, reactors, MCP toolkit, or conflicting migrations. Runtime/workspace policy, tool restrictions, delegation budgets, provider requirements, and non-prompt hooks remain follow-up enforcement work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants