feat: add cost-optimized, hierarchical, and auction assignment strategies#175
feat: add cost-optimized, hierarchical, and auction assignment strategies#175
Conversation
…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.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (8)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis 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
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)
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)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
Summary of ChangesHello, 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
Changelog
Activity
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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".
| effective_scorer = scorer if scorer is not None else AgentTaskScorer() | |
| effective_scorer = scorer if scorer is not None else _DEFAULT_SCORER |
There was a problem hiding this comment.
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
📒 Files selected for processing (14)
DESIGN_SPEC.mdREADME.mdsrc/ai_company/engine/__init__.pysrc/ai_company/engine/assignment/__init__.pysrc/ai_company/engine/assignment/registry.pysrc/ai_company/engine/assignment/strategies.pysrc/ai_company/engine/decomposition/llm.pysrc/ai_company/observability/events/task_assignment.pytests/unit/engine/conftest.pytests/unit/engine/test_assignment_strategies.pytests/unit/engine/test_auction_assignment.pytests/unit/engine/test_cost_optimized_assignment.pytests/unit/engine/test_hierarchical_assignment.pytests/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 usefrom __future__ import annotations— Python 3.14 has PEP 649 native lazy annotations
Useexcept 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, usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention andcopy.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 (usingmodel_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_fieldfor derived values instead of storing + validating redundant fields. UseNotBlankStrfor all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_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__.pysrc/ai_company/observability/events/task_assignment.pytests/unit/engine/conftest.pytests/unit/engine/test_cost_optimized_assignment.pytests/unit/engine/test_auction_assignment.pysrc/ai_company/engine/decomposition/llm.pytests/unit/engine/test_hierarchical_assignment.pysrc/ai_company/engine/assignment/__init__.pysrc/ai_company/engine/assignment/registry.pysrc/ai_company/engine/assignment/strategies.pytests/unit/engine/test_assignment_strategies.pytests/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_loggerthenlogger = get_logger(__name__). Never useimport logging/logging.getLogger()/print()in application code.
Always useloggeras variable name (not_logger, notlog)
Always use event name constants fromai_company.observability.eventsdomain-specific modules (e.g.PROVIDER_CALL_STARTfromevents.provider). Import directly and use structured kwargs:logger.info(EVENT, key=value)— neverlogger.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__.pysrc/ai_company/observability/events/task_assignment.pysrc/ai_company/engine/decomposition/llm.pysrc/ai_company/engine/assignment/__init__.pysrc/ai_company/engine/assignment/registry.pysrc/ai_company/engine/assignment/strategies.py
src/ai_company/{engine,providers}/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
RetryExhaustedErrorsignals that all retries failed — the engine layer catches this to trigger fallback chains
Files:
src/ai_company/engine/__init__.pysrc/ai_company/engine/decomposition/llm.pysrc/ai_company/engine/assignment/__init__.pysrc/ai_company/engine/assignment/registry.pysrc/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/smallas aliases.
Files:
src/ai_company/engine/__init__.pysrc/ai_company/observability/events/task_assignment.pytests/unit/engine/conftest.pytests/unit/engine/test_cost_optimized_assignment.pytests/unit/engine/test_auction_assignment.pysrc/ai_company/engine/decomposition/llm.pytests/unit/engine/test_hierarchical_assignment.pysrc/ai_company/engine/assignment/__init__.pysrc/ai_company/engine/assignment/registry.pysrc/ai_company/engine/assignment/strategies.pytests/unit/engine/test_assignment_strategies.pytests/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-xdistvia-n auto
Prefer@pytest.mark.parametrizefor testing similar cases
Files:
tests/unit/engine/conftest.pytests/unit/engine/test_cost_optimized_assignment.pytests/unit/engine/test_auction_assignment.pytests/unit/engine/test_hierarchical_assignment.pytests/unit/engine/test_assignment_strategies.pytests/unit/engine/test_strategy_registry.py
DESIGN_SPEC.md
📄 CodeRabbit inference engine (CLAUDE.md)
When approved deviations occur, update
DESIGN_SPEC.mdto 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_responsecould beNonefrom mypy's perspective, even though logically it should always be set whenlast_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_agenthelper'sstatusparameter.
360-390: LGTM!The
make_assignment_model_configandmake_assignment_agenthelpers 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_taskfactory follows the same pattern as other helpers in this file. The# type: ignore[arg-type]is appropriate here sincedefaultsis dynamically constructed with mixed types fromoverrides.src/ai_company/engine/assignment/registry.py (3)
1-7: LGTM!Clear module docstring explaining the purpose of
STRATEGY_MAPandbuild_strategy_map, and when to use each.
36-58: LGTM!The static
STRATEGY_MAPcorrectly excludesHierarchicalAssignmentStrategysince it requires aHierarchyResolverat construction. UsingMappingProxyTypeensures 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
HierarchyResolveris 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_mapfactory 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_selectedtest validates core functionality with appropriate assertions onselected,strategy_used, andreason.
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 Noneand check for expected reason substrings.tests/unit/engine/test_cost_optimized_assignment.py (3)
1-18: LGTM!Clean imports with proper
pytest.mark.unitmodule 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_usedandreasoncontent.
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
TestManualAssignmentStrategytests are cleanly refactored to usemake_assignment_agentandmake_assignment_taskhelpers, maintaining the same test coverage with less boilerplate.
100-210: LGTM!The
TestRoleBasedAssignmentStrategytests maintain comprehensive coverage while using the shared test helpers. The test logic and assertions are preserved correctly.
212-432: LGTM!The
TestLoadBalancedAssignmentStrategytests 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.
| 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) | ||
| ) |
There was a problem hiding this comment.
🧩 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.pyRepository: 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.pyRepository: 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 2Repository: 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 -50Repository: 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.
| 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, andAuctionAssignmentStrategy, plus new task-assignment observability event constants. - Extract
STRATEGY_MAP+build_strategy_map(...)intoengine/assignment/registry.pyand 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 "", |
There was a problem hiding this comment.
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.
| content=last_response.content or "" if last_response else "", | |
| content=(last_response.content or "") if last_response else "", |
Greptile SummaryThis PR implements three new pluggable task-assignment strategies (Cost-Optimized, Hierarchical, Auction), extracts the strategy registry into its own Key changes:
Confidence Score: 3/5
Important Files Changed
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
|
- 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
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)
…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)
🤖 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).
🤖 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>
Summary
registry.py— keepsstrategies.pyunder 800-line limit, usesTaskAssignmentStrategyprotocol type instead of private_StrategyTypeuniontask_assignment.capability_fallback,task_assignment.delegator.resolved,task_assignment.hierarchy.transitive_fallbackconftest.pyunion-attrerror inllm.pyretry logic (pre-existing on main)Closes #173
Pre-reviewed by 9 agents, 22 findings addressed
Test plan