Skip to content

feat: implement task assignment subsystem with pluggable strategies#172

Merged
Aureliolo merged 2 commits intomainfrom
feat/task-assignment-integration-test
Mar 8, 2026
Merged

feat: implement task assignment subsystem with pluggable strategies#172
Aureliolo merged 2 commits intomainfrom
feat/task-assignment-integration-test

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

@Aureliolo Aureliolo commented Mar 8, 2026

Summary

  • Task assignment subsystem with three pluggable strategies (Manual, RoleBased, LoadBalanced) behind a TaskAssignmentStrategy protocol
  • TaskAssignmentService orchestrator that validates task status and delegates to a pluggable strategy
  • Frozen Pydantic models: AssignmentRequest, AssignmentResult, AssignmentCandidate, AgentWorkload with model validators (_validate_collections, _validate_selected_not_in_alternatives)
  • TaskAssignmentConfig added to RootConfig schema with strategy, min_score, max_concurrent_tasks_per_agent
  • Structured logging via 8 event constants in observability.events.task_assignment
  • Documentation updated: DESIGN_SPEC.md (§15.3 project structure), CLAUDE.md (engine package description), README.md (feature bullet)

Test plan

  • Unit tests for all 3 strategies (ManualAssignmentStrategy, RoleBasedAssignmentStrategy, LoadBalancedAssignmentStrategy)
  • Unit tests for assignment models (validators, frozen immutability, edge cases)
  • Unit tests for TaskAssignmentService (strategy resolution, delegation, unknown strategy error)
  • Unit tests for TaskAssignmentConfig (defaults, boundaries, NaN/Inf rejection)
  • Unit tests for error hierarchy (TaskAssignmentError, NoEligibleAgentError)
  • Unit tests for event constants (pin-assertions for all 8 constants)
  • Unit tests for STRATEGY_MAP registry (keys, types, immutability)
  • Unit tests for protocol conformance (isinstance checks)
  • Integration test for multi-agent assignment flow
  • Parametrized tests for workload distributions and inactive agent statuses
  • All 3804 tests pass, 96.60% coverage

Review coverage

Pre-reviewed by 10 agents (code-reviewer, python-reviewer, pr-test-analyzer, silent-failure-hunter, comment-analyzer, type-design-analyzer, logging-audit, resilience-audit, security-reviewer, docs-consistency). 25 findings triaged and addressed.

🤖 Generated with Claude Code

Closes #26
Closes #30

Add three assignment strategies (Manual, RoleBased, LoadBalanced) behind
a TaskAssignmentStrategy protocol, with a TaskAssignmentService
orchestrator. Includes frozen Pydantic models, structured logging events,
model validators, and comprehensive tests (unit + integration).

Pre-reviewed by 10 agents, 25 findings addressed.
Copilot AI review requested due to automatic review settings March 8, 2026 18:04
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 8, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 8, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 968bfdff-c666-4736-bb95-c44b502f3277

📥 Commits

Reviewing files that changed from the base of the PR and between 413b5aa and ddb34bb.

📒 Files selected for processing (9)
  • DESIGN_SPEC.md
  • src/ai_company/config/schema.py
  • src/ai_company/engine/assignment/models.py
  • src/ai_company/engine/assignment/protocol.py
  • src/ai_company/engine/assignment/service.py
  • src/ai_company/engine/assignment/strategies.py
  • tests/integration/engine/test_multi_agent_delegation.py
  • tests/unit/engine/test_assignment_models.py
  • tests/unit/engine/test_assignment_service.py

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Task Assignment subsystem with three pluggable strategies: designate specific agents (manual), match tasks by skills and seniority (role-based), or distribute workload evenly (load-balanced). Includes configurable strategy selection, score thresholds, and concurrent task limits.
  • Documentation

    • Updated README documenting the new Task Assignment feature and available strategies.

Walkthrough

Adds a pluggable Task Assignment subsystem to the engine: domain models, a TaskAssignmentStrategy protocol, TaskAssignmentService orchestrator, three strategies (manual, role-based, load-balanced), config/schema wiring, new errors and observability events, and extensive unit/integration tests.

Changes

Cohort / File(s) Summary
Documentation & Config
CLAUDE.md, DESIGN_SPEC.md, README.md, src/ai_company/config/defaults.py, src/ai_company/config/schema.py
Documented task assignment feature; added task_assignment default config key and new TaskAssignmentConfig in schema (strategy, min_score, max_concurrent_tasks_per_agent).
Assignment Public API
src/ai_company/engine/__init__.py, src/ai_company/engine/assignment/__init__.py
New public exports for assignment subsystem (models, strategies, service, protocol, constants) re-exported from engine package.
Domain Models
src/ai_company/engine/assignment/models.py
Added frozen Pydantic models: AgentWorkload, AssignmentCandidate, AssignmentRequest (collection validators), and AssignmentResult (duplication validator).
Strategy Protocol & Service
src/ai_company/engine/assignment/protocol.py, src/ai_company/engine/assignment/service.py
Introduced TaskAssignmentStrategy protocol and TaskAssignmentService orchestrator enforcing status checks, lifecycle logging, and exception handling.
Assignment Strategies
src/ai_company/engine/assignment/strategies.py
Implemented ManualAssignmentStrategy, RoleBasedAssignmentStrategy, and LoadBalancedAssignmentStrategy, shared scoring helpers, constants (STRATEGY_MAP, STRATEGY_NAME_*), and workload-aware selection logic. Review scoring/workload tie-break logic.
Errors & Observability
src/ai_company/engine/errors.py, src/ai_company/observability/events/task_assignment.py
Added TaskAssignmentError, NoEligibleAgentError, and eight task-assignment event constants for observability.
Unit Tests
tests/unit/config/test_schema.py, tests/unit/engine/test_assignment_models.py, tests/unit/engine/test_assignment_service.py, tests/unit/engine/test_assignment_strategies.py, tests/unit/engine/test_errors.py, tests/unit/observability/test_events.py
Comprehensive unit tests for config, models, service behavior, all strategies (edge cases), errors, and event constants.
Integration Tests
tests/integration/engine/test_multi_agent_delegation.py
Large end-to-end integration test exercising delegation, decomposition, routing, parallel execution, and assignment service with a deterministic mock provider; multiple scenarios (happy path, partial failure, loop prevention, concurrency).

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Service as TaskAssignmentService
    participant Strategy as TaskAssignmentStrategy
    participant Scorer as AgentTaskScorer

    Client->>Service: assign(AssignmentRequest)
    activate Service
    Service->>Service: validate task status
    Service->>Service: log("task_assignment.started")
    Service->>Strategy: assign(request)
    activate Strategy
    Strategy->>Scorer: score agents / build subtask
    Scorer-->>Strategy: scored candidates
    Strategy->>Strategy: filter by min_score
    Strategy->>Strategy: select candidate (may use workloads)
    Strategy-->>Service: AssignmentResult
    deactivate Strategy
    alt Agent selected
        Service->>Service: log("task_assignment.agent.selected")
    else No eligible agent
        Service->>Service: log("task_assignment.no_eligible")
    end
    Service->>Service: log("task_assignment.complete")
    Service-->>Client: AssignmentResult
    deactivate Service
Loading
sequenceDiagram
    participant Manual as ManualAssignmentStrategy
    participant RoleBased as RoleBasedAssignmentStrategy
    participant LoadBalanced as LoadBalancedAssignmentStrategy
    participant Scorer as AgentTaskScorer

    Note over Manual,Scorer: Strategy flows

    rect rgba(100, 150, 255, 0.5)
    Note over Manual: Manual Strategy
    Manual->>Manual: validate assigned_to present & in pool & ACTIVE
    Manual-->>Manual: return selected or raise
    end

    rect rgba(100, 200, 150, 0.5)
    Note over RoleBased: Role-Based Strategy
    RoleBased->>Scorer: build subtask & score agents
    RoleBased->>RoleBased: filter by min_score, pick top score, populate alternatives
    RoleBased-->>RoleBased: return selected or none
    end

    rect rgba(200, 150, 100, 0.5)
    Note over LoadBalanced: Load-Balanced Strategy
    LoadBalanced->>Scorer: score agents
    LoadBalanced->>LoadBalanced: filter by min_score
    LoadBalanced->>LoadBalanced: sort by workload asc, tie-break by score desc
    LoadBalanced-->>LoadBalanced: return lowest-loaded selected
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 67.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main feature: implementing a task assignment subsystem with pluggable strategies. It accurately reflects the primary focus of the changeset.
Description check ✅ Passed The PR description clearly outlines the task assignment subsystem implementation with strategies, models, configuration, and logging, directly corresponding to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/task-assignment-integration-test
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/task-assignment-integration-test

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

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a robust and flexible task assignment subsystem, significantly enhancing the platform's ability to intelligently distribute work among AI agents. By providing multiple pluggable strategies and a dedicated service for orchestration, it allows for dynamic and configurable task allocation based on factors like manual designation, agent capabilities, or current workload. This foundational change improves agent efficiency and system scalability, making the overall agent orchestration more sophisticated and adaptable to various operational needs.

Highlights

  • Task Assignment Subsystem: Implemented a new task assignment subsystem featuring three pluggable strategies: Manual, RoleBased, and LoadBalanced, all conforming to a TaskAssignmentStrategy protocol.
  • TaskAssignmentService Orchestration: Introduced a TaskAssignmentService responsible for resolving the appropriate assignment strategy by name from a STRATEGY_MAP and delegating the task assignment process.
  • Pydantic Models and Configuration: Defined frozen Pydantic models (AssignmentRequest, AssignmentResult, AssignmentCandidate, AgentWorkload) with built-in validators, and added TaskAssignmentConfig to the RootConfig schema, including default_strategy, min_score, and max_concurrent_tasks_per_agent.
  • Structured Logging and Error Handling: Integrated structured logging with 8 new event constants under observability.events.task_assignment and introduced specific error types: TaskAssignmentError and NoEligibleAgentError.
  • Documentation Updates: Updated DESIGN_SPEC.md, CLAUDE.md, and README.md to reflect the new task assignment feature and its project structure.
Changelog
  • CLAUDE.md
    • Updated the description of the 'engine' package to include 'task assignment' in its responsibilities.
  • DESIGN_SPEC.md
    • Added a new 'assignment/' directory structure within the 'engine' package, detailing its sub-modules for models, protocols, services, and strategies.
    • Included 'task_assignment.py' in the list of observability event constants.
  • README.md
    • Added 'Task Assignment' as a new feature bullet point, highlighting its pluggable strategies.
  • src/ai_company/config/defaults.py
    • Added an empty 'task_assignment' dictionary to the default configuration.
  • src/ai_company/config/schema.py
    • Added a new Pydantic model, TaskAssignmentConfig, for defining task assignment behavior, including strategy, minimum score, and maximum concurrent tasks per agent.
    • Integrated TaskAssignmentConfig into the RootConfig schema.
  • src/ai_company/engine/init.py
    • Imported and exposed new classes, protocols, and constants related to the task assignment subsystem.
    • Imported and exposed new error types: NoEligibleAgentError and TaskAssignmentError.
  • src/ai_company/engine/assignment/init.py
    • Added a new file to define the task assignment engine package, exporting its models, protocol, service, and strategies.
  • src/ai_company/engine/assignment/models.py
    • Added a new file to define Pydantic models for AgentWorkload, AssignmentCandidate, AssignmentRequest, and AssignmentResult, including validation logic.
  • src/ai_company/engine/assignment/protocol.py
    • Added a new file to define the TaskAssignmentStrategy protocol, outlining the interface for pluggable assignment strategies.
  • src/ai_company/engine/assignment/service.py
    • Added a new file to implement TaskAssignmentService, which orchestrates task assignment using pluggable strategies and includes logging and validation.
  • src/ai_company/engine/assignment/strategies.py
    • Added a new file to implement concrete task assignment strategies: ManualAssignmentStrategy, RoleBasedAssignmentStrategy, and LoadBalancedAssignmentStrategy, along with a module-level strategy registry.
  • src/ai_company/engine/errors.py
    • Added TaskAssignmentError as a new engine error base class.
    • Added NoEligibleAgentError as a specific error for when no eligible agent is found for assignment.
  • src/ai_company/observability/events/task_assignment.py
    • Added a new file to define constants for structured logging events related to task assignment.
  • tests/integration/engine/test_multi_agent_delegation.py
    • Added a new integration test file to validate the multi-agent pipeline, including a test for the TaskAssignmentService with a scoring pipeline.
  • tests/unit/config/test_schema.py
    • Updated imports to include TaskAssignmentConfig.
    • Added unit tests for TaskAssignmentConfig, covering defaults, immutability, and boundary validations.
  • tests/unit/engine/test_assignment_models.py
    • Added a new unit test file to thoroughly test the validation and behavior of the task assignment Pydantic models.
  • tests/unit/engine/test_assignment_service.py
    • Added a new unit test file to verify the functionality of TaskAssignmentService, including strategy delegation and status validation.
  • tests/unit/engine/test_assignment_strategies.py
    • Added a new unit test file to test the individual task assignment strategies and their adherence to the protocol, including workload distribution scenarios.
  • tests/unit/engine/test_errors.py
    • Updated imports to include NoEligibleAgentError and TaskAssignmentError.
    • Added unit tests to confirm the inheritance and behavior of the new task assignment error types.
  • tests/unit/observability/test_events.py
    • Updated imports to include new task assignment event constants.
    • Added unit tests to ensure the correct definition of task assignment event constants and their discovery within the observability module.
Activity
  • The pull request was pre-reviewed by 10 AI agents (code-reviewer, python-reviewer, pr-test-analyzer, silent-failure-hunter, comment-analyzer, type-design-analyzer, logging-audit, resilience-audit, security-reviewer, docs-consistency).
  • A total of 25 findings were triaged and addressed based on the pre-review.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a well-designed and robust task assignment subsystem. The use of a protocol for strategies allows for easy extension, and the initial three strategies (Manual, RoleBased, LoadBalanced) cover common use cases effectively. The code is clean, well-structured, and accompanied by comprehensive unit and integration tests. The new Pydantic models are well-defined and use validation effectively. I have one suggestion to further improve the robustness of the AssignmentRequest model by adding uniqueness validation for agent IDs in its collections. Overall, this is an excellent contribution.

Comment on lines +105 to +111
@model_validator(mode="after")
def _validate_non_empty_agents(self) -> Self:
"""Ensure at least one agent is available for assignment."""
if not self.available_agents:
msg = "available_agents must not be empty"
raise ValueError(msg)
return self
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.

medium

The AssignmentRequest model could be made more robust by validating the uniqueness of agent identifiers in available_agents and workloads. Currently, if there are duplicate agent IDs in these tuples, the logic in the assignment strategies might silently use the first occurrence or, in the case of workloads being converted to a dictionary, the last occurrence. This could lead to incorrect behavior or hide data integrity issues.

I suggest combining the existing _validate_non_empty_agents check with new uniqueness checks for both available_agents and workloads into a single, more comprehensive validator. This will make the system more predictable and easier to debug.

    @model_validator(mode="after")
    def _validate_request_collections(self) -> Self:
        """Ensure collections in the request are valid.

        - `available_agents` must not be empty.
        - Agent IDs in `available_agents` must be unique.
        - Agent IDs in `workloads` must be unique.
        """
        if not self.available_agents:
            msg = "available_agents must not be empty"
            raise ValueError(msg)

        agent_ids = [a.id for a in self.available_agents]
        if len(agent_ids) != len(set(agent_ids)):
            from collections import Counter

            dupes = sorted(str(i) for i, c in Counter(agent_ids).items() if c > 1)
            msg = f"Duplicate agent IDs in available_agents: {dupes}"
            raise ValueError(msg)

        if self.workloads:
            workload_agent_ids = [w.agent_id for w in self.workloads]
            if len(workload_agent_ids) != len(set(workload_agent_ids)):
                from collections import Counter

                dupes = sorted(i for i, c in Counter(workload_agent_ids).items() if c > 1)
                msg = f"Duplicate agent_id in workloads: {dupes}"
                raise ValueError(msg)

        return self

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ai_company/engine/assignment/models.py`:
- Around line 86-89: The workloads Field on the model can contain duplicate
AgentWorkload.agent_id entries which breaks assumptions in
LoadBalancedAssignmentStrategy; add a Pydantic validator (e.g.
`@validator`("workloads") or a root_validator) on the model that iterates the
workloads tuple, detects duplicate AgentWorkload.agent_id values, and raises a
ValueError (with a clear message) when duplicates are found so callers cannot
create models with non‑unique agent IDs.

In `@src/ai_company/engine/assignment/strategies.py`:
- Around line 140-144: The ID comparison relies on stringifying a UUID
(available.id) to match task.assigned_to; make this explicit and robust by
converting task.assigned_to to a UUID before comparison (or vice-versa) and
adding a short comment documenting the expectation. Update the loop that
iterates request.available_agents (the block that sets agent when
str(available.id) == task.assigned_to) to perform a safe conversion using
UUID(task.assigned_to) and catch/handle ValueError/TypeError if the string is
invalid, or normalize both sides to the same canonical representation, and add a
one-line comment documenting the chosen contract.

In `@tests/unit/engine/test_assignment_models.py`:
- Around line 309-318: Move the test_empty_available_agents_rejected test from
TestAssignmentRequestValidation into the existing TestAssignmentRequest class:
remove the TestAssignmentRequestValidation class wrapper and place the test
method (test_empty_available_agents_rejected) alongside the other tests in
TestAssignmentRequest so it runs with related AssignmentRequest tests; ensure
the test still imports/uses AssignmentRequest and _make_task() unchanged and
that the pytest.raises(ValidationError, match="available_agents") assertion
remains intact.
- Around line 288-296: Rename the misleading test method
test_selected_none_with_alternatives_is_valid to accurately reflect that it
asserts selected is None and alternatives is empty (e.g.,
test_selected_none_with_no_alternatives_is_valid or
test_selected_none_and_empty_alternatives_is_valid); update the test function
name where defined in tests/unit/engine/test_assignment_models.py and any
references to it so the name matches the docstring and the actual assertion on
AssignmentResult (constructed with task_id, strategy_used, reason).
- Around line 181-188: Update the test_min_score_out_of_range to cover both
below-zero and above-one cases by parametrizing the input values (e.g., -0.1 and
1.5) so that AssignmentRequest(task=_make_task(),
available_agents=(_make_agent("dev-1"),), min_score=...) raises ValidationError
for each; locate the test function test_min_score_out_of_range in
tests/unit/engine/test_assignment_models.py and modify it to iterate over the
invalid min_score values and assert ValidationError mentions "min_score".

In `@tests/unit/engine/test_assignment_service.py`:
- Around line 29-60: Extract the duplicated helpers _model_config, _make_agent,
and _make_task into a shared conftest.py as reusable fixtures (e.g.,
model_config, make_agent, make_task) that return the same types (ModelConfig,
AgentIdentity, Task) and keep the same default behavior; then update test
functions in the three test modules to accept and use those fixtures (or import
the helpers from conftest) instead of defining local copies, ensuring callers of
_make_agent still pass SeniorityLevel and primary_skills as before and that
_make_task supports overrides.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 149463df-ab12-4690-b485-5602fe4cfe1f

📥 Commits

Reviewing files that changed from the base of the PR and between 951e337 and 413b5aa.

📒 Files selected for processing (20)
  • CLAUDE.md
  • DESIGN_SPEC.md
  • README.md
  • src/ai_company/config/defaults.py
  • src/ai_company/config/schema.py
  • src/ai_company/engine/__init__.py
  • src/ai_company/engine/assignment/__init__.py
  • src/ai_company/engine/assignment/models.py
  • src/ai_company/engine/assignment/protocol.py
  • src/ai_company/engine/assignment/service.py
  • src/ai_company/engine/assignment/strategies.py
  • src/ai_company/engine/errors.py
  • src/ai_company/observability/events/task_assignment.py
  • tests/integration/engine/test_multi_agent_delegation.py
  • tests/unit/config/test_schema.py
  • tests/unit/engine/test_assignment_models.py
  • tests/unit/engine/test_assignment_service.py
  • tests/unit/engine/test_assignment_strategies.py
  • tests/unit/engine/test_errors.py
  • tests/unit/observability/test_events.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use 'from future import annotations' in Python 3.14+ code
Use 'except A, B:' syntax without parentheses in exception handling for Python 3.14 (PEP 758)
Include type hints on all public functions; enforce with mypy strict mode
Use Google-style docstrings, required on all public classes and functions, enforced by ruff D rules
Create new objects rather than mutating existing ones; use copy.deepcopy() for non-Pydantic internal collections and MappingProxyType for read-only enforcement
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models for runtime state
Use Pydantic v2 with BaseModel, model_validator, computed_field, and ConfigDict; use @computed_field for derived values instead of storing redundant fields
Use NotBlankStr from core.types for all identifier/name fields instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code
Enforce maximum line length of 88 characters
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries: user input, external APIs, and config files

Files:

  • src/ai_company/engine/assignment/service.py
  • src/ai_company/config/defaults.py
  • src/ai_company/config/schema.py
  • src/ai_company/engine/errors.py
  • src/ai_company/engine/assignment/__init__.py
  • tests/unit/engine/test_assignment_service.py
  • tests/unit/engine/test_assignment_strategies.py
  • src/ai_company/engine/assignment/models.py
  • tests/unit/config/test_schema.py
  • src/ai_company/observability/events/task_assignment.py
  • tests/integration/engine/test_multi_agent_delegation.py
  • src/ai_company/engine/assignment/protocol.py
  • tests/unit/observability/test_events.py
  • src/ai_company/engine/assignment/strategies.py
  • tests/unit/engine/test_errors.py
  • tests/unit/engine/test_assignment_models.py
  • src/ai_company/engine/__init__.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Every module with business logic must import logger via 'from ai_company.observability import get_logger' and instantiate as 'logger = get_logger(name)'
Never use 'import logging' or 'logging.getLogger()' or 'print()' in application code
Always use 'logger' as the variable name for logging, not '_logger' or 'log'
Use event name constants from domain-specific modules under ai_company.observability.events for logging events
Always use structured logging with kwargs (logger.info(EVENT, key=value)), never formatted strings (logger.info('msg %s', val))
Log at WARNING or ERROR with context for all error paths before raising exceptions
Log at INFO for all state transitions
Log at DEBUG for object creation, internal flow, and entry/exit of key functions
Pure data models, enums, and re-exports do not require logging
All provider calls go through BaseCompletionProvider which applies retry and rate limiting automatically

Files:

  • src/ai_company/engine/assignment/service.py
  • src/ai_company/config/defaults.py
  • src/ai_company/config/schema.py
  • src/ai_company/engine/errors.py
  • src/ai_company/engine/assignment/__init__.py
  • src/ai_company/engine/assignment/models.py
  • src/ai_company/observability/events/task_assignment.py
  • src/ai_company/engine/assignment/protocol.py
  • src/ai_company/engine/assignment/strategies.py
  • src/ai_company/engine/__init__.py
{src,tests}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples; use generic names like 'example-provider', 'example-large-001', 'example-medium-001', 'example-small-001', 'large'/'medium'/'small'

Files:

  • src/ai_company/engine/assignment/service.py
  • src/ai_company/config/defaults.py
  • src/ai_company/config/schema.py
  • src/ai_company/engine/errors.py
  • src/ai_company/engine/assignment/__init__.py
  • tests/unit/engine/test_assignment_service.py
  • tests/unit/engine/test_assignment_strategies.py
  • src/ai_company/engine/assignment/models.py
  • tests/unit/config/test_schema.py
  • src/ai_company/observability/events/task_assignment.py
  • tests/integration/engine/test_multi_agent_delegation.py
  • src/ai_company/engine/assignment/protocol.py
  • tests/unit/observability/test_events.py
  • src/ai_company/engine/assignment/strategies.py
  • tests/unit/engine/test_errors.py
  • tests/unit/engine/test_assignment_models.py
  • src/ai_company/engine/__init__.py
DESIGN_SPEC.md

📄 CodeRabbit inference engine (CLAUDE.md)

Update DESIGN_SPEC.md to reflect approved deviations from the specification

Files:

  • DESIGN_SPEC.md
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Mark unit tests with @pytest.mark.unit
Mark integration tests with @pytest.mark.integration
Mark end-to-end tests with @pytest.mark.e2e
Mark slow tests with @pytest.mark.slow
Prefer @pytest.mark.parametrize for testing similar cases
Use 'test-provider', 'test-small-001', etc. in test code instead of real vendor names

Files:

  • tests/unit/engine/test_assignment_service.py
  • tests/unit/engine/test_assignment_strategies.py
  • tests/unit/config/test_schema.py
  • tests/integration/engine/test_multi_agent_delegation.py
  • tests/unit/observability/test_events.py
  • tests/unit/engine/test_errors.py
  • tests/unit/engine/test_assignment_models.py
🧠 Learnings (3)
📚 Learning: 2026-03-08T09:48:46.483Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-08T09:48:46.483Z
Learning: Applies to **/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models for runtime state

Applied to files:

  • src/ai_company/engine/assignment/models.py
📚 Learning: 2026-03-08T09:48:46.483Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-08T09:48:46.483Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events for logging events

Applied to files:

  • src/ai_company/observability/events/task_assignment.py
  • tests/unit/observability/test_events.py
📚 Learning: 2026-03-08T09:48:46.483Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-08T09:48:46.483Z
Learning: Applies to src/ai_company/providers/**/*.py : RetryExhaustedError signals that all retries failed; engine layer catches this to trigger fallback chains

Applied to files:

  • src/ai_company/engine/__init__.py
🧬 Code graph analysis (9)
src/ai_company/engine/assignment/service.py (5)
src/ai_company/core/enums.py (1)
  • TaskStatus (165-191)
src/ai_company/engine/errors.py (1)
  • TaskAssignmentError (66-67)
src/ai_company/observability/_logger.py (1)
  • get_logger (8-28)
src/ai_company/engine/assignment/models.py (2)
  • AssignmentRequest (64-111)
  • AssignmentResult (114-154)
src/ai_company/engine/assignment/protocol.py (3)
  • TaskAssignmentStrategy (16-41)
  • assign (29-41)
  • name (25-27)
tests/unit/engine/test_assignment_service.py (4)
src/ai_company/engine/assignment/models.py (1)
  • AssignmentRequest (64-111)
src/ai_company/engine/assignment/service.py (2)
  • TaskAssignmentService (33-109)
  • assign (46-109)
src/ai_company/engine/assignment/strategies.py (7)
  • ManualAssignmentStrategy (100-194)
  • name (108-110)
  • name (210-212)
  • name (275-277)
  • assign (112-194)
  • assign (214-257)
  • assign (279-343)
src/ai_company/engine/errors.py (1)
  • TaskAssignmentError (66-67)
tests/unit/engine/test_assignment_strategies.py (6)
src/ai_company/core/agent.py (3)
  • AgentIdentity (246-304)
  • ModelConfig (145-174)
  • SkillSet (125-142)
src/ai_company/core/enums.py (4)
  • AgentStatus (67-72)
  • Complexity (214-220)
  • SeniorityLevel (6-21)
  • TaskType (194-202)
src/ai_company/engine/assignment/models.py (2)
  • AgentWorkload (16-36)
  • AssignmentRequest (64-111)
src/ai_company/engine/assignment/protocol.py (3)
  • TaskAssignmentStrategy (16-41)
  • name (25-27)
  • assign (29-41)
src/ai_company/engine/assignment/strategies.py (9)
  • LoadBalancedAssignmentStrategy (260-343)
  • ManualAssignmentStrategy (100-194)
  • RoleBasedAssignmentStrategy (197-257)
  • name (108-110)
  • name (210-212)
  • name (275-277)
  • assign (112-194)
  • assign (214-257)
  • assign (279-343)
src/ai_company/engine/routing/scorer.py (3)
  • AgentTaskScorer (36-162)
  • score (74-162)
  • min_score (70-72)
src/ai_company/engine/assignment/models.py (3)
src/ai_company/core/agent.py (1)
  • AgentIdentity (246-304)
src/ai_company/core/task.py (1)
  • Task (45-261)
src/ai_company/engine/routing/scorer.py (2)
  • score (74-162)
  • min_score (70-72)
tests/unit/config/test_schema.py (1)
src/ai_company/config/schema.py (1)
  • TaskAssignmentConfig (361-393)
src/ai_company/engine/assignment/protocol.py (3)
src/ai_company/engine/assignment/models.py (2)
  • AssignmentRequest (64-111)
  • AssignmentResult (114-154)
src/ai_company/engine/assignment/strategies.py (6)
  • name (108-110)
  • name (210-212)
  • name (275-277)
  • assign (112-194)
  • assign (214-257)
  • assign (279-343)
src/ai_company/engine/assignment/service.py (1)
  • assign (46-109)
src/ai_company/engine/assignment/strategies.py (3)
src/ai_company/core/enums.py (1)
  • AgentStatus (67-72)
src/ai_company/engine/errors.py (2)
  • NoEligibleAgentError (70-71)
  • TaskAssignmentError (66-67)
src/ai_company/engine/routing/scorer.py (2)
  • AgentTaskScorer (36-162)
  • score (74-162)
tests/unit/engine/test_errors.py (1)
src/ai_company/engine/errors.py (3)
  • NoEligibleAgentError (70-71)
  • TaskAssignmentError (66-67)
  • EngineError (4-5)
tests/unit/engine/test_assignment_models.py (1)
src/ai_company/engine/assignment/models.py (4)
  • AgentWorkload (16-36)
  • AssignmentCandidate (39-61)
  • AssignmentRequest (64-111)
  • AssignmentResult (114-154)
🪛 LanguageTool
README.md

[typographical] ~20-~20: To join two clauses or introduce examples, consider using an em dash.
Context: ...agent-task scoring - Task Assignment - Pluggable strategies (manual, role-based...

(DASH_RULE)

🔇 Additional comments (42)
src/ai_company/engine/errors.py (1)

64-71: LGTM!

Clean addition to the engine error hierarchy. The two-level inheritance (TaskAssignmentErrorNoEligibleAgentError) provides appropriate granularity for distinguishing general assignment failures from the specific case of no eligible agents.

src/ai_company/engine/assignment/protocol.py (1)

1-41: LGTM!

Well-designed protocol with a clean, minimal interface. Good use of TYPE_CHECKING to avoid circular imports while maintaining type hints. The @runtime_checkable decorator enables runtime isinstance() checks for strategy validation.

DESIGN_SPEC.md (1)

2389-2394: LGTM!

Documentation accurately reflects the new assignment subsystem structure with proper file paths and descriptions that match the implementation.

src/ai_company/observability/events/task_assignment.py (1)

1-12: LGTM!

Comprehensive set of event constants covering the full assignment lifecycle. Naming follows the established domain.action and domain.entity.action patterns used by other event modules. Based on learnings, this correctly places event constants in a domain-specific module under ai_company.observability.events.

src/ai_company/engine/assignment/models.py (3)

16-36: LGTM!

Good use of allow_inf_nan=False in ConfigDict to prevent invalid float values in total_cost_usd. The field constraints (ge=0 for count, ge=0.0 for cost) are appropriate for workload tracking.


39-61: LGTM!

Well-structured candidate model with appropriate score bounds and proper use of NotBlankStr for the reason field.


141-153: LGTM!

Good defensive validation ensuring the selected candidate is not duplicated in alternatives. The error message includes the agent name for clear debugging.

src/ai_company/engine/assignment/strategies.py (4)

35-55: LGTM!

Clean helper function that adapts AssignmentRequest to SubtaskDefinition for scoring. Good separation of concerns.


58-97: LGTM!

Well-factored shared scoring logic with proper structured logging. The filtering and sorting by score descending provides a consistent candidate ranking for both RoleBased and LoadBalanced strategies.


312-321: Same ID type convention applies to workload lookup.

The workload map keys are agent_id (string from AgentWorkload), and lookup uses str(c.agent_identity.id). This is consistent with the manual strategy's approach but shares the same implicit contract about string UUID representation.


346-365: LGTM!

Module-level singleton registry with MappingProxyType enforces immutability. The _DEFAULT_SCORER ensures consistent scoring across strategies. The type alias _StrategyType provides clear typing for the registry values.

src/ai_company/engine/assignment/service.py (4)

28-30: LGTM!

Correct set of assignable statuses: CREATED for new tasks, FAILED and INTERRUPTED for retry scenarios. Using frozenset ensures immutability.


33-44: LGTM!

Clean service design with __slots__ for memory efficiency. The docstring correctly notes that the service does NOT mutate the task, maintaining single responsibility.


61-73: LGTM!

Status validation with sorted assignable statuses in the error message provides a clear, deterministic error description. Proper use of WARNING level with context before raising.


82-90: LGTM!

Good exception handling pattern: logs exception with logger.exception (includes traceback) then re-raises. This ensures observability while preserving the original exception for callers.

src/ai_company/engine/assignment/__init__.py (1)

1-39: LGTM!

Clean package initialization that consolidates all public exports from the assignment subsystem. The __all__ list provides explicit API documentation and enables star imports when needed.

src/ai_company/engine/__init__.py (1)

9-23: LGTM!

The re-exports correctly expose the new task assignment subsystem's public API surface. All new symbols are properly added to __all__ in alphabetical order, and the imports are well-organized.

Also applies to: 51-51, 55-55, 117-120, 129-132, 155-155, 157-157, 160-160, 177-177, 191-193

README.md (1)

20-20: LGTM!

The new feature bullet accurately describes the task assignment subsystem and follows the existing formatting pattern used by other feature items in the list.

tests/integration/engine/test_multi_agent_delegation.py (4)

1-79: LGTM!

Well-structured integration test module with proper markers (integration, timeout(30)) applied globally via pytestmark. The mock provider implementation is clean and supports deterministic testing with canned responses and simulated failures.


189-210: LGTM!

The _make_agent factory is well-designed with sensible defaults and uses test-compliant identifiers (test-provider, test-small-001). The noqa: PLR0913 suppression is appropriate for a test factory function.


394-536: LGTM!

The test scenarios provide comprehensive coverage:

  • Happy path: delegation → decomposition → routing → parallel execution → rollup
  • Partial failure: one subtask fails, rollup correctly derives FAILED status
  • Loop prevention: back-delegation to ancestors is blocked
  • Concurrency: max_concurrency limit is respected
  • Assignment service: integration with RoleBasedAssignmentStrategy

848-873: LGTM!

The TestTaskAssignmentServiceIntegration test validates the full integration between TaskAssignmentService, RoleBasedAssignmentStrategy, and AgentTaskScorer. It correctly verifies that the backend agent is selected based on matching skills and role.

src/ai_company/config/defaults.py (1)

32-32: LGTM!

The empty dict default for task_assignment is correct. It aligns with other config sections and will be merged with TaskAssignmentConfig's field-level defaults during validation.

CLAUDE.md (1)

52-52: LGTM!

The engine description accurately reflects the addition of task assignment alongside routing, task lifecycle, recovery, and shutdown.

tests/unit/engine/test_errors.py (1)

11-13: LGTM!

The tests correctly verify the error hierarchy:

  • TaskAssignmentError extends EngineError
  • NoEligibleAgentError extends TaskAssignmentError (and transitively EngineError)

String representation verification ensures error messages propagate correctly.

Also applies to: 48-61

tests/unit/config/test_schema.py (1)

13-13: LGTM!

Comprehensive test coverage for TaskAssignmentConfig:

  • Default values verification
  • Immutability (frozen model)
  • Boundary validation for min_score [0.0, 1.0] and max_concurrent_tasks_per_agent [1, 50]
  • NaN/Inf rejection via allow_inf_nan=False

Also applies to: 498-550

src/ai_company/config/schema.py (2)

361-393: LGTM!

TaskAssignmentConfig is well-designed and follows established patterns:

  • Frozen for immutability
  • NotBlankStr for strategy field (per coding guidelines)
  • Appropriate range constraints for min_score [0.0, 1.0] and max_concurrent_tasks_per_agent [1, 50]
  • allow_inf_nan=False prevents invalid float values
  • Clear documentation including engine-layer usage note

417-418: LGTM!

The task_assignment field is correctly added to RootConfig with default_factory=TaskAssignmentConfig, following the same pattern as other config sections like graceful_shutdown and coordination_metrics. The docstring is properly updated to document the new field.

Also applies to: 482-485

tests/unit/engine/test_assignment_service.py (2)

1-26: LGTM!

The module setup is well-structured with proper imports, the pytestmark = pytest.mark.unit decorator correctly marks all tests as unit tests, and the provider/model names comply with the coding guidelines ("test-provider", "test-small-001").


63-202: LGTM!

The test class provides comprehensive coverage of TaskAssignmentService behavior:

  • Delegation to configured strategy
  • Acceptance of assignable statuses (CREATED, FAILED, INTERRUPTED)
  • Rejection of non-assignable statuses with proper error handling
  • Error propagation from strategies
  • Result contains strategy name

Good use of @pytest.mark.parametrize for testing similar cases as per coding guidelines.

tests/unit/engine/test_assignment_models.py (3)

1-21: LGTM!

Module setup is correct with proper imports and the pytestmark = pytest.mark.unit marker applied at module level.


58-103: LGTM!

Thorough validation tests for AgentWorkload covering:

  • Valid construction
  • Negative value rejection for both active_task_count and total_cost_usd
  • Zero boundary case
  • Immutability (frozen) enforcement

106-155: LGTM!

Good boundary testing for AssignmentCandidate.score field, correctly verifying that scores outside [0.0, 1.0] are rejected while boundary values are accepted.

tests/unit/engine/test_assignment_strategies.py (6)

1-32: LGTM!

Module setup is correct with proper imports and pytestmark = pytest.mark.unit marker. The noqa: PLR0913 comment at line 39 appropriately suppresses the too-many-arguments warning for the _make_agent helper.


73-147: LGTM!

Comprehensive tests for ManualAssignmentStrategy covering:

  • Success with valid assigned_to
  • Error when assigned_to is None
  • Error when designated agent is not in pool
  • Rejection of inactive agents (parametrized for ON_LEAVE and TERMINATED)
  • Strategy name property

Good use of @pytest.mark.parametrize for inactive agent status testing.


150-259: LGTM!

Thorough tests for RoleBasedAssignmentStrategy covering:

  • Best-scoring agent selection
  • Alternatives population
  • No viable agents returns None when below threshold
  • Seniority-only fallback when no required skills
  • Strategy name property

262-480: LGTM!

Excellent coverage for LoadBalancedAssignmentStrategy including:

  • Lowest workload selection
  • Tie-breaking by score
  • Fallback to capability-only when no workloads
  • Parametrized workload distributions
  • No eligible agents returns None
  • Partial workload data defaults to zero
  • Strategy name property

The parametrized test at lines 372-416 effectively covers edge cases with different workload distributions.


483-510: LGTM!

Good registry tests verifying:

  • All expected strategy keys are present
  • Values are correct strategy types
  • Map is immutable (MappingProxyType)

513-530: LGTM!

Protocol conformance tests verify that all three strategy implementations (ManualAssignmentStrategy, RoleBasedAssignmentStrategy, LoadBalancedAssignmentStrategy) satisfy the TaskAssignmentStrategy protocol.

tests/unit/observability/test_events.py (3)

87-96: LGTM!

New task assignment event constants are properly imported. All eight events are included:

  • TASK_ASSIGNMENT_STARTED
  • TASK_ASSIGNMENT_COMPLETE
  • TASK_ASSIGNMENT_FAILED
  • TASK_ASSIGNMENT_NO_ELIGIBLE
  • TASK_ASSIGNMENT_AGENT_SCORED
  • TASK_ASSIGNMENT_AGENT_SELECTED
  • TASK_ASSIGNMENT_MANUAL_VALIDATED
  • TASK_ASSIGNMENT_WORKLOAD_BALANCED

Based on learnings: Uses event name constants from domain-specific modules under ai_company.observability.events for logging events.


165-165: LGTM!

The task_assignment domain module is correctly added to the expected set of domain modules for the discovery test.


266-274: LGTM!

The test method correctly verifies all eight task assignment event constants map to their expected string values following the domain.subject.qualifier pattern. The test is consistent with other *_events_exist test methods in the file.

Comment on lines +140 to +144
agent = None
for available in request.available_agents:
if str(available.id) == task.assigned_to:
agent = available
break
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Type conversion for ID comparison relies on consistent string representation.

The comparison str(available.id) == task.assigned_to works because available.id is a UUID and task.assigned_to is a string (presumably the UUID's string form). This implicit contract should be documented or enforced.

💡 Consider explicit documentation or a helper
+        # task.assigned_to stores agent ID as string; compare against UUID string form
         for available in request.available_agents:
             if str(available.id) == task.assigned_to:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ai_company/engine/assignment/strategies.py` around lines 140 - 144, The
ID comparison relies on stringifying a UUID (available.id) to match
task.assigned_to; make this explicit and robust by converting task.assigned_to
to a UUID before comparison (or vice-versa) and adding a short comment
documenting the expectation. Update the loop that iterates
request.available_agents (the block that sets agent when str(available.id) ==
task.assigned_to) to perform a safe conversion using UUID(task.assigned_to) and
catch/handle ValueError/TypeError if the string is invalid, or normalize both
sides to the same canonical representation, and add a one-line comment
documenting the chosen contract.

Comment on lines +29 to +60
def _model_config() -> ModelConfig:
return ModelConfig(provider="test-provider", model_id="test-small-001")


def _make_agent(
name: str,
*,
level: SeniorityLevel = SeniorityLevel.MID,
primary_skills: tuple[str, ...] = (),
) -> AgentIdentity:
return AgentIdentity(
name=name,
role="Developer",
department="Engineering",
level=level,
model=_model_config(),
hiring_date=date(2026, 1, 1),
skills=SkillSet(primary=primary_skills),
)


def _make_task(**overrides: object) -> Task:
defaults: dict[str, object] = {
"id": "task-001",
"title": "Test task",
"description": "A test task",
"type": TaskType.DEVELOPMENT,
"project": "proj-001",
"created_by": "manager",
}
defaults.update(overrides)
return Task(**defaults) # type: ignore[arg-type]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider extracting shared test helpers to conftest.py.

The _model_config, _make_agent, and _make_task helpers are duplicated across test_assignment_service.py, test_assignment_models.py, and test_assignment_strategies.py. Extracting these to a shared conftest.py in the tests/unit/engine/ directory would reduce duplication and improve maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/engine/test_assignment_service.py` around lines 29 - 60, Extract
the duplicated helpers _model_config, _make_agent, and _make_task into a shared
conftest.py as reusable fixtures (e.g., model_config, make_agent, make_task)
that return the same types (ModelConfig, AgentIdentity, Task) and keep the same
default behavior; then update test functions in the three test modules to accept
and use those fixtures (or import the helpers from conftest) instead of defining
local copies, ensuring callers of _make_agent still pass SeniorityLevel and
primary_skills as before and that _make_task supports overrides.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements a new engine-level task assignment subsystem with pluggable assignment strategies and supporting config, observability events, and test coverage to integrate assignment into the broader delegation/decomposition/routing pipeline.

Changes:

  • Added task assignment domain models, strategy protocol + implementations, and an orchestration service.
  • Added task-assignment config to the root configuration schema and defaults.
  • Added structured observability event constants + updated docs and tests (unit + integration).

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/unit/observability/test_events.py Asserts discovery and exact values for new task-assignment event constants.
tests/unit/engine/test_errors.py Verifies new assignment error types participate in the engine error hierarchy.
tests/unit/engine/test_assignment_strategies.py Unit coverage for Manual/RoleBased/LoadBalanced strategies, registry, and protocol conformance.
tests/unit/engine/test_assignment_service.py Unit coverage for TaskAssignmentService status validation, delegation, and error propagation.
tests/unit/engine/test_assignment_models.py Unit coverage for assignment Pydantic models, validators, and immutability.
tests/unit/config/test_schema.py Adds tests for TaskAssignmentConfig defaults, bounds, and NaN/Inf rejection.
tests/integration/engine/test_multi_agent_delegation.py End-to-end integration exercising delegation→decomposition→routing→execution and assignment service integration.
src/ai_company/observability/events/task_assignment.py Introduces the TASK_ASSIGNMENT_* event name constants.
src/ai_company/engine/errors.py Adds TaskAssignmentError and NoEligibleAgentError.
src/ai_company/engine/assignment/strategies.py Implements the three strategies and a module-level registry (STRATEGY_MAP).
src/ai_company/engine/assignment/service.py Adds TaskAssignmentService orchestration, status validation, and logging.
src/ai_company/engine/assignment/protocol.py Defines the TaskAssignmentStrategy protocol for pluggable strategies.
src/ai_company/engine/assignment/models.py Adds frozen Pydantic request/result/candidate/workload models + validators.
src/ai_company/engine/assignment/init.py Exposes assignment subsystem public API.
src/ai_company/engine/init.py Re-exports assignment API at engine package level.
src/ai_company/config/schema.py Adds TaskAssignmentConfig and wires it into RootConfig.
src/ai_company/config/defaults.py Adds task_assignment default section in generated default config dict.
README.md Documents task assignment as a supported feature.
DESIGN_SPEC.md Updates project structure and observability event module list to include task assignment.
CLAUDE.md Updates engine package description to mention task assignment.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +49 to +56
model_config = ConfigDict(frozen=True)

agent_identity: AgentIdentity = Field(description="Candidate agent")
score: float = Field(
ge=0.0,
le=1.0,
description="Match score (0.0-1.0)",
)
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

AssignmentCandidate uses ConfigDict(frozen=True) without allow_inf_nan=False, so score could potentially accept NaN/Inf values even though it has range constraints. Consider setting allow_inf_nan=False (as done in other numeric-heavy models) to prevent non-finite scores from entering the assignment pipeline.

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +95
model_config = ConfigDict(frozen=True)

task: Task = Field(description="The task to assign")
available_agents: tuple[AgentIdentity, ...] = Field(
description="Pool of agents to consider",
)
workloads: tuple[AgentWorkload, ...] = Field(
default=(),
description="Current workload snapshots per agent",
)
min_score: float = Field(
default=0.1,
ge=0.0,
le=1.0,
description="Minimum score threshold for eligibility",
)
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

AssignmentRequest doesn't set allow_inf_nan=False, so min_score may accept NaN/Inf (Pydantic constraints don't reliably reject NaN unless configured). That can lead to confusing behavior (e.g., NaN comparisons filtering all candidates). Consider adding allow_inf_nan=False to the model config.

Copilot uses AI. Check for mistakes.
Comment on lines +125 to +139
model_config = ConfigDict(frozen=True)

task_id: NotBlankStr = Field(description="Task identifier")
strategy_used: NotBlankStr = Field(
description="Name of the strategy used",
)
selected: AssignmentCandidate | None = Field(
default=None,
description="Selected candidate (None if no viable agent)",
)
alternatives: tuple[AssignmentCandidate, ...] = Field(
default=(),
description="Other candidates considered, ranked by score",
)
reason: NotBlankStr = Field(description="Explanation of decision")
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

AssignmentResult also lacks allow_inf_nan=False. If any strategy accidentally emits a non-finite score inside selected/alternatives, it will validate and then propagate into logs/metrics. Consider setting allow_inf_nan=False on this model as well for consistency with the rest of the codebase.

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +90
try:
result = self._strategy.assign(request)
except Exception:
logger.exception(
TASK_ASSIGNMENT_FAILED,
task_id=task.id,
strategy=self._strategy.name,
)
raise
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

The service logs logger.exception(...) for any strategy-raised exception and then re-raises. For expected/validated failures (e.g., TaskAssignmentError from ManualAssignmentStrategy), this will emit stack traces and can duplicate warnings already logged by the strategy. Consider catching TaskAssignmentError separately (log at warning/info without stack) and reserving logger.exception for unexpected exceptions only.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +46
class TaskAssignmentService:
"""Orchestrates task assignment via a pluggable strategy.

Validates task status before delegating to the strategy.
Does NOT mutate the task — callers are responsible for any
subsequent status transitions.
"""

__slots__ = ("_strategy",)

def __init__(self, strategy: TaskAssignmentStrategy) -> None:
self._strategy = strategy

def assign(self, request: AssignmentRequest) -> AssignmentResult:
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

PR description says TaskAssignmentService “resolves strategy by name from STRATEGY_MAP”, but this implementation requires a concrete TaskAssignmentStrategy instance to be injected and never references STRATEGY_MAP. Either update the service to support name-based resolution (and likely accept a config/strategy name), or adjust the PR description/docs to match the actual API.

Copilot uses AI. Check for mistakes.
Comment on lines +388 to +395
"Maximum concurrent tasks per agent. "
"Reserved for engine-layer enforcement — the engine "
"should pass this value to AssignmentRequest when "
"wiring up the assignment pipeline."
),
)


Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

max_concurrent_tasks_per_agent's description says the engine “should pass this value to AssignmentRequest”, but AssignmentRequest currently has no field to carry this value and none of the strategies enforce it. Either remove/reword this guidance, or add the corresponding request field/enforcement wiring so the config is actionable.

Suggested change
"Maximum concurrent tasks per agent. "
"Reserved for engine-layer enforcement — the engine "
"should pass this value to AssignmentRequest when "
"wiring up the assignment pipeline."
),
)
"Maximum concurrent tasks an agent is intended to handle. "
"Actual enforcement must be implemented by the task "
"assignment/engine layer."
),
)

Copilot uses AI. Check for mistakes.
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 8, 2026

Greptile Summary

This PR introduces a well-structured task assignment subsystem with three pluggable strategies (ManualAssignmentStrategy, RoleBasedAssignmentStrategy, LoadBalancedAssignmentStrategy) behind a TaskAssignmentStrategy protocol, a TaskAssignmentService orchestrator that enforces task-status gating, frozen Pydantic domain models, event constants, and a TaskAssignmentConfig added to RootConfig. The implementation is clean, and previous critical findings (double-logging, inactive-agent scoring vulnerability) have been addressed. Test coverage is thorough (96.6%, 3804 tests).

Three issues warrant attention before merge:

  • TaskAssignmentConfig.strategy accepts any string — there is no @model_validator checking the value against the known strategy names ("manual", "role_based", "load_balanced"), so a typo or invalid strategy name passes validation cleanly and fails only at runtime.

  • max_concurrent_tasks_per_agent is declared but never enforced — the config field exists but is never read or applied by LoadBalancedAssignmentStrategy or any other assignment logic. The field's own docstring acknowledges this gap ("Actual enforcement must be implemented by the engine/assignment layer"), but no such enforcement exists.

  • Inconsistent "no eligible agent" signalingManualAssignmentStrategy raises NoEligibleAgentError; the two scoring strategies return AssignmentResult(selected=None, ...). While TaskAssignmentService handles both patterns, the TaskAssignmentStrategy protocol's docstring does not document this contract, which could lead downstream callers to mishandle the case when no eligible agent exists.

Confidence Score: 3/5

  • Safe to merge with low immediate risk, but two config-level issues (unvalidated strategy name, unenforced concurrency cap) should be resolved before downstream callers depend on them.
  • The core implementation is solid and previous critical findings have been addressed. However, TaskAssignmentConfig.strategy is not validated against STRATEGY_MAP (a misconfiguration passes silently), and max_concurrent_tasks_per_agent is documented but completely unenforced in the assignment layer—both are logic-level gaps that affect operational correctness once the config is wired to runtime. The inconsistent error-handling contract between strategies is a lower-severity API design concern that should be clarified in the protocol documentation.
  • src/ai_company/config/schema.py (strategy validation), src/ai_company/engine/assignment/strategies.py (concurrency cap enforcement + consistent error signaling).

Sequence Diagram

sequenceDiagram
    participant Caller
    participant TaskAssignmentService
    participant Strategy as TaskAssignmentStrategy<br/>(Manual / RoleBased / LoadBalanced)
    participant AgentTaskScorer
    participant STRATEGY_MAP

    Caller->>TaskAssignmentService: assign(AssignmentRequest)
    TaskAssignmentService->>TaskAssignmentService: validate task.status ∈ {CREATED, FAILED, INTERRUPTED}
    alt invalid status
        TaskAssignmentService-->>Caller: raise TaskAssignmentError
    end
    TaskAssignmentService->>Strategy: assign(request)
    alt ManualAssignmentStrategy
        Strategy->>Strategy: find agent by task.assigned_to
        alt agent missing or inactive
            Strategy-->>TaskAssignmentService: raise NoEligibleAgentError
        end
        Strategy-->>TaskAssignmentService: AssignmentResult(selected=candidate)
    else RoleBased / LoadBalanced
        Strategy->>AgentTaskScorer: score(agent, subtask) per ACTIVE agent
        AgentTaskScorer-->>Strategy: RoutingCandidate (score 0.0–1.0)
        Strategy->>Strategy: filter by min_score, sort by score / workload
        alt no candidates above threshold
            Strategy-->>TaskAssignmentService: AssignmentResult(selected=None)
        else candidates found
            Strategy-->>TaskAssignmentService: AssignmentResult(selected=best, alternatives=[...])
        end
    end
    alt TaskAssignmentError raised
        TaskAssignmentService-->>Caller: re-raise (no extra logging)
    else unexpected exception
        TaskAssignmentService->>TaskAssignmentService: logger.exception(TASK_ASSIGNMENT_FAILED)
        TaskAssignmentService-->>Caller: re-raise
    end
    alt result.selected is not None
        TaskAssignmentService->>TaskAssignmentService: log AGENT_SELECTED + COMPLETE
    else result.selected is None
        TaskAssignmentService->>TaskAssignmentService: log WARNING(NO_ELIGIBLE) + COMPLETE
    end
    TaskAssignmentService-->>Caller: AssignmentResult
Loading

Last reviewed commit: ddb34bb

- Add inactive agent guard in scoring pipeline (CRITICAL)
- Split broad except to avoid double-logging TaskAssignmentError
- Add allow_inf_nan=False to all assignment models
- Add uniqueness validators for agent IDs and workload entries
- Update DESIGN_SPEC.md: §6.4 current state, §15.3 engine dir, §15.5 conventions
- Extract _find_designated_agent for ManualAssignmentStrategy
- Add __slots__ to ManualAssignmentStrategy
- Clarify max_concurrent_tasks_per_agent description
- Add WARNING log when no eligible agent found
- Strengthen protocol docstring for sync-only requirement
- Parametrize min_score boundary tests, add blank/NaN/Inf edge cases
- Add unexpected exception propagation test
- Add LoadBalanced integration test with workloads
- Merge TestAssignmentRequestValidation into TestAssignmentRequest
@Aureliolo Aureliolo merged commit c7f1b26 into main Mar 8, 2026
8 of 9 checks passed
@Aureliolo Aureliolo deleted the feat/task-assignment-integration-test branch March 8, 2026 18:27
Comment on lines +343 to +363
workload_map: dict[str, int] = {
w.agent_id: w.active_task_count for w in request.workloads
}

if workload_map:
candidates.sort(
key=lambda c: (
workload_map.get(str(c.agent_identity.id), 0),
-c.score,
),
)
logger.debug(
TASK_ASSIGNMENT_WORKLOAD_BALANCED,
task_id=request.task.id,
agent_name=candidates[0].agent_identity.name,
workload=workload_map.get(
str(candidates[0].agent_identity.id),
0,
),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

max_concurrent_tasks_per_agent limit is declared but never enforced

TaskAssignmentConfig.max_concurrent_tasks_per_agent (lines 383–391) is configured as the "maximum concurrent tasks an agent is intended to handle", but LoadBalancedAssignmentStrategy never reads or respects this limit. It sorts by ascending workload and picks the minimum, but will happily select an agent that already has 10 active tasks if that is the lowest workload in the pool—even when the config says the limit is 5.

The config field's own docstring acknowledges this gap ("Actual enforcement must be implemented by the engine/assignment layer"), but no such enforcement exists in either the service or the strategies. This means the config value is decorative and the feature is half-implemented.

To properly enforce the cap, add max_concurrent_tasks_per_agent: int to AssignmentRequest so the limit flows from config into the request, then filter candidates in the workload-sorting block before selecting the winner:

if workload_map:
    cap = request.max_concurrent_tasks_per_agent
    candidates = [
        c for c in candidates
        if workload_map.get(str(c.agent_identity.id), 0) < cap
    ]

Without this connection, the config field provides no runtime safety guarantee.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ai_company/engine/assignment/strategies.py
Line: 343-363

Comment:
`max_concurrent_tasks_per_agent` limit is declared but never enforced

`TaskAssignmentConfig.max_concurrent_tasks_per_agent` (lines 383–391) is configured as the "maximum concurrent tasks an agent is intended to handle", but `LoadBalancedAssignmentStrategy` never reads or respects this limit. It sorts by ascending workload and picks the minimum, but will happily select an agent that already has 10 active tasks if that is the lowest workload in the pool—even when the config says the limit is 5.

The config field's own docstring acknowledges this gap ("Actual enforcement must be implemented by the engine/assignment layer"), but no such enforcement exists in either the service or the strategies. This means the config value is decorative and the feature is half-implemented.

To properly enforce the cap, add `max_concurrent_tasks_per_agent: int` to `AssignmentRequest` so the limit flows from config into the request, then filter candidates in the workload-sorting block before selecting the winner:

```python
if workload_map:
    cap = request.max_concurrent_tasks_per_agent
    candidates = [
        c for c in candidates
        if workload_map.get(str(c.agent_identity.id), 0) < cap
    ]
```

Without this connection, the config field provides no runtime safety guarantee.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +261 to +276
if not candidates:
logger.warning(
TASK_ASSIGNMENT_NO_ELIGIBLE,
task_id=request.task.id,
strategy=self.name,
agent_count=len(request.available_agents),
min_score=request.min_score,
)
return AssignmentResult(
task_id=request.task.id,
strategy_used=self.name,
reason=(
f"No agents scored above threshold "
f"{request.min_score} for task {request.task.id!r}"
),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inconsistent "no eligible agent" signaling across strategies

ManualAssignmentStrategy raises NoEligibleAgentError when the designated agent cannot be found or is inactive (lines 170, 186). RoleBasedAssignmentStrategy (here) and LoadBalancedAssignmentStrategy return AssignmentResult(selected=None, ...) instead.

While TaskAssignmentService handles both patterns correctly, the inconsistency leaks to downstream callers: a caller that only guards against TaskAssignmentError exceptions will silently receive a result with selected=None when a scoring strategy finds no eligible agents, and may proceed without an actual assignment.

The TaskAssignmentStrategy protocol's docstring should either:

  1. Document the contract explicitly—that either NoEligibleAgentError is raised OR selected=None is returned, and when each is used, or
  2. Standardize the behavior across all three strategies to always raise NoEligibleAgentError when no eligible agent exists.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ai_company/engine/assignment/strategies.py
Line: 261-276

Comment:
Inconsistent "no eligible agent" signaling across strategies

`ManualAssignmentStrategy` raises `NoEligibleAgentError` when the designated agent cannot be found or is inactive (lines 170, 186). `RoleBasedAssignmentStrategy` (here) and `LoadBalancedAssignmentStrategy` return `AssignmentResult(selected=None, ...)` instead.

While `TaskAssignmentService` handles both patterns correctly, the inconsistency leaks to downstream callers: a caller that only guards against `TaskAssignmentError` exceptions will silently receive a result with `selected=None` when a scoring strategy finds no eligible agents, and may proceed without an actual assignment.

The `TaskAssignmentStrategy` protocol's docstring should either:
1. Document the contract explicitly—that either `NoEligibleAgentError` is raised OR `selected=None` is returned, and when each is used, or
2. Standardize the behavior across all three strategies to always raise `NoEligibleAgentError` when no eligible agent exists.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +373 to +376
strategy: NotBlankStr = Field(
default="role_based",
description="Assignment strategy name",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

strategy field is not validated against known strategy names

TaskAssignmentConfig.strategy accepts any NotBlankStr with no validator checking it against the actual entries in STRATEGY_MAP ("manual", "role_based", "load_balanced"). A misconfigured value like "auction" or a typo like "role_base" passes config validation cleanly and will only fail (or be silently ignored) when the caller tries to resolve the strategy at runtime.

Add a @model_validator method to assert the value is a known key. The allowed set can be defined as a constant in engine/assignment/strategies.py to avoid circular imports between config and engine modules.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ai_company/config/schema.py
Line: 373-376

Comment:
`strategy` field is not validated against known strategy names

`TaskAssignmentConfig.strategy` accepts any `NotBlankStr` with no validator checking it against the actual entries in `STRATEGY_MAP` (`"manual"`, `"role_based"`, `"load_balanced"`). A misconfigured value like `"auction"` or a typo like `"role_base"` passes config validation cleanly and will only fail (or be silently ignored) when the caller tries to resolve the strategy at runtime.

Add a `@model_validator` method to assert the value is a known key. The allowed set can be defined as a constant in `engine/assignment/strategies.py` to avoid circular imports between `config` and `engine` modules.

How can I resolve this? If you propose a fix, please make it concise.

Aureliolo added a commit that referenced this pull request Mar 8, 2026
PR #174 — git_worktree.py:
- _validate_git_ref now accepts error_cls/event params so merge context
  raises WorkspaceMergeError and teardown raises WorkspaceCleanupError
- _run_git catches asyncio.CancelledError to kill subprocess before
  re-raising, preventing orphaned git processes

PR #172 — task assignment:
- TaskAssignmentConfig.strategy validated against known strategy names
- max_concurrent_tasks_per_agent now enforced in _score_and_filter_candidates
  via new AssignmentRequest.max_concurrent_tasks field
- TaskAssignmentStrategy protocol docstring documents error signaling contract

PR #171 — worktree skill:
- rebase uses --left-right --count with triple-dot to detect behind-main
- setup reuse path uses correct git worktree add (without -b)
- setup handles dirty working tree with stash/abort prompt
- status table shows both ahead and behind counts
- tree command provides circular dependency recovery guidance

PR #170 — meeting parsing:
- Fix assigned? regex to assigned (prevents false-positive assignee
  extraction from "assign to X" in action item descriptions)
Aureliolo added a commit that referenced this pull request Mar 8, 2026
…176)

## Summary

- Fix CI failures on main: 2 test assertion mismatches in cost-optimized
assignment tests + mypy `attr-defined` error in strategy registry test
- Address all Greptile post-merge review findings across PRs #170#175
(14 fixes total)

### PR #175 — Test assertion fixes (CI blockers)
- `"no cost data"` → `"insufficient cost data"` to match implementation
wording
- `unknown-dev` → `known-dev` winner assertion (all-or-nothing fallback,
sort stability)
- `getattr()` for `_scorer` access on protocol type (Windows/Linux mypy
difference)

### PR #174 — Workspace isolation
- `_validate_git_ref` raises context-appropriate exception types
(`WorkspaceMergeError` in merge, `WorkspaceCleanupError` in teardown)
- `_run_git` catches `asyncio.CancelledError` to kill subprocess before
re-raising (prevents orphaned git processes)

### PR #172 — Task assignment
- `TaskAssignmentConfig.strategy` validated against 6 known strategy
names
- `max_concurrent_tasks_per_agent` enforced via new
`AssignmentRequest.max_concurrent_tasks` field in
`_score_and_filter_candidates`
- `TaskAssignmentStrategy` protocol docstring documents error signaling
contract (raises vs `selected=None`)

### PR #171 — Worktree skill
- `rebase` uses `--left-right --count` with triple-dot to detect
behind-main worktrees
- `setup` reuse path uses `git worktree add` without `-b` for existing
branches
- `setup` handles dirty working tree with stash/abort prompt
- `status` table shows both ahead and behind counts
- `tree` provides circular dependency recovery guidance

### PR #170 — Meeting parsing
- `assigned?` → `assigned` regex fix (prevents false-positive assignee
extraction from "assign to X")

## Test plan

- [x] All 3988 tests pass (10 new tests added)
- [x] mypy strict: 0 errors (463 source files)
- [x] ruff lint + format: all clean
- [x] Coverage: 96.53% (threshold: 80%)
- [x] Pre-commit hooks pass

## Review coverage

Quick mode — automated checks only (lint, type-check, tests, coverage).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Aureliolo added a commit that referenced this pull request Mar 10, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.1](ai-company-v0.1.0...ai-company-v0.1.1)
(2026-03-10)


### Features

* add autonomy levels and approval timeout policies
([#42](#42),
[#126](#126))
([#197](#197))
([eecc25a](eecc25a))
* add CFO cost optimization service with anomaly detection, reports, and
approval decisions
([#186](#186))
([a7fa00b](a7fa00b))
* add code quality toolchain (ruff, mypy, pre-commit, dependabot)
([#63](#63))
([36681a8](36681a8))
* add configurable cost tiers and subscription/quota-aware tracking
([#67](#67))
([#185](#185))
([9baedfa](9baedfa))
* add container packaging, Docker Compose, and CI pipeline
([#269](#269))
([435bdfe](435bdfe)),
closes [#267](#267)
* add coordination error taxonomy classification pipeline
([#146](#146))
([#181](#181))
([70c7480](70c7480))
* add cost-optimized, hierarchical, and auction assignment strategies
([#175](#175))
([ce924fa](ce924fa)),
closes [#173](#173)
* add design specification, license, and project setup
([8669a09](8669a09))
* add env var substitution and config file auto-discovery
([#77](#77))
([7f53832](7f53832))
* add FastestStrategy routing + vendor-agnostic cleanup
([#140](#140))
([09619cb](09619cb)),
closes [#139](#139)
* add HR engine and performance tracking
([#45](#45),
[#47](#47))
([#193](#193))
([2d091ea](2d091ea))
* add issue auto-search and resolution verification to PR review skill
([#119](#119))
([deecc39](deecc39))
* add memory retrieval, ranking, and context injection pipeline
([#41](#41))
([873b0aa](873b0aa))
* add pluggable MemoryBackend protocol with models, config, and events
([#180](#180))
([46cfdd4](46cfdd4))
* add pluggable MemoryBackend protocol with models, config, and events
([#32](#32))
([46cfdd4](46cfdd4))
* add pluggable PersistenceBackend protocol with SQLite implementation
([#36](#36))
([f753779](f753779))
* add progressive trust and promotion/demotion subsystems
([#43](#43),
[#49](#49))
([3a87c08](3a87c08))
* add retry handler, rate limiter, and provider resilience
([#100](#100))
([b890545](b890545))
* add SecOps security agent with rule engine, audit log, and ToolInvoker
integration ([#40](#40))
([83b7b6c](83b7b6c))
* add shared org memory and memory consolidation/archival
([#125](#125),
[#48](#48))
([4a0832b](4a0832b))
* design unified provider interface
([#86](#86))
([3e23d64](3e23d64))
* expand template presets, rosters, and add inheritance
([#80](#80),
[#81](#81),
[#84](#84))
([15a9134](15a9134))
* implement agent runtime state vs immutable config split
([#115](#115))
([4cb1ca5](4cb1ca5))
* implement AgentEngine core orchestrator
([#11](#11))
([#143](#143))
([f2eb73a](f2eb73a))
* implement basic tool system (registry, invocation, results)
([#15](#15))
([c51068b](c51068b))
* implement built-in file system tools
([#18](#18))
([325ef98](325ef98))
* implement communication foundation — message bus, dispatcher, and
messenger ([#157](#157))
([8e71bfd](8e71bfd))
* implement company template system with 7 built-in presets
([#85](#85))
([cbf1496](cbf1496))
* implement conflict resolution protocol
([#122](#122))
([#166](#166))
([e03f9f2](e03f9f2))
* implement core entity and role system models
([#69](#69))
([acf9801](acf9801))
* implement crash recovery with fail-and-reassign strategy
([#149](#149))
([e6e91ed](e6e91ed))
* implement engine extensions — Plan-and-Execute loop and call
categorization
([#134](#134),
[#135](#135))
([#159](#159))
([9b2699f](9b2699f))
* implement enterprise logging system with structlog
([#73](#73))
([2f787e5](2f787e5))
* implement graceful shutdown with cooperative timeout strategy
([#130](#130))
([6592515](6592515))
* implement hierarchical delegation and loop prevention
([#12](#12),
[#17](#17))
([6be60b6](6be60b6))
* implement LiteLLM driver and provider registry
([#88](#88))
([ae3f18b](ae3f18b)),
closes [#4](#4)
* implement LLM decomposition strategy and workspace isolation
([#174](#174))
([aa0eefe](aa0eefe))
* implement meeting protocol system
([#123](#123))
([ee7caca](ee7caca))
* implement message and communication domain models
([#74](#74))
([560a5d2](560a5d2))
* implement model routing engine
([#99](#99))
([d3c250b](d3c250b))
* implement parallel agent execution
([#22](#22))
([#161](#161))
([65940b3](65940b3))
* implement per-call cost tracking service
([#7](#7))
([#102](#102))
([c4f1f1c](c4f1f1c))
* implement personality injection and system prompt construction
([#105](#105))
([934dd85](934dd85))
* implement single-task execution lifecycle
([#21](#21))
([#144](#144))
([c7e64e4](c7e64e4))
* implement subprocess sandbox for tool execution isolation
([#131](#131))
([#153](#153))
([3c8394e](3c8394e))
* implement task assignment subsystem with pluggable strategies
([#172](#172))
([c7f1b26](c7f1b26)),
closes [#26](#26)
[#30](#30)
* implement task decomposition and routing engine
([#14](#14))
([9c7fb52](9c7fb52))
* implement Task, Project, Artifact, Budget, and Cost domain models
([#71](#71))
([81eabf1](81eabf1))
* implement tool permission checking
([#16](#16))
([833c190](833c190))
* implement YAML config loader with Pydantic validation
([#59](#59))
([ff3a2ba](ff3a2ba))
* implement YAML config loader with Pydantic validation
([#75](#75))
([ff3a2ba](ff3a2ba))
* initialize project with uv, hatchling, and src layout
([39005f9](39005f9))
* initialize project with uv, hatchling, and src layout
([#62](#62))
([39005f9](39005f9))
* Litestar REST API, WebSocket feed, and approval queue (M6)
([#189](#189))
([29fcd08](29fcd08))
* make TokenUsage.total_tokens a computed field
([#118](#118))
([c0bab18](c0bab18)),
closes [#109](#109)
* parallel tool execution in ToolInvoker.invoke_all
([#137](#137))
([58517ee](58517ee))
* testing framework, CI pipeline, and M0 gap fixes
([#64](#64))
([f581749](f581749))
* wire all modules into observability system
([#97](#97))
([f7a0617](f7a0617))


### Bug Fixes

* address Greptile post-merge review findings from PRs
[#170](https://github.com/Aureliolo/ai-company/issues/170)-[#175](https://github.com/Aureliolo/ai-company/issues/175)
([#176](#176))
([c5ca929](c5ca929))
* address post-merge review feedback from PRs
[#164](https://github.com/Aureliolo/ai-company/issues/164)-[#167](https://github.com/Aureliolo/ai-company/issues/167)
([#170](#170))
([3bf897a](3bf897a)),
closes [#169](#169)
* enforce strict mypy on test files
([#89](#89))
([aeeff8c](aeeff8c))
* harden Docker sandbox, MCP bridge, and code runner
([#50](#50),
[#53](#53))
([d5e1b6e](d5e1b6e))
* harden git tools security + code quality improvements
([#150](#150))
([000a325](000a325))
* harden subprocess cleanup, env filtering, and shutdown resilience
([#155](#155))
([d1fe1fb](d1fe1fb))
* incorporate post-merge feedback + pre-PR review fixes
([#164](#164))
([c02832a](c02832a))
* pre-PR review fixes for post-merge findings
([#183](#183))
([26b3108](26b3108))
* strengthen immutability for BaseTool schema and ToolInvoker boundaries
([#117](#117))
([7e5e861](7e5e861))


### Performance

* harden non-inferable principle implementation
([#195](#195))
([02b5f4e](02b5f4e)),
closes [#188](#188)


### Refactoring

* adopt NotBlankStr across all models
([#108](#108))
([#120](#120))
([ef89b90](ef89b90))
* extract _SpendingTotals base class from spending summary models
([#111](#111))
([2f39c1b](2f39c1b))
* harden BudgetEnforcer with error handling, validation extraction, and
review fixes
([#182](#182))
([c107bf9](c107bf9))
* harden personality profiles, department validation, and template
rendering ([#158](#158))
([10b2299](10b2299))
* pre-PR review improvements for ExecutionLoop + ReAct loop
([#124](#124))
([8dfb3c0](8dfb3c0))
* split events.py into per-domain event modules
([#136](#136))
([e9cba89](e9cba89))


### Documentation

* add ADR-001 memory layer evaluation and selection
([#178](#178))
([db3026f](db3026f)),
closes [#39](#39)
* add agent scaling research findings to DESIGN_SPEC
([#145](#145))
([57e487b](57e487b))
* add CLAUDE.md, contributing guide, and dev documentation
([#65](#65))
([55c1025](55c1025)),
closes [#54](#54)
* add crash recovery, sandboxing, analytics, and testing decisions
([#127](#127))
([5c11595](5c11595))
* address external review feedback with MVP scope and new protocols
([#128](#128))
([3b30b9a](3b30b9a))
* expand design spec with pluggable strategy protocols
([#121](#121))
([6832db6](6832db6))
* finalize 23 design decisions (ADR-002)
([#190](#190))
([8c39742](8c39742))
* update project docs for M2.5 conventions and add docs-consistency
review agent
([#114](#114))
([99766ee](99766ee))


### Tests

* add e2e single agent integration tests
([#24](#24))
([#156](#156))
([f566fb4](f566fb4))
* add provider adapter integration tests
([#90](#90))
([40a61f4](40a61f4))


### CI/CD

* add Release Please for automated versioning and GitHub Releases
([#278](#278))
([a488758](a488758))
* bump actions/checkout from 4 to 6
([#95](#95))
([1897247](1897247))
* bump actions/upload-artifact from 4 to 7
([#94](#94))
([27b1517](27b1517))
* harden CI/CD pipeline
([#92](#92))
([ce4693c](ce4693c))
* split vulnerability scans into critical-fail and high-warn tiers
([#277](#277))
([aba48af](aba48af))


### Maintenance

* add /worktree skill for parallel worktree management
([#171](#171))
([951e337](951e337))
* add design spec context loading to research-link skill
([8ef9685](8ef9685))
* add post-merge-cleanup skill
([#70](#70))
([f913705](f913705))
* add pre-pr-review skill and update CLAUDE.md
([#103](#103))
([92e9023](92e9023))
* add research-link skill and rename skill files to SKILL.md
([#101](#101))
([651c577](651c577))
* bump aiosqlite from 0.21.0 to 0.22.1
([#191](#191))
([3274a86](3274a86))
* bump pyyaml from 6.0.2 to 6.0.3 in the minor-and-patch group
([#96](#96))
([0338d0c](0338d0c))
* bump ruff from 0.15.4 to 0.15.5
([a49ee46](a49ee46))
* fix M0 audit items
([#66](#66))
([c7724b5](c7724b5))
* pin setup-uv action to full SHA
([#281](#281))
([4448002](4448002))
* post-audit cleanup — PEP 758, loggers, bug fixes, refactoring, tests,
hookify rules
([#148](#148))
([c57a6a9](c57a6a9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Aureliolo added a commit that referenced this pull request Mar 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.0](v0.0.0...v0.1.0)
(2026-03-11)


### Features

* add autonomy levels and approval timeout policies
([#42](#42),
[#126](#126))
([#197](#197))
([eecc25a](eecc25a))
* add CFO cost optimization service with anomaly detection, reports, and
approval decisions
([#186](#186))
([a7fa00b](a7fa00b))
* add code quality toolchain (ruff, mypy, pre-commit, dependabot)
([#63](#63))
([36681a8](36681a8))
* add configurable cost tiers and subscription/quota-aware tracking
([#67](#67))
([#185](#185))
([9baedfa](9baedfa))
* add container packaging, Docker Compose, and CI pipeline
([#269](#269))
([435bdfe](435bdfe)),
closes [#267](#267)
* add coordination error taxonomy classification pipeline
([#146](#146))
([#181](#181))
([70c7480](70c7480))
* add cost-optimized, hierarchical, and auction assignment strategies
([#175](#175))
([ce924fa](ce924fa)),
closes [#173](#173)
* add design specification, license, and project setup
([8669a09](8669a09))
* add env var substitution and config file auto-discovery
([#77](#77))
([7f53832](7f53832))
* add FastestStrategy routing + vendor-agnostic cleanup
([#140](#140))
([09619cb](09619cb)),
closes [#139](#139)
* add HR engine and performance tracking
([#45](#45),
[#47](#47))
([#193](#193))
([2d091ea](2d091ea))
* add issue auto-search and resolution verification to PR review skill
([#119](#119))
([deecc39](deecc39))
* add mandatory JWT + API key authentication
([#256](#256))
([c279cfe](c279cfe))
* add memory retrieval, ranking, and context injection pipeline
([#41](#41))
([873b0aa](873b0aa))
* add pluggable MemoryBackend protocol with models, config, and events
([#180](#180))
([46cfdd4](46cfdd4))
* add pluggable MemoryBackend protocol with models, config, and events
([#32](#32))
([46cfdd4](46cfdd4))
* add pluggable output scan response policies
([#263](#263))
([b9907e8](b9907e8))
* add pluggable PersistenceBackend protocol with SQLite implementation
([#36](#36))
([f753779](f753779))
* add progressive trust and promotion/demotion subsystems
([#43](#43),
[#49](#49))
([3a87c08](3a87c08))
* add retry handler, rate limiter, and provider resilience
([#100](#100))
([b890545](b890545))
* add SecOps security agent with rule engine, audit log, and ToolInvoker
integration ([#40](#40))
([83b7b6c](83b7b6c))
* add shared org memory and memory consolidation/archival
([#125](#125),
[#48](#48))
([4a0832b](4a0832b))
* design unified provider interface
([#86](#86))
([3e23d64](3e23d64))
* expand template presets, rosters, and add inheritance
([#80](#80),
[#81](#81),
[#84](#84))
([15a9134](15a9134))
* implement agent runtime state vs immutable config split
([#115](#115))
([4cb1ca5](4cb1ca5))
* implement AgentEngine core orchestrator
([#11](#11))
([#143](#143))
([f2eb73a](f2eb73a))
* implement AuditRepository for security audit log persistence
([#279](#279))
([94bc29f](94bc29f))
* implement basic tool system (registry, invocation, results)
([#15](#15))
([c51068b](c51068b))
* implement built-in file system tools
([#18](#18))
([325ef98](325ef98))
* implement communication foundation — message bus, dispatcher, and
messenger ([#157](#157))
([8e71bfd](8e71bfd))
* implement company template system with 7 built-in presets
([#85](#85))
([cbf1496](cbf1496))
* implement conflict resolution protocol
([#122](#122))
([#166](#166))
([e03f9f2](e03f9f2))
* implement core entity and role system models
([#69](#69))
([acf9801](acf9801))
* implement crash recovery with fail-and-reassign strategy
([#149](#149))
([e6e91ed](e6e91ed))
* implement engine extensions — Plan-and-Execute loop and call
categorization
([#134](#134),
[#135](#135))
([#159](#159))
([9b2699f](9b2699f))
* implement enterprise logging system with structlog
([#73](#73))
([2f787e5](2f787e5))
* implement graceful shutdown with cooperative timeout strategy
([#130](#130))
([6592515](6592515))
* implement hierarchical delegation and loop prevention
([#12](#12),
[#17](#17))
([6be60b6](6be60b6))
* implement LiteLLM driver and provider registry
([#88](#88))
([ae3f18b](ae3f18b)),
closes [#4](#4)
* implement LLM decomposition strategy and workspace isolation
([#174](#174))
([aa0eefe](aa0eefe))
* implement meeting protocol system
([#123](#123))
([ee7caca](ee7caca))
* implement message and communication domain models
([#74](#74))
([560a5d2](560a5d2))
* implement model routing engine
([#99](#99))
([d3c250b](d3c250b))
* implement parallel agent execution
([#22](#22))
([#161](#161))
([65940b3](65940b3))
* implement per-call cost tracking service
([#7](#7))
([#102](#102))
([c4f1f1c](c4f1f1c))
* implement personality injection and system prompt construction
([#105](#105))
([934dd85](934dd85))
* implement single-task execution lifecycle
([#21](#21))
([#144](#144))
([c7e64e4](c7e64e4))
* implement subprocess sandbox for tool execution isolation
([#131](#131))
([#153](#153))
([3c8394e](3c8394e))
* implement task assignment subsystem with pluggable strategies
([#172](#172))
([c7f1b26](c7f1b26)),
closes [#26](#26)
[#30](#30)
* implement task decomposition and routing engine
([#14](#14))
([9c7fb52](9c7fb52))
* implement Task, Project, Artifact, Budget, and Cost domain models
([#71](#71))
([81eabf1](81eabf1))
* implement tool permission checking
([#16](#16))
([833c190](833c190))
* implement YAML config loader with Pydantic validation
([#59](#59))
([ff3a2ba](ff3a2ba))
* implement YAML config loader with Pydantic validation
([#75](#75))
([ff3a2ba](ff3a2ba))
* initialize project with uv, hatchling, and src layout
([39005f9](39005f9))
* initialize project with uv, hatchling, and src layout
([#62](#62))
([39005f9](39005f9))
* Litestar REST API, WebSocket feed, and approval queue (M6)
([#189](#189))
([29fcd08](29fcd08))
* make TokenUsage.total_tokens a computed field
([#118](#118))
([c0bab18](c0bab18)),
closes [#109](#109)
* parallel tool execution in ToolInvoker.invoke_all
([#137](#137))
([58517ee](58517ee))
* testing framework, CI pipeline, and M0 gap fixes
([#64](#64))
([f581749](f581749))
* wire all modules into observability system
([#97](#97))
([f7a0617](f7a0617))


### Bug Fixes

* address Greptile post-merge review findings from PRs
[#170](https://github.com/Aureliolo/ai-company/issues/170)-[#175](https://github.com/Aureliolo/ai-company/issues/175)
([#176](#176))
([c5ca929](c5ca929))
* address post-merge review feedback from PRs
[#164](https://github.com/Aureliolo/ai-company/issues/164)-[#167](https://github.com/Aureliolo/ai-company/issues/167)
([#170](#170))
([3bf897a](3bf897a)),
closes [#169](#169)
* enforce strict mypy on test files
([#89](#89))
([aeeff8c](aeeff8c))
* harden Docker sandbox, MCP bridge, and code runner
([#50](#50),
[#53](#53))
([d5e1b6e](d5e1b6e))
* harden git tools security + code quality improvements
([#150](#150))
([000a325](000a325))
* harden subprocess cleanup, env filtering, and shutdown resilience
([#155](#155))
([d1fe1fb](d1fe1fb))
* incorporate post-merge feedback + pre-PR review fixes
([#164](#164))
([c02832a](c02832a))
* pre-PR review fixes for post-merge findings
([#183](#183))
([26b3108](26b3108))
* resolve circular imports, bump litellm, fix release tag format
([#286](#286))
([a6659b5](a6659b5))
* strengthen immutability for BaseTool schema and ToolInvoker boundaries
([#117](#117))
([7e5e861](7e5e861))


### Performance

* harden non-inferable principle implementation
([#195](#195))
([02b5f4e](02b5f4e)),
closes [#188](#188)


### Refactoring

* adopt NotBlankStr across all models
([#108](#108))
([#120](#120))
([ef89b90](ef89b90))
* extract _SpendingTotals base class from spending summary models
([#111](#111))
([2f39c1b](2f39c1b))
* harden BudgetEnforcer with error handling, validation extraction, and
review fixes
([#182](#182))
([c107bf9](c107bf9))
* harden personality profiles, department validation, and template
rendering ([#158](#158))
([10b2299](10b2299))
* pre-PR review improvements for ExecutionLoop + ReAct loop
([#124](#124))
([8dfb3c0](8dfb3c0))
* split events.py into per-domain event modules
([#136](#136))
([e9cba89](e9cba89))


### Documentation

* add ADR-001 memory layer evaluation and selection
([#178](#178))
([db3026f](db3026f)),
closes [#39](#39)
* add agent scaling research findings to DESIGN_SPEC
([#145](#145))
([57e487b](57e487b))
* add CLAUDE.md, contributing guide, and dev documentation
([#65](#65))
([55c1025](55c1025)),
closes [#54](#54)
* add crash recovery, sandboxing, analytics, and testing decisions
([#127](#127))
([5c11595](5c11595))
* address external review feedback with MVP scope and new protocols
([#128](#128))
([3b30b9a](3b30b9a))
* expand design spec with pluggable strategy protocols
([#121](#121))
([6832db6](6832db6))
* finalize 23 design decisions (ADR-002)
([#190](#190))
([8c39742](8c39742))
* update project docs for M2.5 conventions and add docs-consistency
review agent
([#114](#114))
([99766ee](99766ee))


### Tests

* add e2e single agent integration tests
([#24](#24))
([#156](#156))
([f566fb4](f566fb4))
* add provider adapter integration tests
([#90](#90))
([40a61f4](40a61f4))


### CI/CD

* add Release Please for automated versioning and GitHub Releases
([#278](#278))
([a488758](a488758))
* bump actions/checkout from 4 to 6
([#95](#95))
([1897247](1897247))
* bump actions/upload-artifact from 4 to 7
([#94](#94))
([27b1517](27b1517))
* bump anchore/scan-action from 6.5.1 to 7.3.2
([#271](#271))
([80a1c15](80a1c15))
* bump docker/build-push-action from 6.19.2 to 7.0.0
([#273](#273))
([dd0219e](dd0219e))
* bump docker/login-action from 3.7.0 to 4.0.0
([#272](#272))
([33d6238](33d6238))
* bump docker/metadata-action from 5.10.0 to 6.0.0
([#270](#270))
([baee04e](baee04e))
* bump docker/setup-buildx-action from 3.12.0 to 4.0.0
([#274](#274))
([5fc06f7](5fc06f7))
* bump sigstore/cosign-installer from 3.9.1 to 4.1.0
([#275](#275))
([29dd16c](29dd16c))
* harden CI/CD pipeline
([#92](#92))
([ce4693c](ce4693c))
* split vulnerability scans into critical-fail and high-warn tiers
([#277](#277))
([aba48af](aba48af))


### Maintenance

* add /worktree skill for parallel worktree management
([#171](#171))
([951e337](951e337))
* add design spec context loading to research-link skill
([8ef9685](8ef9685))
* add post-merge-cleanup skill
([#70](#70))
([f913705](f913705))
* add pre-pr-review skill and update CLAUDE.md
([#103](#103))
([92e9023](92e9023))
* add research-link skill and rename skill files to SKILL.md
([#101](#101))
([651c577](651c577))
* bump aiosqlite from 0.21.0 to 0.22.1
([#191](#191))
([3274a86](3274a86))
* bump pyyaml from 6.0.2 to 6.0.3 in the minor-and-patch group
([#96](#96))
([0338d0c](0338d0c))
* bump ruff from 0.15.4 to 0.15.5
([a49ee46](a49ee46))
* fix M0 audit items
([#66](#66))
([c7724b5](c7724b5))
* **main:** release ai-company 0.1.1
([#282](#282))
([2f4703d](2f4703d))
* pin setup-uv action to full SHA
([#281](#281))
([4448002](4448002))
* post-audit cleanup — PEP 758, loggers, bug fixes, refactoring, tests,
hookify rules
([#148](#148))
([c57a6a9](c57a6a9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Signed-off-by: Aurelio <19254254+Aureliolo@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integration test: multi-agent delegation with task decomposition Implement task assignment strategies (role-based, load-balanced, manual)

2 participants