[Bug] Claude says it is going to do research and start working, but instead stops with stop_reason:tool_use and does nothing

Resolved 💬 4 comments Opened Mar 13, 2026 by dimfeld Closed Apr 7, 2026

Bug Description
This started within the last week or two, where I send in a fairly large prompt, and then Claude says that it's going to gather research context, but then most of the time does nothing.

I feed in the prompt below or one similar to it, and Claude responds like

I'll start by selecting Tasks 1-6 as the first batch — these form the foundational layer (module structure, types, registry, first action type, database schema, permissions/feature flag). They're
  tightly coupled and all needed before the service functions and tests.

  Let me first gather research context from the key reference files.

And then nothing happens. If I follow up with "continue" or similar then it seems to get going.

Environment Info

  • Platform: darwin
  • Terminal: WezTerm
  • Version: 2.1.74
  • Feedback ID: 26549389-7a51-486f-ad3b-5fc385bb4a46

Errors

[{"error":"Error: NON-FATAL: Lock acquisition failed for /Users/dimfeld/.local/share/claude/versions/2.1.74 (expected in multi-process scenarios)\n    at kvT (/$bunfs/root/src/entrypoints/cli.js:2644:2025)\n    at Cbq (/$bunfs/root/src/entrypoints/cli.js:2644:1169)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-03-13T08:17:08.275Z"},{"error":"Error: File does not exist. Note: your current working directory is /Users/dimfeld/Documents/cc/code4.\n    at call (/$bunfs/root/src/entrypoints/cli.js:1466:6033)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-03-13T08:18:46.737Z"},{"error":"Error: Request was aborted.\n    at rJT (/$bunfs/root/src/entrypoints/cli.js:1129:67346)\n    at next (native:1:11)\n    at i59 (/$bunfs/root/src/entrypoints/cli.js:6209:6811)\n    at next (native:1:11)\n    at a59 (/$bunfs/root/src/entrypoints/cli.js:6214:9174)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-03-13T08:18:47.893Z"}]

Original prompt below though note the file reference so it may not fully reproduce here.

<details>
<summary>The Prompt</summary>

Multi-Agent Orchestration Instructions

You are the orchestrator for a tim multi-agent development workflow. tim is a tool for managing step-by-step project plans. Your role is to coordinate between specialized subagents to complete the coding tasks described below.

Batch Task Processing Mode

You have been provided with multiple incomplete tasks from a project plan. Your responsibility is to:

  1. Analyze all provided tasks to understand their scope, dependencies, and relationships
  2. Select a logical subset of tasks that make sense to execute together in this batch.

You are permitted to implement tasks from different Areas together.

  1. Execute the selected tasks using the specialized agents
  2. Update the plan file to document your work
  3. Mark the tasks done.

If existing work has been done on the plan, you can find it described in the "# Implementation Notes" section of the plan file's details field.

Task Selection Guidelines

When selecting which tasks to batch together, consider:

  • Related functionality: Tasks that work on similar features or components
  • Shared files: Tasks that modify the same files or modules
  • Logical dependencies: Tasks where one naturally builds upon another
  • Efficiency: Tasks that can reuse context or setup work
  • Reasonable scope: Select a single task or 2-5 related tasks rather than attempting all tasks at once.

IMPORTANT: Do not attempt to complete all tasks in a single batch. Focus on a reasonable subset that can be completed thoroughly and tested properly.

Plan File Updates

After successfully completing your selected tasks, you MUST use the Edit tool to update the plan file at: @/Users/dimfeld/Documents/cc/code4/tasks/522-issue-df-2348-action-definitions-and-permission-settings.plan.md

For each completed task, update the YAML structure by setting done: true. Find each task item using the title. Here's an example:

tasks:
  - title: "Implement user authentication"
    done: true  # Already completed
    description: "Add login/logout functionality"
    
  - title: "Add password validation"
    # Add done: true here if this has been completed
    description: "Implement password strength checking"

CRITICAL: Only mark tasks as done: true after they have been successfully implemented, tested, and reviewed. Do not mark tasks as done if:

  • Implementation failed or is incomplete
  • Tests are failing
  • Code review identified blocking issues

You don't need to mark the entire plan file as complete. We will handle that for you. But if you do, you must use 'status: done'

Available Agents

You have access to two specialized agents that you MUST invoke via the Bash tool:

  • Implementer: Run tim subagent implementer 522 --input "<instructions>" via the Bash tool (or --input-file <paths...>)
  • Tester: Run tim subagent tester 522 --input "<instructions>" via the Bash tool (or --input-file <paths...>)

Code reviews are performed by running tim review (not a subagent). You can pass additional context to the reviewer via --input-file <paths...>.

Each subagent command may take a long time to complete. Always use a timeout of at least 1800000 ms (30 minutes) when invoking them via the Bash tool.

Output capture requirement for every tim subagent run:

  • Always pass --output-file <path> and use the full path, not $TMPDIR/<filename>
  • Use a temp-dir file name that includes the plan ID and agent name to avoid collisions with other concurrent agents/projects.
  • Recommended pattern: tim-522-<agent>-output-XXXXXX.txt.
  • If the command returns no stdout/stderr output, immediately read the --output-file path and use that as the subagent output.

Output capture requirement for every tim review run:

  • Always pass --output-file <path> and use the full path, not $TMPDIR/<filename>
  • Use a temp-dir file name that includes the plan ID (for example tim-522-review-output-XXXXXX.txt) to avoid collisions with other concurrent agents/projects.
  • If the command returns no stdout/stderr output, immediately read the --output-file path and use that as the review output.

Subagent Executor Selection

You must choose which executor to use for each subagent invocation by passing -x codex-cli or -x claude-code to the tim subagent command.

Decision guidance: Prefer claude-code for UI tasks, codex-cli for everything else.

Workflow Instructions

You MUST follow this iterative development process:

  1. Task Selection Phase
  • First, analyze all provided tasks and select a logical subset to work on
  • Document your selection and reasoning before proceeding
  • Focus on 2-5 related tasks that can be completed together efficiently
  1. Implementation Phase
  • Run tim subagent implementer 522 --input "<instructions>" via the Bash tool with a timeout of at least 1800000 ms (30 minutes)
  • Choose the appropriate executor (-x claude-code or -x codex-cli) based on the executor selection guidance above.
  • Always include --output-file with a plan-specific temp file path for this command.
  • If command output is empty, read the output file you passed to --output-file and treat it as the subagent result.
  • In the input (--input or --input-file), specify which tasks to work on and provide relevant context
  • Wait for the subagent to complete and review its output
  1. Testing Phase
  • After implementation is complete, run tim subagent tester 522 --input "<instructions>" via the Bash tool with a timeout of at least 1800000 ms (30 minutes)
  • Choose the appropriate executor (-x claude-code or -x codex-cli) based on the executor selection guidance above.
  • Always include --output-file with a plan-specific temp file path for this command.
  • If command output is empty, read the output file you passed to --output-file and treat it as the subagent result.
  • In the input (--input or --input-file), ask the tester to create comprehensive tests for the implemented functionality, if needed
  • Emphasize that tests must test actual implementation code. Testing a reproduction or simulation of the code is useless.
  • In the input, instruct the tester to run tests and fix any failures
  • Include relevant context from the implementer's output in the input
  1. Review Phase
  • Run tim review 522 --print --output-file <output_path> using the Bash tool.
  • Always include --output-file with a plan-specific temp file path for this command.
  • Pass your research context file and any relevant notes to the reviewer via --input-file <paths...> so it has the full picture of what was intended and why.
  • If command output is empty, read the output file you passed to --output-file and treat it as the review result.
  • Scope the review to the tasks you worked on using --task-index (1-based). Pass each task index separately: --task-index 1 --task-index 3 for tasks 1 and 3.
  • The review command may take up to 15 minutes; use a long timeout.
  • The review output focuses on problems; don't expect positive feedback even if the code is perfect.
  1. Notes Phase

Progress Updates (Plan File)

Update the plan file at: @/Users/dimfeld/Documents/cc/code4/tasks/522-issue-df-2348-action-definitions-and-permission-settings.plan.md

After each successful iteration (and again at the end of the run), update the plan file's ## Current Progress section:

  • Create the section at the end of the file if it does not exist (keep it outside any generated delimiters).
  • Update in place: edit or replace outdated text so the section reflects current reality while preserving meaningful history.
  • No timestamps anywhere in the section.
  • Focus on what changed, why it changed, and what's next. Omit testing/review info, focus on what's useful to remember when starting the next task.
  • Only remove information if it no longer applies. For example, decisions from previous tasks should remain unless they have been changed.
  • In ### Lessons Learned, capture surprises, gotchas, non-obvious fixes, and what was learned from review feedback or review-fix iterations. Only include lessons that will actually be useful in the future, not things that are just specific only to the work just performed in this plan.

Use this structured template (fill every heading; use "None" when empty):

Current Progress

Current State

  • ...

Completed (So Far)

  • ...

Remaining

  • ...

Next Iteration Guidance

  • ...

Decisions / Changes

  • ...

Lessons Learned

  • Record surprises, unexpected issues, non-obvious solutions, workarounds, or undocumented insights.
  • Especially note what you learned while fixing review feedback and why the issue occurred, if the insights are applicable to future tasks and not just this one.
  • Update this subsection in place. Keep ongoing lessons that still matter; remove only stale lessons.
  • None

Risks / Blockers

  • None
  1. Iteration
  • If the review output identifies issues or tests fail:
  • For straightforward review follow-ups that are easy to implement correctly (for example wording tweaks, small logic adjustments, or similarly contained edits), you may apply the changes yourself without spawning the implementer subagent.
  • Return to step 2 when substantial code changes are required.
  • After implementing straightforward follow-up changes, run the relevant targeted checks yourself. If the entire set of changes is trivial and guaranteed to not introduce any bugs (e.g. just wording changes), you may skip re-running tim review 522 --print --output-file <output_path>. If in doubt, run another review; even simple changes can sometimes have unintended downstream effects.
  • If the review still flags an issue that was supposedly just fixed, trust the review — the fix was incomplete or incorrect. Investigate the issue again rather than dismissing the feedback.
  • Continue this loop until all tests pass and the implementation is satisfactory

Important Guidelines

  • DO NOT implement code directly. Always delegate implementation tasks to the appropriate subagent via tim subagent.
  • DO NOT write tests directly. Always use the tester subagent via tim subagent tester for test execution and updates.
  • DO NOT review code directly. Always run tim review 522 --print --output-file <output_path> for code quality assessment.
  • Exception: if review feedback requires only straightforward, contained edits, you may apply those edits directly instead of spawning implementer again.
  • After straightforward review follow-ups, run focused verification yourself. If the scope remains straightforward and verification is clear, you may skip re-running tim review 522 --print --output-file <output_path>.
  • You are responsible only for coordination and ensuring the workflow is followed correctly.
  • The subagents have access to the same task instructions below that you do, so you don't need to repeat them. You should reference which specific task titles are being worked on so the subagents can focus on the right tasks.
  • When invoking subagents, provide clear, specific instructions in --input (or --input-file) about what needs to be done in addition to referencing the task titles.
  • - You can use both --input-file and --input together. --input-file is read first and --input is appended afterward.
  • Include relevant context from previous subagent responses when invoking the next subagent.
  • - If input is large (roughly over 50KB), write it to a temporary file in a temp directory (for example, /tmp or a mktemp path) and pass --input-file <paths...> instead of --input.
  • When you create an input file for a subagent or reviewer, include the plan ID plus an extra random suffix in the filename so repeated runs on the same plan do not collide with earlier files.
  • Recommended pattern: /tmp/tim-522-<purpose>-XXXXXX.md.
  • Always explicitly pass the full path instead of using "$TMPDIR/filename".
  • You can also pipe input to stdin and use --input-file -.

Sharing Research with Subagents

Before invoking a subagent, write a research context file that includes everything you have learned so far that is relevant to the subagent's work. This saves the subagent from needing to repeat your exploration. The file should contain:

  • Relevant code snippets or file locations you discovered
  • Architectural decisions or patterns you identified
  • Dependencies, constraints, or gotchas you found
  • Any analysis of the existing codebase that informs the implementation

Write this research to a file with a randomized, plan-scoped name (for example, /tmp/tim-522-research-XXXXXX.md) and reference it in the subagent's --input-file, along with your task-specific instructions. Update the file as you learn more across iterations.
When invoking tim review 522 --print --output-file <output_path>, also pass your research and any relevant context via --input-file <paths...> so the reviewer has the full picture of what was intended and why. Use the same randomized filename approach for any reviewer input files you create.

Plan Documentation During Implementation

If you or a subagent discover that the plan needs to change during implementation (e.g. the approach needs to differ from what was planned, a task needs to be split or reordered, new tasks are discovered, or requirements turn out to be different than expected):

  1. Update the plan text itself to reflect the change. Modify the relevant task descriptions, details, or add new tasks as needed so the plan file always represents the current state of the work.
  2. Document the change in a ## Changes Made During Implementation section at the bottom of the plan file's markdown body (before any ## Current Progress section). Each entry should briefly explain what changed and why. This prevents reviewers from getting confused by discrepancies between the plan and the actual implementation.

Instruct subagents to report any plan changes they believe are necessary in their output, so you can make the updates.

Failure Protocol (Conflicting/Impossible Requirements)

  • Monitor all subagent outputs (implementer, tester) and the tim review output for a line starting with "FAILED:".
  • If any subagent or tim review emits a FAILED line, you MUST stop orchestration immediately.
  • Output a concise failure message and propagate details:
  • First line: FAILED: <agent> reported a failure — <1-sentence summary>
  • Where <agent> is one of: implementer | tester | fixer | review
  • Then include the subagent's detailed report verbatim (requirements, problems, possible solutions).
  • Do NOT proceed to further phases or mark tasks done after a failure.
  • You may add brief additional context if necessary (e.g., which tasks were being processed).
  • Subagents will have access to the entire list of incomplete tasks from the plan file, so be sure to include which tasks to focus on in your subagent instructions.
  • Be selective: Don't attempt all tasks at once - choose a reasonable subset that works well together and prefer to choose smaller subsets.

Marking Tasks Done

Only perform the following if no subagent failure occurred during this run.
If any agent emitted a line beginning with 'FAILED:', do not run any of the following commands — stop immediately.

When updating tasks after successful implementation, testing, and review, use the Bash command 'tim set-task-done 522 --title "<taskTitle>"'.
To set Task 2 done for plan 165, use 'tim set-task-done 165 --title "do it"'. To set multiple tasks done, run the command multiple times for each task.

After marking tasks done, commit your changes with a descriptive message about what tasks were completed. Do not include attribution comments in the commit message.

Progress Updates (Plan File)

Update the plan file at: @/Users/dimfeld/Documents/cc/code4/tasks/522-issue-df-2348-action-definitions-and-permission-settings.plan.md

After each successful iteration (and again at the end of the run), update the plan file's ## Current Progress section:

  • Create the section at the end of the file if it does not exist (keep it outside any generated delimiters).
  • Update in place: edit or replace outdated text so the section reflects current reality while preserving meaningful history.
  • No timestamps anywhere in the section.
  • Focus on what changed, why it changed, and what's next. Omit testing/review info, focus on what's useful to remember when starting the next task.
  • Only remove information if it no longer applies. For example, decisions from previous tasks should remain unless they have been changed.
  • In ### Lessons Learned, capture surprises, gotchas, non-obvious fixes, and what was learned from review feedback or review-fix iterations. Only include lessons that will actually be useful in the future, not things that are just specific only to the work just performed in this plan.

Use this structured template (fill every heading; use "None" when empty):

Current Progress

Current State

  • ...

Completed (So Far)

  • ...

Remaining

  • ...

Next Iteration Guidance

  • ...

Decisions / Changes

  • ...

Lessons Learned

  • Record surprises, unexpected issues, non-obvious solutions, workarounds, or undocumented insights.
  • Especially note what you learned while fixing review feedback and why the issue occurred, if the insights are applicable to future tasks and not just this one.
  • Update this subsection in place. Keep ongoing lessons that still matter; remove only stale lessons.
  • None

Risks / Blockers

  • None

Task Context

Below is the original task that needs to be completed through this multi-agent workflow:

---

@/Users/dimfeld/Documents/cc/code4/tasks/522-issue-df-2348-action-definitions-and-permission-settings.plan.md

Project Details:

Action Definitions and Permission Settings

Define the data model and schema for agent actions:

  • Create a registry/table of all possible agent actions
  • Permission settings per action type:
  • Confidence-based: if the action has a confidence score, set a minimum confidence level (low, medium, or high) to auto-approve; require approval below that level
  • Always allow: no approval needed
  • Require approval: always needs human sign-off
  • These settings should be configurable per organization

This is the foundational schema that the rest of the system builds on.

Research

Overview

This plan establishes the foundational data model for agent actions: a code-based registry of action type definitions and a database table for per-organization permission settings that control whether each action type requires human approval, is auto-approved, or uses confidence-based thresholds.

Key Findings

1. Registry Pattern (Code-Based)

The codebase has a well-established pattern for type-safe registries, most notably the Checklist Criteria Registry (modules/checklists/src/criteria/registry.ts). This pattern uses:

  • A Map<string, RegisteredDefinition> keyed by a string type identifier
  • A factory function (e.g., defineChecklistCriteria()) that both defines and auto-registers
  • Zod schema validation for configuration data
  • Type-safe lookup with getRegistered*(type) functions

Other registries following similar patterns:

  • Tool Registry (libs/agents/src/tools/registry.ts) — for agent tools with category classification
  • Finish Handler Registry (modules/checklists/src/finish-handlers/registry.ts)
  • Webhook Integration Registry (modules/external-integrations/src/services/incoming-webhooks/registry.ts)

The action type registry should follow this same pattern: definitions live in code, not the database.

2. Existing Agent Framework

The agent framework is already well-developed:

  • Agent Tools (libs/agents/src/tools/types.ts) have a category field with values: read_only, local_write, business_write, control
  • Business writes already go through a specific execution path via UserWriteVO (Restate Virtual Object) that serializes writes per user
  • ToolDefinition includes paramsSchema (Zod), resultSchema, idempotent, requiresIdempotencyKey flags
  • The ToolRegistry class has getBusinessWriteToolNames() which returns a ReadonlySet of business_write tools

The action type registry is decoupled from the tool registry. Not every business write requires an action type (some writes are pre-approved within their flow), and a single action type could potentially involve multiple writes or come from a non-agent source. The action type registry is a separate concept that the agent framework integration (plan 525) will wire into the business write path when appropriate.

3. Database Schema Patterns

Key patterns from the existing schema:

  • Organization-scoped tables use organizationReference() from libs/db/src/schema/common.ts with CASCADE delete
  • Audit fields via auditFields (createdAt, updatedAt, active) from common.ts
  • ID generation uses UUIDv7 via idColumn from common.ts
  • pgEnum is used for database-level enums (e.g., inventoryTypeEnum, salesAccountStatusEnum)
  • JSONB columns with $type<T>() are used for typed settings (e.g., organizations.settings)
  • Unique indexes commonly use (organizationId, typeKey) patterns
4. Permission System

Permissions are role-based (libs/auth/src/permissions/):

  • Permissions are defined as string IDs in libs/auth/src/permissions/permissions.ts (170+ permissions)
  • Organized by PermissionCategory (e.g., BUNDLE, TICKETS, ORDER_REQUESTS)
  • Some permissions imply others (explicit vs internal implications)
  • Checked via checkPermission() or .anyPermission() / .allPermissions() on serviceMiddleware

New permissions will be needed:

  • Managing action permission settings (what approval mode each action type uses)
  • The actual approval/denial of action instances (covered by plan 524/526)
5. Organization Settings Patterns

Two approaches exist in the codebase:

A. JSONB on organizations table (settingsTypes.ts): Used for simple key-value settings. Good for boolean flags and simple config. Not suitable here because action permission settings are per-action-type and benefit from individual row management.

B. Separate configuration tables: Used for more complex configurations like checklistDefaultAssignees, featureFlags, notificationSettings. These have their own tables with organizationId + key columns.

The per-action-type permission settings fit pattern B — a dedicated table with (organizationId, actionType) as the key.

6. Service Middleware

Services use the serviceMiddleware builder from @libs/service_middleware:

export const myService = serviceMiddleware('domain.action')
  .input(z.object({ ... }))
  .anyPermission(['some:permission'])
  .wrap(async ({ context, input }) => { ... });

Key methods: .input(), .anyPermission(), .allPermissions(), .allowNoUser(), .featureFlag(), .wrap().

ServiceContext provides: db, user, organization, permissions, flags, reportError, restateClient, s3Client, triggerChecklistRefresh.

7. Migration Conventions
  • Located in libs/db/drizzle/
  • Named with timestamp + random name (e.g., 20260309160912_rolling_ptarmigan)
  • Generated via pnpm run generate-migrations in libs/db
  • Each has up.sql and down.sql
8. Default Assignee Grouping

The parent plan mentions wanting group-based default assignee assignment (e.g., "all bill-only approvals assigned to group A"). The checklist system already supports typeKey-based default assignees via checklistDefaultAssignees table. Plan 524 will handle the checklist integration, but this plan should ensure action types have a category or group field that can be used for grouping default assignees.

9. Confidence Scores

Confidence scores exist in the codebase but are not standardized:

  • Bill-only import has extraction metadata with confidence
  • Agent order form processing uses confidence-based decision matrices
  • No centralized confidence score type definition exists

The permission settings table needs a confidenceLevel column using a low | medium | high enum, only used when approvalMode = 'confidence_based'. The agent also produces confidence as low | medium | high — no numeric values involved. resolveApprovalMode compares the agent's confidence level against the configured minimum level (low < medium < high) to decide whether approval is needed.

10. Audit Logging

The audit events table (libs/db/src/schema/eventsSchema.ts) records:

  • eventType, action, objectId, secondaryObjectId, payload (JSONB)
  • userId, organizationId, route, url, traceId

Decision logging (libs/modules_shared/src/decision-logging.ts) should be used when the system decides whether an action needs approval based on the permission settings.

Critical Files Reference

| File | Relevance |
|------|-----------|
| libs/db/src/schema/common.ts | Reusable column definitions (idColumn, organizationReference, auditFields) |
| libs/db/src/schema/settingsTypes.ts | Settings type patterns |
| libs/db/src/schema/eventsSchema.ts | Audit event schema |
| libs/auth/src/permissions/permissions.ts | Permission definitions — add new permissions here |
| libs/auth/src/permissions/get.ts | Permission checking functions |
| libs/agents/src/tools/types.ts | ToolDefinition with category field |
| libs/agents/src/tools/registry.ts | ToolRegistry class |
| modules/checklists/src/criteria/registry.ts | Registry pattern to follow |
| modules/checklists/src/criteria/define.ts | Factory function pattern to follow |
| modules/checklists/src/services/default-assignee-service.ts | Default assignee pattern |
| libs/service_middleware/src/serviceMiddleware.ts | Service middleware builder |
| libs/modules_shared/src/decision-logging.ts | Decision logging for conditional actions |

---

Implementation Guide

Expected Behavior/Outcome

After this plan is complete:

  1. Action Type Registry: A code-based registry exists where each agent action type is defined with its name, description, category/group, and whether it supports confidence scores. New action types can be added by calling a factory function that auto-registers.
  1. Permission Settings Table: A database table (agent_action_permission_settings) stores per-organization permission settings for each action type. Each row specifies an approval mode: always_allow, require_approval, or confidence_based (with a confidence level).
  1. Default Settings: Each action type definition includes a default approval mode that applies when no organization-specific override exists.
  1. Service Functions: Service functions exist to get/set permission settings, resolve the effective approval mode for a given action + organization (considering defaults), and query available action types.
  1. Permission Gate: A new permission (agent_actions:manage_settings) controls who can modify action permission settings.

Acceptance Criteria

  • [ ] Action type registry exists with factory function for defining new types
  • [ ] At least one action type is registered (for the bill-only email metadata update use case from plan 527)
  • [ ] Database migration creates agent_action_permission_settings table with proper indexes
  • [ ] Service functions exist: getActionPermissionSettings, setActionPermissionSettings, resolveApprovalMode
  • [ ] resolveApprovalMode correctly falls back to the action type's default when no org-specific setting exists
  • [ ] Permission agent_actions:manage_settings is defined and required for modifying settings
  • [ ] Decision logging is used when resolving approval mode
  • [ ] All new code paths are covered by integration tests
  • [ ] pgEnums agent_action_approval_mode and agent_action_confidence_level are created
  • [ ] Settings UI page exists at /settings/automation/agent-actions showing all action types grouped by category
  • [ ] Settings UI allows changing approval mode and confidence level per action type, with reset-to-default

Dependencies & Constraints

  • Dependencies: Uses existing patterns from checklist criteria registry, service middleware, permission system
  • Technical Constraints: Must be organization-scoped. Must not break existing agent tool registry. Action type keys must be stable strings that can be referenced by other plans (523, 524, 525).
  • Downstream Consumers: Plans 523 (action instances), 524 (checklist integration), 525 (agent framework integration) all depend on the action type registry and permission settings from this plan.

Step-by-Step Implementation

Step 1: Define the Action Type Registry Types

Create a new module modules/agent_actions/ following the standard module structure. Start with the type definitions.

Create modules/agent_actions/src/types/action-types.ts:

  • Define AgentActionTypeDefinition interface:
  • type: string — unique key (e.g., 'bill_only.email_metadata_update')
  • name: string — human-readable name
  • description: string — what this action does
  • category: AgentActionCategory — grouping key used for both default assignee grouping and determining which settings page the action configurations appear in. Defined as a TypeScript union type (e.g., 'bill_only' | 'order_requests') for consistency, with new values added as new action types emerge.
  • supportsConfidence: boolean — whether this action type provides confidence scores
  • defaultApprovalMode: AgentActionApprovalMode — default when no org override exists
  • defaultConfidenceLevel?: AgentActionConfidenceLevel — default confidence level if mode is confidence-based ('low' | 'medium' | 'high')
  • dataSchema: ZodType — Zod schema describing the action's proposed write payload (editable by approvers during approval)
  • contextSchema?: ZodType — optional Zod schema for type-specific context fields that must be provided when creating an instance (e.g., orderId, locationId for bill-only actions). Plan 523's action instance table will also have common context fields (like sourceEmailId, sourceThreadId) available to all action types.
  • Define AgentActionApprovalMode as a union type: 'always_allow' | 'require_approval' | 'confidence_based'
  • Define AgentActionConfidenceLevel as a union type: 'low' | 'medium' | 'high'. Both the agent's output and the org setting use this same enum — no numeric values. resolveApprovalMode compares the agent's confidence level against the configured minimum (low < medium < high) to decide whether approval is needed.
  • Define AgentActionCategory as a TypeScript union type: initially 'bill_only', with new values added as action types are created. This type ensures consistency across action definitions and is used by both the default assignee system (plan 524) and the settings UI to group actions.

Reference the checklist criteria types at modules/checklists/src/criteria/types.ts for structural guidance.

Step 2: Create the Action Type Registry

Create modules/agent_actions/src/registry/registry.ts:

  • Follow the pattern from modules/checklists/src/criteria/registry.ts
  • Use a Map<string, AgentActionTypeDefinition> for storage
  • Export functions:
  • registerAgentActionType(definition) — registers a type, throws on duplicate
  • getAgentActionType(type) — returns definition or undefined
  • getAllAgentActionTypes() — returns all registered types
  • getAgentActionTypesByCategory(category) — returns types filtered by category

Create modules/agent_actions/src/registry/define.ts:

  • Factory function defineAgentActionType(definition) that validates and auto-registers
  • Similar to defineChecklistCriteria() pattern
Step 3: Register the First Action Type

Create modules/agent_actions/src/action-types/bill-only-email-metadata-update.ts:

  • Use defineAgentActionType() to register the first action type
  • Type key: 'bill_only.email_metadata_update'
  • Category: 'bill_only'
  • supportsConfidence: true
  • defaultApprovalMode: 'confidence_based'
  • defaultConfidenceLevel: 'high'
  • Data schema: Define a Zod schema for the metadata fields that can be updated (PO number, etc.)
  • Context schema: Define a Zod schema for required context (e.g., orderId, locationId)

This provides a concrete test case and validates the registry design.

Step 4: Create Database Schema

Add to libs/db/src/schema/ — create agentActionSchema.ts:

Create the pgEnums:

export const agentActionApprovalModeEnum = pgEnum('agent_action_approval_mode', [
  'always_allow',
  'require_approval',
  'confidence_based',
]);

export const agentActionConfidenceLevelEnum = pgEnum('agent_action_confidence_level', [
  'low',
  'medium',
  'high',
]);

Create the permission settings table:

export const agentActionPermissionSettings = pgTable('agent_action_permission_settings', {
  ...idColumn,
  ...organizationReference(organizations),
  actionType: text('action_type').notNull(),
  approvalMode: agentActionApprovalModeEnum('approval_mode').notNull(),
  confidenceLevel: agentActionConfidenceLevelEnum('confidence_level'),  // nullable, only used when approvalMode = 'confidence_based'
  ...auditFields,
}, (table) => [
  uniqueIndex('agent_action_permission_settings_org_type_idx')
    .on(table.organizationId, table.actionType),
]);

Add relations in the same file linking to organizations.

Export from libs/db/src/schema/index.ts.

Step 5: Generate Migration

Run pnpm run generate-migrations in libs/db to generate the migration SQL. Verify the generated SQL creates the enum and table correctly.

Step 6: Add Permission Definition and Feature Flag

Edit libs/auth/src/permissions/permissions.ts:

  • Add a new PermissionCategory: AGENT_ACTIONS
  • Add permission: 'agent_actions:manage_settings' — allows modifying action permission settings for the organization
  • Add to the appropriate standard roles (likely Administrator)

Edit libs/auth/src/featureflags/flags.ts:

  • Add feature flag: agent_actions with default: false, use: 'access', active: true
  • This flag gates only the settings UI, not the backend services or registry (so that the agent framework can use resolveApprovalMode regardless of flag state)
Step 7: Create Service Functions

Create modules/agent_actions/src/services/permission-settings-service.ts:

Using serviceMiddleware pattern:

  1. getActionPermissionSettings — Fetches all permission settings for an organization
  • Uses ServiceContext organization
  • Permission: read-only, any authenticated user
  • Returns: array of settings rows joined with action type metadata from registry
  1. setActionPermissionSetting — Upserts a single action type's permission setting
  • Input: { actionType, approvalMode, confidenceLevel? }
  • Permission: agent_actions:manage_settings
  • Validates: actionType exists in registry, confidenceLevel required iff mode is confidence_based, must be a valid AgentActionConfidenceLevel
  • Uses ON CONFLICT (organizationId, actionType) DO UPDATE
  • Logs audit event
  1. resolveApprovalMode — Determines whether an action needs approval
  • Input: { actionType, agentConfidence?: AgentActionConfidenceLevel }agentConfidence is required when the action type has supportsConfidence: true
  • Uses ServiceContext organization (no explicit org ID needed)
  • Uses .allowNoUser() since this will be called from agent/workflow contexts without a user session
  • Looks up org-specific setting; falls back to registry default
  • For confidence_based mode: compares agentConfidence against the configured minimum level (low < medium < high). If the agent's confidence meets or exceeds the threshold, no approval is needed.
  • Uses logDecision to record the resolution (both the setting used and the outcome)
  • Returns: { needsApproval: boolean, approvalMode, confidenceLevel?, source: 'organization' | 'default' }
  1. deleteActionPermissionSetting — Removes org-specific override, reverting to default
  • Input: { actionType }
  • Permission: agent_actions:manage_settings
  • Logs audit event
Step 8: Create Module Package Structure

Set up the standard module package:

  • modules/agent_actions/package.json — with exports map for services, types, etc.
  • modules/agent_actions/src/index.ts — barrel export
  • modules/agent_actions/tsconfig.json
  • modules/agent_actions/vitest.config.ts (if needed, check other modules)

Reference modules/inventory/ or modules/organizations/ for the standard structure.

Run pnpm install after creating the package to ensure monorepo linking.

Step 9: Write Integration Tests

Create modules/agent_actions/src/services/permission-settings-service.integration.test.ts:

Using PostgresTestEnvironment:

  1. Test setActionPermissionSetting:
  • Sets a new permission setting and verifies it's stored
  • Updates an existing setting and verifies the change
  • Rejects invalid action types (not in registry)
  • Rejects confidence_based mode without a confidence level
  • Rejects invalid confidence level values
  • Requires agent_actions:manage_settings permission
  1. Test getActionPermissionSettings:
  • Returns all settings for the organization
  • Returns empty array when no settings exist
  • Includes action type metadata from registry
  1. Test resolveApprovalMode:
  • Returns org-specific setting when it exists
  • Falls back to registry default when no org setting exists
  • For always_allow: returns needsApproval: false
  • For require_approval: returns needsApproval: true
  • For confidence_based with agent confidence >= configured level: returns needsApproval: false
  • For confidence_based with agent confidence < configured level: returns needsApproval: true
  • Decision logging is called in all cases
  1. Test deleteActionPermissionSetting:
  • Removes the setting
  • resolveApprovalMode returns default after deletion

Create modules/agent_actions/src/registry/registry.test.ts:

Unit tests for the registry:

  1. Registers and retrieves action types
  2. Throws on duplicate registration
  3. Filters by category
  4. Returns undefined for unknown types
Step 10: Settings UI — Add Navigation and Route

Add agent action settings to the existing settings navigation and create the route structure.

Edit apps/inbox/src/routes/settings/+layout.svelte:

  • Add a new nav item under the Automation section for "Agent Actions" (or integrate into existing automation pages by category)
  • Gate visibility behind agent_actions:manage_settings permission AND agent_actions feature flag
  • Link to /settings/automation/agent-actions

Create apps/inbox/src/routes/settings/automation/agent-actions/:

  • +page.server.ts — Load function that:
  • Requires agent_actions feature flag and agent_actions:manage_settings permission
  • Calls getActionPermissionSettings() to get org-specific overrides
  • Calls getAllAgentActionTypes() from the registry to get all available action types with their defaults
  • Returns both so the UI can show current effective settings for each action type
  • Adds breadcrumb
Step 11: Settings UI — Remote Functions and Schema

Create apps/inbox/src/routes/settings/automation/agent-actions/agent-actions.types.ts:

  • Zod schema for the settings form: actionType (string), approvalMode (enum), confidenceLevel (optional enum)

Create apps/inbox/src/routes/settings/automation/agent-actions/agent-actions.remote.ts:

  • form() handler for updating a single action type's permission setting
  • Wraps with remoteGuard().anyPermission(['agent_actions:manage_settings']).wrapForm()
  • Calls setActionPermissionSetting() service
  • Returns success message via withFormMeta()
  • command() handler for resetting to default (calls deleteActionPermissionSetting())
Step 12: Settings UI — Page Component

Create apps/inbox/src/routes/settings/automation/agent-actions/+page.svelte:

Following the existing automation settings page patterns:

  • Use PageHeader with title "Agent Action Settings"
  • Group action types by category using Card.Root per category
  • For each action type within a category, show:
  • Action name and description
  • Current approval mode (dropdown/select: Always Allow, Require Approval, Confidence-Based)
  • If confidence-based: confidence level selector (Low, Medium, High)
  • Whether the current setting is the default or an org override (visual indicator)
  • Reset-to-default button when an override exists
  • Use FormManager per action type row (each is an independent form)
  • Wrap in <fieldset disabled={!data.canManageSettings}> for read-only when user lacks permission
  • Show Form.FormLevelErrors and Form.FormMessage for feedback
Step 13: Verify Quality

Run from the module directory:

  • pnpm test — all tests pass
  • pnpm check — TypeScript compiles
  • pnpm run lint — no lint errors
  • turbo run semgrep — no semgrep violations

Implementation Notes

  • Recommended Approach: Code-based registry for action type definitions (like checklist criteria) + database table for per-org permission settings. This gives type safety for definitions while allowing runtime configuration.
  • Potential Gotchas:
  • The registry must be populated before any service functions reference it. Ensure auto-registration happens on module load (follow checklist criteria pattern where imports trigger registration).
  • The confidenceLevel column uses a pgEnum (agent_action_confidence_level). Both the org setting and the agent's output use the same low | medium | high enum — no numeric values anywhere.
  • When adding the new schema file, ensure it's exported from libs/db/src/schema/index.ts so Drizzle picks it up for migration generation.
  • The actionType column in the settings table is a text referencing the registry key, not a foreign key — this is intentional since definitions are code-based.
  • Why not a database table for action type definitions?: Action types are tightly coupled to code (they have Zod schemas, execution logic). A code-based registry ensures type safety and avoids sync issues. The database table is only for the configurable parts (approval settings per org).

Current Plan Context

Current Plan File: tasks/522-issue-df-2348-action-definitions-and-permission-settings.plan.md
Current Plan Title: Action definitions and permission settings

Parent Plan Context

Parent Plan File: tasks/521-issue-df-2346-agent-actions-and-approvals-system.plan.md
Parent Plan: Agent actions and approvals system (ID: 521)
Parent Details: We should be able to define a big list of actions that agents can do. There should be permission settings around these actions, which can be based on the confidence if the action has a confidence and otherwise is just require approval or always allow. Actions that require approval should be able to be approved or denied or, when applicable, have the data edited. Approval should also have an optional note.

Actions that require approval should have related checklist items so that people can see what is waiting for approval. These checklist items should be individually and have default assignees, like the current system allows. We should also have a way to assign groups of them to have defaults. For example all bill-only approvals may be assigned to a certain group while all order request approvals might be assigned to a different group or something like that.

The table of the actual instances of actions should record:

  • the decision and the notes on the decision
  • the original and edited data

We'll also need to have links back to things such as the source email or other information, depending on the item type. The UI for handling these actions should show all the related data, for example showing the content and relevant addresses and subject of the email.

Integrate this into the agent framework so that whenever it tries to do a business write that links into the agent action that is appropriate. If the action requires approval then the agent will stall and wait for the approval. The framework should make it easy to resume and either finish the write or handle the decline and then continue in whatever way is appropriate, whether that's an actual agent model continuing or just a state machine handling it and going to a different state.

The first instance of this will be for a simple agent that will parse an email looking for updated metadata for bill-only orders and then try to trigger a business write to do the updates.

I think business writes may always be linked to users right now; we will want to remove that requirement if so.

Sibling Plans (Same Parent)

These are other plans that are part of the same parent plan. Reference them for additional context about the overall project structure.

Pending Sibling Plans:

  • Action instances data model (File: tasks/523-issue-df-2349-action-instances-data-model.plan.md)
  • Approval workflow with checklist integration (File: tasks/524-issue-df-2350-approval-workflow-with-checklist-integration.plan.md)
  • Agent framework integration for actions and approvals (File: tasks/525-issue-df-2351-agent-framework-integration-for-actions-and-approvals.plan.md)
  • Agent Action Approval UI (File: tasks/526-issue-df-2352-agent-action-approval-ui.plan.md)
  • First agent: bill-only order email metadata updates (File: tasks/527-issue-df-2353-first-agent-bill-only-order-email-metadata-updates.plan.md)

Remaining Tasks

Please select and complete a logical subset of the following incomplete tasks that makes sense to work on together.

IMPORTANT: It's better to choose a small number of closely related tasks that form an atomic unit than to take on too many at once. Focus on what can be completed well in a single iteration.

Available tasks:

Task 1: Create module package structure
Description: Set up the modules/agent_actions/ module following standard module structure. Create package.json with exports map (services, types, registry), src/index.ts barrel export, tsconfig.json, and vitest.config.ts. Reference modules/inventory/ or modules/organizations/ for the standard structure. Run pnpm install after to ensure monorepo linking. This should be done first so subsequent files have a home.

Task 2: Define action type registry types
Description: Create modules/agent_actions/src/types/action-types.ts with:

  • AgentActionApprovalMode union type: 'always_allow' | 'require_approval' | 'confidence_based'
  • AgentActionConfidenceLevel union type: 'low' | 'medium' | 'high' (used by both agent output and org settings — no numeric values)
  • AgentActionCategory union type: initially 'bill_only', extended as new action types are added
  • AgentActionTypeDefinition interface with fields: type (unique key), name, description, category: AgentActionCategory, supportsConfidence: boolean, defaultApprovalMode, defaultConfidenceLevel?, dataSchema: ZodType (proposed write payload, editable by approvers), contextSchema?: ZodType (type-specific context fields for instances, e.g. orderId, locationId)

Reference modules/checklists/src/criteria/types.ts for structural guidance.

Task 3: Create the action type registry
Description: Create modules/agent_actions/src/registry/registry.ts following the pattern from modules/checklists/src/criteria/registry.ts:

  • Use a Map<string, AgentActionTypeDefinition> for storage
  • Export functions: registerAgentActionType(definition) (throws on duplicate), getAgentActionType(type), getAllAgentActionTypes(), getAgentActionTypesByCategory(category)

Create modules/agent_actions/src/registry/define.ts:

  • Factory function defineAgentActionType(definition) that validates and auto-registers, similar to defineChecklistCriteria() pattern.

Task 4: Register the first action type (bill-only email metadata update)
Description: Create modules/agent_actions/src/action-types/bill-only-email-metadata-update.ts using defineAgentActionType():

  • Type key: 'bill_only.email_metadata_update'
  • Category: 'bill_only'
  • supportsConfidence: true
  • defaultApprovalMode: 'confidence_based'
  • defaultConfidenceLevel: 'high'
  • dataSchema: Zod schema for metadata fields that can be updated (PO number, etc.)
  • contextSchema: Zod schema for required context (e.g., orderId, locationId)

This provides a concrete test case and validates the registry design.

Task 5: Create database schema for permission settings
Description: Create libs/db/src/schema/agentActionSchema.ts with:

  • agentActionApprovalModeEnum pgEnum: ['always_allow', 'require_approval', 'confidence_based']
  • agentActionConfidenceLevelEnum pgEnum: ['low', 'medium', 'high']
  • agentActionPermissionSettings table: idColumn, organizationReference(organizations), actionType: text, approvalMode: agentActionApprovalModeEnum, confidenceLevel: agentActionConfidenceLevelEnum (nullable), auditFields
  • Unique index on (organizationId, actionType)
  • Relations linking to organizations

Export from libs/db/src/schema/index.ts. Then run pnpm run generate-migrations in libs/db and verify the generated SQL.

Task 6: Add permission definition and feature flag
Description: Edit libs/auth/src/permissions/permissions.ts:

  • Add PermissionCategory.AGENT_ACTIONS
  • Add permission 'agent_actions:manage_settings' for modifying action permission settings
  • Add to appropriate standard roles (Administrator)

Edit libs/auth/src/featureflags/flags.ts:

  • Add feature flag agent_actions with default: false, use: 'access', active: true
  • This flag gates only the settings UI, not backend services or registry

Task 7: Create service functions for permission settings
Description: Create modules/agent_actions/src/services/permission-settings-service.ts using serviceMiddleware pattern:

  1. getActionPermissionSettings — Fetches all permission settings for the ServiceContext organization. Read-only, any authenticated user. Returns settings rows joined with action type metadata from registry.
  1. setActionPermissionSetting — Upserts a single action type's setting. Input: { actionType, approvalMode, confidenceLevel? }. Permission: agent_actions:manage_settings. Validates actionType exists in registry, confidenceLevel required iff confidence_based. Uses ON CONFLICT DO UPDATE. Logs audit event.
  1. resolveApprovalMode — Determines whether an action needs approval. Input: { actionType, agentConfidence?: AgentActionConfidenceLevel }. Uses .allowNoUser() for agent/workflow contexts. Looks up org-specific setting, falls back to registry default. For confidence_based: compares agentConfidence >= configured level (low < medium < high). Uses logDecision. Returns { needsApproval: boolean, approvalMode, confidenceLevel?, source: 'organization' | 'default' }.
  1. deleteActionPermissionSetting — Removes org-specific override. Permission: agent_actions:manage_settings. Logs audit event.

Task 8: Write registry unit tests
Description: Create modules/agent_actions/src/registry/registry.test.ts with unit tests:

  1. Registers and retrieves action types
  2. Throws on duplicate registration
  3. Filters by category correctly
  4. Returns undefined for unknown types
  5. getAllAgentActionTypes() returns all registered types

Task 9: Write service integration tests
Description: Create modules/agent_actions/src/services/permission-settings-service.integration.test.ts using PostgresTestEnvironment:

  1. setActionPermissionSetting: sets new setting, updates existing, rejects invalid action types, rejects confidence_based without confidence level, requires permission
  2. getActionPermissionSettings: returns all settings, returns empty when none exist, includes action type metadata
  3. resolveApprovalMode: returns org-specific when exists, falls back to default, always_allow → needsApproval false, require_approval → needsApproval true, confidence_based with agent >= configured → needsApproval false, confidence_based with agent < configured → needsApproval true, decision logging called in all cases
  4. deleteActionPermissionSetting: removes setting, resolveApprovalMode returns default after deletion

Task 10: Settings UI — Add navigation and route
Description: Edit apps/inbox/src/routes/settings/+layout.svelte:

  • Add nav item under Automation section for 'Agent Actions'
  • Gate visibility behind agent_actions:manage_settings permission AND agent_actions feature flag
  • Link to /settings/automation/agent-actions

Create apps/inbox/src/routes/settings/automation/agent-actions/+page.server.ts:

  • Requires agent_actions feature flag and agent_actions:manage_settings permission
  • Calls getActionPermissionSettings() for org-specific overrides
  • Calls getAllAgentActionTypes() from registry for all available types with defaults
  • Returns both so UI can show current effective settings
  • Adds breadcrumb

Task 11: Settings UI — Remote functions and schema
Description: Create apps/inbox/src/routes/settings/automation/agent-actions/agent-actions.types.ts:

  • Zod schema for settings form: actionType (string), approvalMode (enum), confidenceLevel (optional enum)

Create apps/inbox/src/routes/settings/automation/agent-actions/agent-actions.remote.ts:

  • form() handler for updating a single action type's setting, wrapped with remoteGuard().anyPermission(['agent_actions:manage_settings']).wrapForm(), calls setActionPermissionSetting(), returns success via withFormMeta()
  • command() handler for resetting to default, calls deleteActionPermissionSetting()

Task 12: Settings UI — Page component
Description: Create apps/inbox/src/routes/settings/automation/agent-actions/+page.svelte following existing automation settings patterns:

  • PageHeader with title 'Agent Action Settings'
  • Group action types by category using Card.Root per category
  • Per action type: name, description, approval mode dropdown (Always Allow, Require Approval, Confidence-Based), confidence level selector (Low/Medium/High, shown only when confidence-based), default/override indicator, reset-to-default button when override exists
  • FormManager per action type row (independent forms)
  • <fieldset disabled={!data.canManageSettings}> for read-only without permission
  • Form.FormLevelErrors and Form.FormMessage for feedback

Task 13: Verify quality
Description: Run from the module directory and repo root:

  • cd modules/agent_actions && pnpm test — all tests pass
  • pnpm check — TypeScript compiles
  • pnpm run lint — no lint errors
  • turbo run semgrep — no semgrep violations
  • turbo run format — code formatted

Plan File

  • @tasks/522-issue-df-2348-action-definitions-and-permission-settings.plan.md: This is the plan file you can reference if you need to check the plan again.

Execution Guidelines

Understand the Codebase Context

Before implementing changes:

  • Examine existing patterns and conventions in the codebase
  • Look for similar implementations or components that can serve as examples
  • Understand the project structure and where your changes fit
  • Review any relevant tests to understand expected behavior

Track Your Progress

Create a TODO list to organize your work:

  • Break down the task into specific, actionable items
  • Include items for code changes, tests, and verification
  • Track which items are completed as you progress
  • Update the list if you discover additional work needed
  • Use the TodoWrite tool to maintain your TODO list.

Follow Best Practices

Ensure your implementation:

  • Matches the existing code style and patterns
  • Uses the same libraries and utilities already in the codebase
  • Follows the project's naming conventions
  • Maintains consistent error handling patterns
  • Includes appropriate type annotations

Verify Your Work

After implementing changes:

  • Run the build command to ensure compilation succeeds
  • Execute tests to verify functionality
  • Run linting tools to check code quality
  • Fix any issues before considering the task complete

Self-Review Checklist

Before marking the task as done, verify:

  • [ ] Changes align with the plan's goals and requirements
  • [ ] Code follows existing patterns in the codebase
  • [ ] All tests pass successfully
  • [ ] Linting checks pass without errors
  • [ ] No unnecessary files or debug code included
  • [ ] Changes are focused and don't include modifications to unrelated parts of the code

Remember: Quality is more important than speed. Take time to understand the codebase and verify your changes work correctly within the existing system.
</details>

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗