Skip to content

feat: add cost-optimized, hierarchical, and auction assignment strategies#175

Merged
Aureliolo merged 4 commits intomainfrom
feat/additional-assignment-strategies
Mar 8, 2026
Merged

feat: add cost-optimized, hierarchical, and auction assignment strategies#175
Aureliolo merged 4 commits intomainfrom
feat/additional-assignment-strategies

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • 3 new assignment strategies: CostOptimizedAssignmentStrategy (cheapest agent by total_cost_usd), HierarchicalAssignmentStrategy (delegates to subordinates via org chart), AuctionAssignmentStrategy (score × availability bidding)
  • Strategy registry extracted to registry.py — keeps strategies.py under 800-line limit, uses TaskAssignmentStrategy protocol type instead of private _StrategyType union
  • 3 new observability events: task_assignment.capability_fallback, task_assignment.delegator.resolved, task_assignment.hierarchy.transitive_fallback
  • Test suite split from 1 large file into 5 focused files (per-strategy + registry) with shared helpers in conftest.py
  • Fix: mypy union-attr error in llm.py retry logic (pre-existing on main)
  • Docs: DESIGN_SPEC §15.3/§15.5 updated, README task assignment bullet updated

Closes #173

Pre-reviewed by 9 agents, 22 findings addressed

Agent Findings
code-reviewer 5
python-reviewer 4
pr-test-analyzer 3
silent-failure-hunter 2
type-design-analyzer 2
docs-consistency 3
logging-audit 2
resilience-audit 0
comment-analyzer 1

Test plan

  • All 3976 tests pass locally
  • 96.51% coverage (threshold: 80%)
  • mypy strict passes (463 source files)
  • ruff lint + format clean
  • CI green on push

…gies (#173)

Implement the 3 remaining strategies from DESIGN_SPEC §6.4:
- CostOptimizedAssignmentStrategy: sorts by total_cost_usd ascending
- HierarchicalAssignmentStrategy: delegates to subordinates via HierarchyResolver
- AuctionAssignmentStrategy: bids = score * availability_factor

Adds build_strategy_map() factory for runtime hierarchical inclusion,
updates STRATEGY_MAP to 5 entries, and adds 28 new unit tests (57 total).
- Fix dead `selected` assignment in CostOptimizedAssignmentStrategy
- Extract registry (STRATEGY_MAP, build_strategy_map) to registry.py
  bringing strategies.py under 800-line limit
- Use TaskAssignmentStrategy protocol in public API types instead of
  private _StrategyType union
- Fix misleading reason strings when no workload/cost data provided
- Add fallback logging (TASK_ASSIGNMENT_CAPABILITY_FALLBACK) when
  strategies degrade to capability-only sorting
- Add delegator resolution and transitive hierarchy fallback logging
- Fix docstrings (CostOptimized, Hierarchical, Auction)
- Split 1291-line test file into 5 focused files with shared conftest
- Add missing tests: build_strategy_map(scorer=...), hierarchical
  subordinates below min_score, reports not in pool
- Add STRATEGY_MAP negative assertion for hierarchical
- Update DESIGN_SPEC.md §15.5 and README.md for six strategies
Guard `last_response.content` access with a None check to fix
mypy [union-attr] error introduced in the workspace isolation PR.
Copilot AI review requested due to automatic review settings March 8, 2026 19:19
@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: c81c055d-0a1c-41e8-a966-811c168ddf0b

📥 Commits

Reviewing files that changed from the base of the PR and between 31709c6 and 64d4e51.

📒 Files selected for processing (8)
  • src/ai_company/core/task.py
  • src/ai_company/engine/assignment/registry.py
  • src/ai_company/engine/assignment/strategies.py
  • src/ai_company/engine/decomposition/llm.py
  • tests/unit/engine/test_assignment_strategies.py
  • tests/unit/engine/test_auction_assignment.py
  • tests/unit/engine/test_hierarchical_assignment.py
  • tests/unit/engine/test_strategy_registry.py

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Three new assignment strategies: Cost-Optimized, Hierarchical, Auction
    • Dynamic strategy registry with a factory to build the runtime strategy map; new strategy identifiers are publicly exposed
  • Bug Fixes

    • Guarded retry handling when prior response data is missing
  • Documentation

    • Updated design spec and README to describe expanded strategy set and registry
  • Tests

    • Comprehensive new and updated unit tests covering strategies and the registry
  • Observability

    • Added event types for cost, hierarchical, auction, and fallback flows

Walkthrough

This PR expands task assignment from three to six strategies, adds a registry and factory (STRATEGY_MAP, build_strategy_map) to assemble strategies at runtime, exposes new public symbols, introduces observability events, and adds comprehensive unit tests for the new strategies and registry behavior.

Changes

Cohort / File(s) Summary
Design & Docs
DESIGN_SPEC.md, README.md
Updated design and README to describe M4 state: six strategies, registry-based STRATEGY_MAP, and build_strategy_map factory.
Public API surface
src/ai_company/engine/__init__.py, src/ai_company/engine/assignment/__init__.py
Re-exported new strategy name constants, new strategy classes, and build_strategy_map to extend the package public surface.
Assignment Registry
src/ai_company/engine/assignment/registry.py
New module: STRATEGY_MAP (immutable map of five pre-built strategies) and build_strategy_map(*, hierarchy=None, scorer=None) factory that includes Hierarchical when a HierarchyResolver is supplied; returns MappingProxyType.
Strategy implementations
src/ai_company/engine/assignment/strategies.py
Expanded strategies from 3→6: added CostOptimizedAssignmentStrategy, HierarchicalAssignmentStrategy, AuctionAssignmentStrategy, plus STRATEGY_NAME_COST_OPTIMIZED, STRATEGY_NAME_HIERARCHICAL, STRATEGY_NAME_AUCTION. New scoring/bidding/hierarchy logic and new logging events integrated.
Observability events
src/ai_company/observability/events/task_assignment.py
Added seven new event constants for cost-optimized, hierarchical delegation, auction bids/wins, capability fallback, delegator resolution, and transitive hierarchy fallback.
Decomposition safety fix
src/ai_company/engine/decomposition/llm.py
Guarded assistant message construction against missing last_response in the LLM decomposition retry path.
Test fixtures & helpers
tests/unit/engine/conftest.py
Added test helpers: make_assignment_model_config(), make_assignment_agent(...), make_assignment_task(...) and imported AgentStatus.
Tests — updated & new
tests/unit/engine/test_assignment_strategies.py, tests/unit/engine/test_auction_assignment.py, tests/unit/engine/test_cost_optimized_assignment.py, tests/unit/engine/test_hierarchical_assignment.py, tests/unit/engine/test_strategy_registry.py
Refactored existing strategy tests to use fixtures; added comprehensive unit tests for Auction, Cost-Optimized, Hierarchical strategies; added registry tests validating STRATEGY_MAP contents, immutability, protocol conformance, and build_strategy_map behavior.
Core model docs
src/ai_company/core/task.py
Minor docstring updates for created_by and delegation_chain field descriptions (agent name vs agent ID).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant HierarchicalAssignmentStrategy
    participant HierarchyResolver
    participant AgentTaskScorer

    Client->>HierarchicalAssignmentStrategy: assign(request, agents)
    HierarchicalAssignmentStrategy->>HierarchicalAssignmentStrategy: resolve delegator (delegation_chain or created_by)
    HierarchicalAssignmentStrategy->>HierarchyResolver: get_direct_and_transitive_subordinates(delegator)
    HierarchyResolver-->>HierarchicalAssignmentStrategy: subordinate_ids
    HierarchicalAssignmentStrategy->>HierarchicalAssignmentStrategy: filter agents to subordinates
    HierarchicalAssignmentStrategy->>AgentTaskScorer: score(task, filtered_agents)
    AgentTaskScorer-->>HierarchicalAssignmentStrategy: scores
    HierarchicalAssignmentStrategy->>HierarchicalAssignmentStrategy: select best (min_score threshold)
    HierarchicalAssignmentStrategy-->>Client: AssignmentResult (selected / none)
Loading
sequenceDiagram
    participant Client
    participant AuctionAssignmentStrategy
    participant AgentTaskScorer
    participant BidCalculator

    Client->>AuctionAssignmentStrategy: assign(request, agents)
    AuctionAssignmentStrategy->>AgentTaskScorer: score(task, agents)
    AgentTaskScorer-->>AuctionAssignmentStrategy: scores
    loop for each eligible agent (score >= min_score)
        AuctionAssignmentStrategy->>BidCalculator: compute bid = score * availability_factor
        BidCalculator-->>AuctionAssignmentStrategy: bid_value
    end
    AuctionAssignmentStrategy->>AuctionAssignmentStrategy: pick highest bid
    AuctionAssignmentStrategy-->>Client: AssignmentResult (winner / none)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding three new assignment strategies (cost-optimized, hierarchical, auction).
Description check ✅ Passed The description is well-structured and relates directly to the changeset, covering the three new strategies, registry extraction, observability events, test suite restructuring, and documentation updates.
Linked Issues check ✅ Passed All coding-related requirements from #173 are met: three new strategies implemented with proper registration, unit tests provided, and DESIGN_SPEC updated to M4 state.
Out of Scope Changes check ✅ Passed All changes align with PR objectives: new strategies, registry infrastructure, observability events, test restructuring, llm.py fix, and documentation updates are all within scope of #173.
Docstring Coverage ✅ Passed Docstring coverage is 87.84% which is sufficient. The required threshold is 80.00%.

✏️ 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/additional-assignment-strategies
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/additional-assignment-strategies

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 significantly enhances the task assignment capabilities by introducing three advanced strategies: cost-optimized, hierarchical, and auction-based. These additions provide more sophisticated mechanisms for matching tasks to agents, improving efficiency and alignment with organizational structures. The changes also include a substantial refactoring of the assignment module's internal structure, improving modularity and testability, alongside minor bug fixes and documentation updates to reflect the new features.

Highlights

  • New Assignment Strategies: Implemented three new task assignment strategies: CostOptimizedAssignmentStrategy (assigns to the cheapest agent by total_cost_usd), HierarchicalAssignmentStrategy (delegates tasks based on an organizational chart), and AuctionAssignmentStrategy (assigns based on a score × availability bidding system).
  • Strategy Registry Refactoring: Extracted the strategy registry into a new registry.py file to manage the growing number of strategies and keep strategies.py concise. The registry now uses the TaskAssignmentStrategy protocol type.
  • New Observability Events: Added three new observability events related to task assignment: task_assignment.capability_fallback, task_assignment.delegator.resolved, and task_assignment.hierarchy.transitive_fallback.
  • Test Suite Restructuring: Split the large assignment test file into five focused files (one per strategy and one for the registry) and introduced shared test helpers in conftest.py for better organization and maintainability.
  • Mypy Fix: Addressed a pre-existing mypy union-attr error in llm.py's retry logic.
  • Documentation Updates: Updated the DESIGN_SPEC.md and README.md to reflect the new task assignment strategies and the current state of implementation (M4).
Changelog
  • DESIGN_SPEC.md
    • Updated the 'Current state' for task assignment to M4, reflecting the implementation of all six strategies.
    • Modified the file structure section to include registry.py and indicate that strategies.py now contains all six implementations.
  • README.md
    • Updated the 'Task Assignment' bullet point to list the newly added strategies: cost-optimized, hierarchical, and auction.
  • src/ai_company/engine/init.py
    • Imported new strategy names (STRATEGY_NAME_AUCTION, STRATEGY_NAME_COST_OPTIMIZED, STRATEGY_NAME_HIERARCHICAL), new strategy classes, and the build_strategy_map function.
    • Added the newly imported names and classes to the __all__ export list.
  • src/ai_company/engine/assignment/init.py
    • Updated the module docstring to include the new assignment strategies.
    • Modified imports to pull STRATEGY_MAP and build_strategy_map from the new registry.py.
    • Imported new strategy classes (AuctionAssignmentStrategy, CostOptimizedAssignmentStrategy, HierarchicalAssignmentStrategy) and their corresponding names.
    • Added all new imports to the __all__ export list.
  • src/ai_company/engine/assignment/registry.py
    • Added a new file to house the STRATEGY_MAP (excluding hierarchical) and the build_strategy_map factory function.
    • Implemented build_strategy_map to dynamically create a strategy map, optionally including HierarchicalAssignmentStrategy when a HierarchyResolver is provided.
  • src/ai_company/engine/assignment/strategies.py
    • Updated the module docstring to reflect the six concrete strategies and the relocation of STRATEGY_MAP to registry.py.
    • Removed the module-level STRATEGY_MAP and _StrategyType definition.
    • Added new strategy name constants (STRATEGY_NAME_COST_OPTIMIZED, STRATEGY_NAME_HIERARCHICAL, STRATEGY_NAME_AUCTION).
    • Updated the _score_and_filter_candidates docstring to reflect its use by all scorer-based strategies.
    • Refined comments for agent ID comparison in _find_designated_agent.
    • Implemented CostOptimizedAssignmentStrategy to select agents based on lowest total cost, with score as a tiebreaker.
    • Implemented HierarchicalAssignmentStrategy to assign tasks to subordinates based on an organizational hierarchy, supporting direct and transitive delegation.
    • Implemented AuctionAssignmentStrategy to assign tasks based on a bid calculated from capability score and availability.
    • Added debug logging for capability fallback in LoadBalancedAssignmentStrategy and CostOptimizedAssignmentStrategy.
    • Added debug logging for cost-optimized selection, auction bids, auction wins, delegator resolution, and hierarchical transitive fallback.
  • src/ai_company/engine/decomposition/llm.py
    • Fixed a mypy union-attr error by adding a null check for last_response when accessing its attributes.
  • src/ai_company/observability/events/task_assignment.py
    • Added new observability event constants: TASK_ASSIGNMENT_COST_OPTIMIZED, TASK_ASSIGNMENT_HIERARCHICAL_DELEGATED, TASK_ASSIGNMENT_AUCTION_BID, TASK_ASSIGNMENT_AUCTION_WON, TASK_ASSIGNMENT_CAPABILITY_FALLBACK, TASK_ASSIGNMENT_DELEGATOR_RESOLVED, and TASK_ASSIGNMENT_HIERARCHY_TRANSITIVE.
  • tests/unit/engine/conftest.py
    • Added AgentStatus to imports.
    • Introduced new helper functions make_assignment_model_config, make_assignment_agent, and make_assignment_task to standardize agent and task creation for assignment tests.
  • tests/unit/engine/test_assignment_strategies.py
    • Refactored the test file to use the new make_assignment_agent and make_assignment_task helper functions.
    • Removed the internal _model_config, _make_agent, and _make_task helper functions.
    • Removed tests for STRATEGY_MAP and TestProtocolConformance as these are now covered in test_strategy_registry.py.
    • Updated the module docstring to reflect that it now only covers Manual, RoleBased, and LoadBalanced strategies.
    • Added an assertion for the reason string in test_empty_workloads_falls_back_to_capability.
  • tests/unit/engine/test_auction_assignment.py
    • Added a new test file dedicated to unit tests for AuctionAssignmentStrategy, covering various bidding scenarios and edge cases.
  • tests/unit/engine/test_cost_optimized_assignment.py
    • Added a new test file dedicated to unit tests for CostOptimizedAssignmentStrategy, including tests for cheapest agent selection, tie-breaking, and fallback behavior.
  • tests/unit/engine/test_hierarchical_assignment.py
    • Added a new test file dedicated to unit tests for HierarchicalAssignmentStrategy, covering direct and transitive delegation, delegator resolution, and handling of unknown delegators or lack of subordinates.
  • tests/unit/engine/test_strategy_registry.py
    • Added a new test file for STRATEGY_MAP and build_strategy_map, verifying correct strategy inclusion/exclusion and immutability.
    • Included tests for TaskAssignmentStrategy protocol conformance for all six strategies.
Activity
  • The pull request introduces significant new features, including three new assignment strategies.
  • The existing codebase was refactored to accommodate these new strategies, including moving the strategy registry to a dedicated file.
  • New observability events were added to provide better insight into the task assignment process.
  • The test suite was reorganized and expanded with new files to ensure comprehensive coverage of the new strategies.
  • A pre-existing mypy error was fixed in llm.py.
  • Documentation in DESIGN_SPEC.md and README.md was updated to reflect the changes.
  • The pull request was pre-reviewed by 9 agents, and 22 findings were addressed, indicating a thorough internal review process.
  • All 3976 local tests passed, achieving 96.51% coverage (above the 80% threshold).
  • Mypy strict passed for all 463 source files.
  • Ruff lint and format checks passed cleanly.
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 three new task assignment strategies: CostOptimizedAssignmentStrategy, HierarchicalAssignmentStrategy, and AuctionAssignmentStrategy. The implementation is well-structured, with the strategy registry extracted into a new registry.py file to improve modularity. The test suite has been thoughtfully split into focused files for each strategy, with shared helpers in conftest.py, which greatly improves test organization and readability. The documentation in DESIGN_SPEC.md and README.md has been updated accordingly.

My review includes one suggestion for improvement in src/ai_company/engine/assignment/registry.py to enhance consistency in how the default AgentTaskScorer is handled. Overall, this is a high-quality contribution that significantly expands the framework's capabilities.

Returns:
Immutable mapping of strategy names to instances.
"""
effective_scorer = scorer if scorer is not None else AgentTaskScorer()
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 current implementation creates a new AgentTaskScorer instance every time build_strategy_map is called without a scorer argument. This is inconsistent with the module-level STRATEGY_MAP, which uses a single shared _DEFAULT_SCORER instance.

This can lead to surprising behavior where strategies returned by build_strategy_map() (with no arguments) use a different scorer instance than those in the global STRATEGY_MAP. Reusing the module-level _DEFAULT_SCORER would ensure consistency and avoid creating unnecessary objects.

I suggest changing this line to use the shared scorer. You'll also want to update the docstring on line 77 to reflect this change from "Defaults to a new AgentTaskScorer" to something like "Defaults to the shared module-level AgentTaskScorer instance".

Suggested change
effective_scorer = scorer if scorer is not None else AgentTaskScorer()
effective_scorer = scorer if scorer is not None else _DEFAULT_SCORER

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

🤖 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/strategies.py`:
- Around line 591-694: The assign method is doing multiple responsibilities;
split it into small helpers: extract delegator resolution and membership checks
into a helper (e.g., _validate_delegator_hierarchy) that calls
_resolve_delegator, uses _hierarchy.get_direct_reports and
_hierarchy.get_supervisor and returns (delegator, has_reports, has_supervisor)
or raises/returns a failure result; extract request rebuilding into
_build_filtered_request which calls _filter_by_hierarchy and constructs the
AssignmentRequest (reuse names filtered_request and subtask via
_build_subtask_definition); extract scoring/selection into
_score_candidates_and_choose which calls _score_and_filter_candidates and
returns (candidates, selected, alternatives) or a failure reason; extract
logging/result formatting into _format_assignment_result which builds the final
AssignmentResult and logs using TASK_ASSIGNMENT_NO_ELIGIBLE /
TASK_ASSIGNMENT_HIERARCHICAL_DELEGATED; update assign to orchestrate these
helpers and return early on helper failure results so assign stays small and
declarative.
- Around line 455-480: The current cost-based ordering uses cost_map.get(...,
0.0) which treats missing AgentWorkload entries as zero and incorrectly ranks
uncosted agents as cheapest; update the logic in the block that builds cost_map
and calls candidates.sort (the code surrounding cost_map, candidates.sort, and
the TASK_ASSIGNMENT_COST_OPTIMIZED logger) to first verify that every
candidate's agent_identity.id is present in cost_map (e.g., compare
set(cost_map.keys()) with the candidate ids); if any candidate lacks a cost
entry, skip the cost-based sort and fall back to the capability ordering and
TASK_ASSIGNMENT_CAPABILITY_FALLBACK logging, otherwise perform the sort using
cost_map[...] (no default) and -c.score so only fully-costed agents are compared
by cost.
- Around line 527-589: _resolve_delegator currently returns task.created_by or
delegation_chain[-1] (Agent ID strings) but _filter_by_hierarchy and
HierarchyResolver expect agent names; update the flow to resolve that Agent ID
to the corresponding AgentIdentity.name from request.available_agents before
calling hierarchy methods. Concretely: in _filter_by_hierarchy (or immediately
after _resolve_delegator is called), look up the delegator in
request.available_agents by comparing str(agent.id) or agent.id to the delegator
ID and replace delegator with agent.name if found; if no match, log and treat as
unknown (return empty tuple or fallback behavior). Reference:
_resolve_delegator, _filter_by_hierarchy, HierarchyResolver.get_direct_reports,
HierarchyResolver.is_subordinate, and the ManualAssignmentStrategy pattern for
ID→name lookup.

In `@tests/unit/engine/test_strategy_registry.py`:
- Around line 185-213: The test currently relies on request.min_score to produce
the failure so it doesn't prove injection of the scorer into build_strategy_map;
change the test_custom_scorer_injected to use a scorer double (subclass or mock
of AgentTaskScorer that overrides score()) and pass that into
build_strategy_map, and ensure the AssignmentRequest.min_score is set to a value
that would allow selection under the default scorer (or left at the default) so
the only reason selection changes is the custom scorer.score() implementation;
locate symbols build_strategy_map, AgentTaskScorer (or your mock subclass),
test_custom_scorer_injected, STRATEGY_NAME_ROLE_BASED and AssignmentRequest to
implement the change and assert the selected agent/eligibility differs because
of the overridden score() method.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 377d1d63-164f-48b9-b690-1a5228c28a29

📥 Commits

Reviewing files that changed from the base of the PR and between aa0eefe and 31709c6.

📒 Files selected for processing (14)
  • DESIGN_SPEC.md
  • README.md
  • src/ai_company/engine/__init__.py
  • src/ai_company/engine/assignment/__init__.py
  • src/ai_company/engine/assignment/registry.py
  • src/ai_company/engine/assignment/strategies.py
  • src/ai_company/engine/decomposition/llm.py
  • src/ai_company/observability/events/task_assignment.py
  • tests/unit/engine/conftest.py
  • tests/unit/engine/test_assignment_strategies.py
  • tests/unit/engine/test_auction_assignment.py
  • tests/unit/engine/test_cost_optimized_assignment.py
  • tests/unit/engine/test_hierarchical_assignment.py
  • tests/unit/engine/test_strategy_registry.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 (6)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do NOT use from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations
Use except A, B: (no parentheses) — ruff enforces PEP 758 except syntax on Python 3.14
Type hints required on all public functions, mypy strict mode enforced
Google style docstrings required on public classes/functions — enforced by ruff D rules
Create new objects for immutability, never mutate existing ones. For non-Pydantic internal collections, use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use @computed_field for derived values instead of storing + validating redundant fields. Use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Line length 88 characters enforced by ruff
Functions < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow them
Validate at system boundaries (user input, external APIs, config files)

Files:

  • src/ai_company/engine/__init__.py
  • src/ai_company/observability/events/task_assignment.py
  • tests/unit/engine/conftest.py
  • tests/unit/engine/test_cost_optimized_assignment.py
  • tests/unit/engine/test_auction_assignment.py
  • src/ai_company/engine/decomposition/llm.py
  • tests/unit/engine/test_hierarchical_assignment.py
  • src/ai_company/engine/assignment/__init__.py
  • src/ai_company/engine/assignment/registry.py
  • src/ai_company/engine/assignment/strategies.py
  • tests/unit/engine/test_assignment_strategies.py
  • tests/unit/engine/test_strategy_registry.py
src/ai_company/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/ai_company/**/*.py: Every module with business logic MUST have: from ai_company.observability import get_logger then logger = get_logger(__name__). Never use import logging / logging.getLogger() / print() in application code.
Always use logger as variable name (not _logger, not log)
Always use event name constants from ai_company.observability.events domain-specific modules (e.g. PROVIDER_CALL_START from events.provider). Import directly and use structured kwargs: logger.info(EVENT, key=value) — never logger.info("msg %s", val)
All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.

Files:

  • src/ai_company/engine/__init__.py
  • src/ai_company/observability/events/task_assignment.py
  • src/ai_company/engine/decomposition/llm.py
  • src/ai_company/engine/assignment/__init__.py
  • src/ai_company/engine/assignment/registry.py
  • src/ai_company/engine/assignment/strategies.py
src/ai_company/{engine,providers}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

RetryExhaustedError signals that all retries failed — the engine layer catches this to trigger fallback chains

Files:

  • src/ai_company/engine/__init__.py
  • src/ai_company/engine/decomposition/llm.py
  • src/ai_company/engine/assignment/__init__.py
  • src/ai_company/engine/assignment/registry.py
  • src/ai_company/engine/assignment/strategies.py
{src/ai_company/**/*.py,tests/**/*.py,**/*.yaml,**/*.yml}

📄 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: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases.

Files:

  • src/ai_company/engine/__init__.py
  • src/ai_company/observability/events/task_assignment.py
  • tests/unit/engine/conftest.py
  • tests/unit/engine/test_cost_optimized_assignment.py
  • tests/unit/engine/test_auction_assignment.py
  • src/ai_company/engine/decomposition/llm.py
  • tests/unit/engine/test_hierarchical_assignment.py
  • src/ai_company/engine/assignment/__init__.py
  • src/ai_company/engine/assignment/registry.py
  • src/ai_company/engine/assignment/strategies.py
  • tests/unit/engine/test_assignment_strategies.py
  • tests/unit/engine/test_strategy_registry.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Test timeout: 30 seconds per test. Parallelism: pytest-xdist via -n auto
Prefer @pytest.mark.parametrize for testing similar cases

Files:

  • tests/unit/engine/conftest.py
  • tests/unit/engine/test_cost_optimized_assignment.py
  • tests/unit/engine/test_auction_assignment.py
  • tests/unit/engine/test_hierarchical_assignment.py
  • tests/unit/engine/test_assignment_strategies.py
  • tests/unit/engine/test_strategy_registry.py
DESIGN_SPEC.md

📄 CodeRabbit inference engine (CLAUDE.md)

When approved deviations occur, update DESIGN_SPEC.md to reflect the new reality

Files:

  • DESIGN_SPEC.md
🧠 Learnings (2)
📚 Learning: 2026-03-08T19:07:25.519Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-08T19:07:25.519Z
Learning: Applies to src/ai_company/**/*.py : Always use event name constants from `ai_company.observability.events` domain-specific modules (e.g. `PROVIDER_CALL_START` from `events.provider`). Import directly and use structured kwargs: `logger.info(EVENT, key=value)` — never `logger.info("msg %s", val)`

Applied to files:

  • src/ai_company/observability/events/task_assignment.py
📚 Learning: 2026-03-08T19:07:25.519Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-08T19:07:25.519Z
Learning: Applies to src/ai_company/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.

Applied to files:

  • tests/unit/engine/conftest.py
🧬 Code graph analysis (7)
src/ai_company/engine/__init__.py (2)
src/ai_company/engine/assignment/protocol.py (1)
  • TaskAssignmentStrategy (16-42)
src/ai_company/engine/assignment/registry.py (1)
  • build_strategy_map (61-106)
tests/unit/engine/conftest.py (4)
src/ai_company/core/enums.py (3)
  • AgentStatus (67-72)
  • SeniorityLevel (6-21)
  • TaskType (194-202)
src/ai_company/core/agent.py (3)
  • ModelConfig (145-174)
  • AgentIdentity (246-304)
  • SkillSet (125-142)
src/ai_company/engine/assignment/strategies.py (6)
  • name (127-129)
  • name (253-255)
  • name (318-320)
  • name (418-420)
  • name (523-525)
  • name (713-715)
src/ai_company/core/task.py (1)
  • Task (45-261)
tests/unit/engine/test_cost_optimized_assignment.py (4)
src/ai_company/core/enums.py (2)
  • Complexity (214-220)
  • SeniorityLevel (6-21)
src/ai_company/engine/assignment/models.py (2)
  • AgentWorkload (17-37)
  • AssignmentRequest (65-128)
src/ai_company/engine/assignment/strategies.py (13)
  • CostOptimizedAssignmentStrategy (403-499)
  • assign (202-237)
  • assign (257-300)
  • assign (322-400)
  • assign (422-499)
  • assign (591-694)
  • assign (717-795)
  • name (127-129)
  • name (253-255)
  • name (318-320)
  • name (418-420)
  • name (523-525)
  • name (713-715)
src/ai_company/engine/routing/scorer.py (2)
  • AgentTaskScorer (36-162)
  • min_score (70-72)
tests/unit/engine/test_hierarchical_assignment.py (4)
src/ai_company/communication/delegation/hierarchy.py (1)
  • HierarchyResolver (16-277)
src/ai_company/engine/assignment/models.py (1)
  • AssignmentRequest (65-128)
src/ai_company/engine/assignment/strategies.py (13)
  • HierarchicalAssignmentStrategy (502-694)
  • name (127-129)
  • name (253-255)
  • name (318-320)
  • name (418-420)
  • name (523-525)
  • name (713-715)
  • assign (202-237)
  • assign (257-300)
  • assign (322-400)
  • assign (422-499)
  • assign (591-694)
  • assign (717-795)
src/ai_company/engine/routing/scorer.py (2)
  • AgentTaskScorer (36-162)
  • min_score (70-72)
src/ai_company/engine/assignment/__init__.py (2)
src/ai_company/engine/assignment/registry.py (1)
  • build_strategy_map (61-106)
src/ai_company/engine/assignment/strategies.py (3)
  • AuctionAssignmentStrategy (697-795)
  • CostOptimizedAssignmentStrategy (403-499)
  • HierarchicalAssignmentStrategy (502-694)
src/ai_company/engine/assignment/strategies.py (10)
tests/unit/engine/test_hierarchical_assignment.py (1)
  • hierarchy (23-41)
src/ai_company/communication/delegation/hierarchy.py (4)
  • HierarchyResolver (16-277)
  • get_direct_reports (147-159)
  • is_subordinate (177-198)
  • get_supervisor (136-145)
src/ai_company/core/agent.py (1)
  • AgentIdentity (246-304)
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)
  • AssignmentCandidate (40-62)
  • AssignmentRequest (65-128)
  • AssignmentResult (131-171)
src/ai_company/engine/decomposition/models.py (1)
  • SubtaskDefinition (22-63)
src/ai_company/engine/parallel_models.py (2)
  • task_id (87-89)
  • agent_id (79-81)
src/ai_company/engine/shutdown.py (1)
  • strategy (352-354)
src/ai_company/engine/assignment/protocol.py (2)
  • name (26-28)
  • assign (30-42)
src/ai_company/engine/assignment/service.py (1)
  • assign (50-122)
tests/unit/engine/test_strategy_registry.py (7)
src/ai_company/core/company.py (2)
  • Company (400-483)
  • Department (257-322)
src/ai_company/core/enums.py (2)
  • Complexity (214-220)
  • SeniorityLevel (6-21)
src/ai_company/engine/assignment/models.py (1)
  • AssignmentRequest (65-128)
src/ai_company/engine/assignment/protocol.py (1)
  • TaskAssignmentStrategy (16-42)
src/ai_company/engine/assignment/registry.py (1)
  • build_strategy_map (61-106)
src/ai_company/engine/assignment/strategies.py (18)
  • AuctionAssignmentStrategy (697-795)
  • CostOptimizedAssignmentStrategy (403-499)
  • HierarchicalAssignmentStrategy (502-694)
  • LoadBalancedAssignmentStrategy (303-400)
  • ManualAssignmentStrategy (117-237)
  • RoleBasedAssignmentStrategy (240-300)
  • name (127-129)
  • name (253-255)
  • name (318-320)
  • name (418-420)
  • name (523-525)
  • name (713-715)
  • assign (202-237)
  • assign (257-300)
  • assign (322-400)
  • assign (422-499)
  • assign (591-694)
  • assign (717-795)
src/ai_company/engine/routing/scorer.py (2)
  • AgentTaskScorer (36-162)
  • min_score (70-72)
🪛 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 (21)
src/ai_company/engine/decomposition/llm.py (1)

161-162: LGTM!

The guards correctly handle the case where last_response could be None from mypy's perspective, even though logically it should always be set when last_error is not None. The fallbacks ("" for content, () for tool_calls) are appropriate defaults for retry context reconstruction.

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

17-17: LGTM!

AgentStatus import is correctly added to support the new make_assignment_agent helper's status parameter.


360-390: LGTM!

The make_assignment_model_config and make_assignment_agent helpers follow established patterns in the file and use vendor-agnostic names ("test-provider", "test-small-001") as required by coding guidelines. The default values are sensible for testing assignment strategies.


393-404: LGTM!

The make_assignment_task factory follows the same pattern as other helpers in this file. The # type: ignore[arg-type] is appropriate here since defaults is dynamically constructed with mixed types from overrides.

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

1-7: LGTM!

Clear module docstring explaining the purpose of STRATEGY_MAP and build_strategy_map, and when to use each.


36-58: LGTM!

The static STRATEGY_MAP correctly excludes HierarchicalAssignmentStrategy since it requires a HierarchyResolver at construction. Using MappingProxyType ensures immutability as per coding guidelines.


61-106: LGTM!

The factory function correctly creates fresh strategy instances with the provided or default scorer, and conditionally includes the hierarchical strategy when a HierarchyResolver is supplied. The docstring accurately describes the behavior.

README.md (1)

20-20: LGTM!

The documentation accurately reflects the expanded six-strategy task assignment system. The dash formatting is consistent with the rest of the Key Features list.

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

9-30: LGTM!

The new strategy classes, name constants, and build_strategy_map factory are correctly imported from the assignment subpackage and follow the existing import organization.


142-254: LGTM!

All new symbols are correctly added to __all__ in alphabetical order, consistent with the existing list organization.

tests/unit/engine/test_hierarchical_assignment.py (4)

1-16: LGTM!

Clean imports and proper use of pytest.mark.unit. The conftest helpers are imported correctly from the relative module.


19-72: LGTM!

The hierarchy fixture creates a clear manager → lead → (dev-1, dev-2) structure. The test_direct_report_selected test validates core functionality with appropriate assertions on selected, strategy_used, and reason.


74-167: LGTM!

Good coverage of scoring tie-breaking, transitive subordinate fallback, and delegation_chain precedence. The test names and docstrings clearly communicate intent.


169-295: LGTM!

Comprehensive edge case coverage: no subordinates, unknown delegator, reports not in pool, and scores below threshold. The assertions correctly verify result.selected is None and check for expected reason substrings.

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

1-18: LGTM!

Clean imports with proper pytest.mark.unit module marker. The conftest helpers are correctly imported.


20-134: LGTM!

Comprehensive tests for core cost-optimized behavior: cheapest agent selection, cost tie-breaking by score, and fallback to capability-only sorting when workloads are empty. The assertions correctly verify strategy_used and reason content.


136-247: LGTM!

Good coverage of edge cases: no eligible agents, partial cost data defaulting to 0.0, and parametrized cost distributions. The parametrized test with descriptive IDs ("first-cheapest", "last-cheapest", "all-equal") effectively validates deterministic winner selection.

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

1-20: LGTM!

Clean refactoring with updated module docstring accurately reflecting scope. The imports are properly organized with conftest helpers and the unit marker is correctly applied.


23-98: LGTM!

The TestManualAssignmentStrategy tests are cleanly refactored to use make_assignment_agent and make_assignment_task helpers, maintaining the same test coverage with less boilerplate.


100-210: LGTM!

The TestRoleBasedAssignmentStrategy tests maintain comprehensive coverage while using the shared test helpers. The test logic and assertions are preserved correctly.


212-432: LGTM!

The TestLoadBalancedAssignmentStrategy tests are well-refactored. The added assertion on line 321 ("no workload data" in result.reason) correctly validates the fallback behavior, and the clarifying comment on lines 328-329 helps explain the sort stability expectation.

Comment on lines +527 to +589
def _resolve_delegator(self, request: AssignmentRequest) -> str:
"""Determine the delegator from the task.

Uses ``delegation_chain[-1]`` if non-empty, else ``created_by``.

Args:
request: The assignment request.

Returns:
Delegator agent name.
"""
task = request.task
if task.delegation_chain:
delegator = task.delegation_chain[-1]
logger.debug(
TASK_ASSIGNMENT_DELEGATOR_RESOLVED,
task_id=task.id,
delegator=delegator,
source="delegation_chain",
)
return delegator
logger.debug(
TASK_ASSIGNMENT_DELEGATOR_RESOLVED,
task_id=task.id,
delegator=task.created_by,
source="created_by",
)
return task.created_by

def _filter_by_hierarchy(
self,
request: AssignmentRequest,
delegator: str,
) -> tuple[AgentIdentity, ...]:
"""Filter available agents to subordinates of the delegator.

Tries direct reports first, then transitive subordinates.

Args:
request: The assignment request.
delegator: Delegator agent name.

Returns:
Filtered tuple of agents that are subordinates.
"""
direct_reports = set(self._hierarchy.get_direct_reports(delegator))

# Try direct reports first
direct = tuple(a for a in request.available_agents if a.name in direct_reports)
if direct:
return direct

# Fall back to transitive subordinates
logger.debug(
TASK_ASSIGNMENT_HIERARCHY_TRANSITIVE,
delegator=delegator,
direct_report_count=len(direct_reports),
)
return tuple(
a
for a in request.available_agents
if self._hierarchy.is_subordinate(delegator, a.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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Task identity fields"
rg -n -C2 'assigned_to|created_by|delegation_chain' src/ai_company/core/task.py

echo
echo "HierarchyResolver lookup keys"
rg -n -C2 'get_direct_reports|get_supervisor|is_subordinate' \
  src/ai_company/communication/delegation/hierarchy.py

echo
echo "Hierarchical assignment delegator resolution"
rg -n -C2 '_resolve_delegator|_filter_by_hierarchy' \
  src/ai_company/engine/assignment/strategies.py

Repository: Aureliolo/ai-company

Length of output: 4226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== AgentIdentity structure ==="
rg -n 'class AgentIdentity' -A 15 src/

echo
echo "=== ManualAssignmentStrategy assign method ==="
rg -n 'class ManualAssignmentStrategy' -A 80 src/ai_company/engine/assignment/strategies.py | head -100

echo
echo "=== Check how assigned_to is used in context ==="
rg -n 'assigned_to' src/ai_company/engine/assignment/strategies.py | head -20

echo
echo "=== Check _filter_by_hierarchy full usage context ==="
sed -n '556,625p' src/ai_company/engine/assignment/strategies.py

Repository: Aureliolo/ai-company

Length of output: 7394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check if delegator is ever converted before hierarchy lookup ==="
sed -n '594,625p' src/ai_company/engine/assignment/strategies.py

echo
echo "=== Verify HierarchyResolver data structure: what are the keys? ==="
rg -n '_direct_reports_of|_supervisor_of' src/ai_company/communication/delegation/hierarchy.py -B 2 -A 2

Repository: Aureliolo/ai-company

Length of output: 2618


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== HierarchyResolver.__init__ internals ==="
sed -n '37,90p' src/ai_company/communication/delegation/hierarchy.py

echo
echo "=== Company model: check agents structure ==="
rg -n 'class Company' -A 30 src/ai_company/core/company.py | head -50

Repository: Aureliolo/ai-company

Length of output: 4209


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Department/Team/ReportingLine structure ==="
rg -n 'class Department|class Team|class ReportingLine' src/ai_company/core/company.py -A 25 | head -150

echo
echo "=== Check what fields are in Team (lead, members) ==="
sed -n '1,400p' src/ai_company/core/company.py | grep -A 30 'class Team'

Repository: Aureliolo/ai-company

Length of output: 5510


Resolve delegator Agent ID to agent name before hierarchy lookup.

_resolve_delegator() returns task.created_by or task.delegation_chain[-1], which are Agent IDs (UUID strings). However, HierarchyResolver methods (get_direct_reports, is_subordinate) expect agent names. The hierarchy is built entirely on agent names (from team.lead, team.members, dept.head, and reporting_lines), not UUIDs. This mismatch causes valid delegators to be rejected as "unknown" (lines 602–618).

ManualAssignmentStrategy already handles the inverse case by comparing task.assigned_to (Agent ID string) against str(available.id) to resolve the agent. A similar translation is needed here: look up the delegator's AgentIdentity.name from the available agents before passing it to hierarchy methods.

🤖 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 527 - 589,
_resolve_delegator currently returns task.created_by or delegation_chain[-1]
(Agent ID strings) but _filter_by_hierarchy and HierarchyResolver expect agent
names; update the flow to resolve that Agent ID to the corresponding
AgentIdentity.name from request.available_agents before calling hierarchy
methods. Concretely: in _filter_by_hierarchy (or immediately after
_resolve_delegator is called), look up the delegator in request.available_agents
by comparing str(agent.id) or agent.id to the delegator ID and replace delegator
with agent.name if found; if no match, log and treat as unknown (return empty
tuple or fallback behavior). Reference: _resolve_delegator,
_filter_by_hierarchy, HierarchyResolver.get_direct_reports,
HierarchyResolver.is_subordinate, and the ManualAssignmentStrategy pattern for
ID→name lookup.

Comment on lines +185 to +213
def test_custom_scorer_injected(self) -> None:
"""Custom scorer is used by strategies in the returned map."""
custom_scorer = AgentTaskScorer(min_score=0.5)
result = build_strategy_map(scorer=custom_scorer)

# Verify the custom scorer is effective by running an
# assignment that would pass default min_score (0.1) but
# fail with the custom min_score (0.5)
strategy = result[STRATEGY_NAME_ROLE_BASED]
agent = make_assignment_agent(
"dev-1",
primary_skills=("testing",),
level=SeniorityLevel.JUNIOR,
)
task = make_assignment_task(
estimated_complexity=Complexity.EPIC,
)
request = AssignmentRequest(
task=task,
available_agents=(agent,),
required_skills=("python", "api-design"),
required_role="Backend Developer",
min_score=0.5,
)

assignment_result = strategy.assign(request)

# Agent should not be selected with the high threshold
assert assignment_result.selected is None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This test does not actually prove scorer injection.

AgentTaskScorer.min_score is not what drives the failure here; request.min_score=0.5 on Line 207 is. build_strategy_map() could ignore custom_scorer entirely and this assertion would still pass. Please make the injected scorer observable in the outcome, e.g. with a scorer double that overrides score() and changes the selected agent / eligibility.

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

In `@tests/unit/engine/test_strategy_registry.py` around lines 185 - 213, The test
currently relies on request.min_score to produce the failure so it doesn't prove
injection of the scorer into build_strategy_map; change the
test_custom_scorer_injected to use a scorer double (subclass or mock of
AgentTaskScorer that overrides score()) and pass that into build_strategy_map,
and ensure the AssignmentRequest.min_score is set to a value that would allow
selection under the default scorer (or left at the default) so the only reason
selection changes is the custom scorer.score() implementation; locate symbols
build_strategy_map, AgentTaskScorer (or your mock subclass),
test_custom_scorer_injected, STRATEGY_NAME_ROLE_BASED and AssignmentRequest to
implement the change and assert the selected agent/eligibility differs because
of the overridden score() method.

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

Adds three new task-assignment strategies to the engine (cost-optimized, hierarchical, auction), refactors the strategy registry into a dedicated module, and updates observability/test/docs to match the expanded strategy set.

Changes:

  • Implement CostOptimizedAssignmentStrategy, HierarchicalAssignmentStrategy, and AuctionAssignmentStrategy, plus new task-assignment observability event constants.
  • Extract STRATEGY_MAP + build_strategy_map(...) into engine/assignment/registry.py and update package re-exports accordingly.
  • Split/expand unit tests for assignment strategies/registry and add shared test builders in tests/unit/engine/conftest.py; update README + DESIGN_SPEC.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/unit/engine/test_strategy_registry.py New coverage for registry immutability, expected keys, and protocol conformance.
tests/unit/engine/test_hierarchical_assignment.py New coverage for hierarchical delegation selection and fallbacks.
tests/unit/engine/test_cost_optimized_assignment.py New coverage for cost-based selection + tie-breaking/fallback behaviors.
tests/unit/engine/test_auction_assignment.py New coverage for bid-based selection combining score and availability.
tests/unit/engine/test_assignment_strategies.py Updated/simplified tests for existing strategies using shared helpers; removed registry tests now covered elsewhere.
tests/unit/engine/conftest.py Adds shared helpers to construct agents/tasks for assignment strategy tests.
src/ai_company/observability/events/task_assignment.py Adds task-assignment event name constants for new strategies and fallbacks.
src/ai_company/engine/decomposition/llm.py Attempts to fix retry context handling for possibly-missing last_response.
src/ai_company/engine/assignment/strategies.py Adds the three new strategy implementations and related structured logging.
src/ai_company/engine/assignment/registry.py New registry/factory module providing static and runtime-extended strategy maps.
src/ai_company/engine/assignment/init.py Updates assignment package exports to include new strategies and registry factory.
src/ai_company/engine/init.py Updates engine top-level exports for new strategies and factory.
README.md Updates Task Assignment feature bullet to include the new strategies.
DESIGN_SPEC.md Updates current-state notes and repo layout to reflect registry split and the full strategy set.

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

assistant_msg = ChatMessage(
role=MessageRole.ASSISTANT,
content=last_response.content or "",
content=last_response.content or "" if last_response else "",
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 ternary expression here has the wrong operator precedence: last_response.content or "" if last_response else "" will still try to evaluate last_response.content when last_response is None. Wrap the or branch in parentheses (or restructure the conditional) so last_response is checked before accessing .content.

Suggested change
content=last_response.content or "" if last_response else "",
content=(last_response.content or "") if last_response else "",

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

greptile-apps bot commented Mar 8, 2026

Greptile Summary

This PR implements three new pluggable task-assignment strategies (Cost-Optimized, Hierarchical, Auction), extracts the strategy registry into its own registry.py module, splits one large test file into five focused files with shared conftest.py helpers, and fixes a pre-existing mypy union-attr error in llm.py. The architecture is clean and consistent with existing patterns.

Key changes:

  • CostOptimizedAssignmentStrategy — picks the cheapest agent by total_cost_usd, score as tiebreaker; falls back to score-only ranking when cost data is absent or incomplete
  • HierarchicalAssignmentStrategy — resolves delegator from delegation_chain[-1] / created_by, filters to direct reports first then transitive subordinates; correctly excluded from the static STRATEGY_MAP because it requires a runtime HierarchyResolver
  • AuctionAssignmentStrategy — bid = capability_score × (1 / (1 + active_tasks)); falls back to pure score when workload data is missing
  • LoadBalancedAssignmentStrategy behaviour change — partial workload data (not all candidates covered) now triggers a score-only fallback instead of defaulting missing agents to workload 0
  • Two test assertions in test_cost_optimized_assignment.py contradict the actual implementation: test_partial_cost_data expects unknown-dev to win under the old "default-0.0" semantics that no longer exist, and test_empty_workloads_falls_back_to_capability checks for the substring "no cost data" which is not present in the actual reason string "Best match (insufficient cost data): ..."

Confidence Score: 3/5

  • PR is safe to merge only after fixing two failing assertions in test_cost_optimized_assignment.py.
  • Production logic is sound and well-structured. The two test failures in test_cost_optimized_assignment.py (wrong winner assertion in test_partial_cost_data and wrong substring in test_empty_workloads_falls_back_to_capability) indicate the tests were not updated to reflect the intentional behavior change from "default-missing-to-0.0" to "all-or-nothing fallback". These failures block a green CI run and could mask real regressions.
  • tests/unit/engine/test_cost_optimized_assignment.py — two test assertions contradict the current implementation.

Important Files Changed

Filename Overview
src/ai_company/engine/assignment/strategies.py Adds CostOptimizedAssignmentStrategy, HierarchicalAssignmentStrategy, and AuctionAssignmentStrategy. LoadBalanced behavior changed from partial-workload-default-0 to all-or-nothing; new strategies follow the same pattern. Logic is sound; the "complete data" guard is consistent across all three new strategies.
tests/unit/engine/test_cost_optimized_assignment.py Two test assertions are mismatched with the actual implementation: test_partial_cost_data expects unknown-dev to win (old 0.0-default behavior, not the new fallback), and test_empty_workloads_falls_back_to_capability checks for "no cost data" but the code emits "insufficient cost data".
src/ai_company/engine/assignment/registry.py New module cleanly extracts STRATEGY_MAP and build_strategy_map from strategies.py. Correctly excludes HierarchicalAssignmentStrategy from the static map and adds it conditionally. Uses TYPE_CHECKING for HierarchyResolver to avoid circular imports.
tests/unit/engine/test_auction_assignment.py Thorough auction test coverage; bids, idle-preference, high-score-overcoming-load, empty-workloads, parametrized scenarios, and name property all tested correctly.
tests/unit/engine/test_hierarchical_assignment.py Comprehensive hierarchical tests covering direct reports, transitive fallback, delegation_chain precedence, unknown delegator, no-subordinates, and below-min-score edge cases.
src/ai_company/engine/decomposition/llm.py Pre-existing mypy union-attr fix: added explicit None guard with correct parenthesisation — (last_response.content or "") if last_response else "" — matching the pattern already used on the next line for tool_calls.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    REQ[AssignmentRequest] --> SVC[TaskAssignmentService]
    SVC --> MAP{Strategy lookup\nSTRATEGY_MAP /\nbuild_strategy_map}

    MAP --> MANUAL[ManualAssignmentStrategy\nassigned_to UUID match]
    MAP --> ROLE[RoleBasedAssignmentStrategy\nhighest capability score]
    MAP --> LB[LoadBalancedAssignmentStrategy\nleast active tasks + score]
    MAP --> COST[CostOptimizedAssignmentStrategy\nlowest total_cost_usd + score]
    MAP --> AUCTION[AuctionAssignmentStrategy\nbid = score × availability]
    MAP --> HIER[HierarchicalAssignmentStrategy\nrequires HierarchyResolver]

    COST --> CD{Complete\ncost data?}
    CD -- Yes --> CSORT[Sort by cost asc\nthen score desc]
    CD -- No --> CFALL[Fallback: sort by\nscore desc]

    LB --> WD{Complete\nworkload data?}
    WD -- Yes --> WSORT[Sort by tasks asc\nthen score desc]
    WD -- No --> WFALL[Fallback: sort by\nscore desc]

    AUCTION --> AD{Complete\nworkload data?}
    AD -- Yes --> ABID[bid = score ÷ 1+tasks\nsort bids desc]
    AD -- No --> AFALL[availability = 1.0\nbid = score]

    HIER --> DELG[Resolve delegator\ndelegation_chain OR created_by]
    DELG --> DR{Direct reports\nin pool?}
    DR -- Yes --> SCORE_D[Score direct reports]
    DR -- No --> TRANS[Transitive subordinates]
    TRANS --> SCORE_T[Score transitive subs]

    CSORT --> RES[AssignmentResult]
    CFALL --> RES
    WSORT --> RES
    WFALL --> RES
    ABID --> RES
    AFALL --> RES
    SCORE_D --> RES
    SCORE_T --> RES
    MANUAL --> RES
Loading

Comments Outside Diff (2)

  1. tests/unit/engine/test_cost_optimized_assignment.py, line 1926-1959 (link)

    Test assertion contradicts the actual fallback behavior

    test_partial_cost_data expects unknown-dev to win (comment: "unknown-dev defaults to 0.0 cost, so it wins"), but the production code does not default missing costs to 0.0. When workload data is incomplete (candidate_ids <= cost_map.keys() is False), has_complete_data = False and the code falls back entirely to score-based ranking — partial cost entries are ignored.

    Both agents here have identical profiles (primary_skills=("python",), level=SeniorityLevel.MID), so their scores are equal. Python's stable sort keeps known-dev first (it's the first element of available_agents), so candidates[0] is known-dev, not unknown-dev.

    The assertion on line 1959 will therefore fail:

    assert result.selected.agent_identity.name == "unknown-dev"  # actually "known-dev"

    This docstring/assertion is left over from a prior design that used cost_map.get(agent_id, 0.0). The new "all-or-nothing" semantics (has_complete_data) were correctly applied to LoadBalanced (see test_partial_workload_data_falls_back), but the equivalent test for CostOptimized was not updated to match.

    The test should instead assert known-dev wins (by sort stability) and verify the fallback reason contains "insufficient cost data":

    assert result.selected.agent_identity.name == "known-dev"
    assert "insufficient cost data" in result.reason
  2. tests/unit/engine/test_cost_optimized_assignment.py, line 1872-1899 (link)

    Reason string assertion does not match what the code produces

    Line 1899 asserts:

    assert "no cost data" in result.reason

    But CostOptimizedAssignmentStrategy.assign() builds the fallback reason as:

    f"Best match (insufficient cost data): {selected.agent_identity.name!r} (score={selected.score:.2f})"

    The substring "no cost data" does not appear anywhere in "Best match (insufficient cost data): ...", so this assertion will always fail.

    The parallel assertion for LoadBalancedAssignmentStrategy (in test_assignment_strategies.py) correctly checks for "insufficient workload data". The fix here is to match the actual wording:

Last reviewed commit: 64d4e51

- Add partial workload/cost data guards (has_complete_data) to
  LoadBalanced, CostOptimized, and Auction strategies to prevent
  bias toward unknown agents
- Refactor HierarchicalAssignmentStrategy.assign into <50-line
  methods (_is_known_delegator, _score_subordinates)
- Fix Task model docstrings: created_by/delegation_chain store
  agent names, not IDs
- Add explicit parentheses in llm.py for operator precedence clarity
- Add logger + _DEFAULT_SCORER reuse in registry.py
- Change capability fallback log level from DEBUG to WARNING
- Fix class/method docstrings across all strategies
- Add diagnostic context to hierarchy transitive fallback log
- Improve test_custom_scorer_injected to verify instance identity
- Add tests: inactive agent filtering, single delegation chain,
  auction alternatives assertion, partial workload fallback
@Aureliolo Aureliolo merged commit ce924fa into main Mar 8, 2026
6 of 9 checks passed
@Aureliolo Aureliolo deleted the feat/additional-assignment-strategies branch March 8, 2026 19:45
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.

Implement additional task assignment strategies (cost-optimized, hierarchical, auction)

2 participants