refactor: adopt NotBlankStr across all models (#108)#120
Conversation
Replace str + Field(min_length=1) + manual @model_validator whitespace checks with NotBlankStr type annotation from core.types across 15 source files. Migrate 80+ fields (scalar and tuple), delete 25+ redundant validators, rename validate_non_blank_unique_strings to validate_unique_strings. Net reduction of ~295 lines. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update CLAUDE.md: NotBlankStr convention from "Planned" to "Adopted", include optional and tuple variants in documentation - Fix 3 stale docstrings in communication/ that still said "non-blank and unique" after blank-check moved to NotBlankStr type annotation - Add empty-string deadline rejection tests for Project and Task - Simplify fragile assertion pattern in test_types.py Pre-reviewed by 9 agents, 6 findings addressed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
📝 WalkthroughWalkthroughThis PR systematically replaces manual string validation with the Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 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 refactors string validation across the codebase by introducing and adopting a custom 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
|
Greptile SummaryThis PR completes the migration of all 15 source modules to use Key changes:
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["Raw field input (str)"] --> B{"StringConstraints\nmin_length=1"}
B -- "empty string ''" --> C["❌ ValidationError\n'at least 1 character'"]
B -- "len ≥ 1" --> D{"AfterValidator\n_check_not_whitespace"}
D -- "value.strip() == ''" --> E["❌ ValidationError\n'must not be whitespace-only'"]
D -- "has non-whitespace content" --> F["✅ Valid NotBlankStr"]
F --> G{Field type}
G -- "NotBlankStr" --> H["Scalar field\ne.g. agent_id, name"]
G -- "NotBlankStr | None" --> I["Optional field\ne.g. fallback_model, lead"]
G -- "tuple[NotBlankStr, ...]" --> J["Collection field\ne.g. traits, subscribers"]
J --> K{"validate_unique_strings\ncalled in @model_validator"}
K -- "duplicates found" --> L["❌ ValidationError\n'Duplicate entries in ...'"]
K -- "all unique" --> M["✅ Valid tuple"]
Last reviewed commit: fbe0ecd |
There was a problem hiding this comment.
Code Review
This pull request is an excellent and extensive refactoring to adopt the NotBlankStr type across all Pydantic models, which significantly reduces boilerplate validation code and improves maintainability. The changes are applied consistently, and the addition of comprehensive tests for the new type is a great enhancement. My review found one minor issue in the test suite where the expected validation error message for whitespace-only strings was incorrect.
There was a problem hiding this comment.
Pull request overview
Refactors string validation across the codebase by centralizing “non-empty + not whitespace-only” enforcement into a shared NotBlankStr type, removing repeated per-model validators and updating tests/docs accordingly.
Changes:
- Introduce and adopt
NotBlankStracross core/budget/communication/observability Pydantic models to replace manual whitespace validators. - Rename
validate_non_blank_unique_strings→validate_unique_strings(uniqueness-only), and update call sites/exports. - Add and update unit tests + documentation to reflect the new type-level validation behavior and error messages.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/observability/test_config.py | Updates validation-error matching for log_dir now validated via NotBlankStr. |
| tests/unit/core/test_types.py | Adds new unit tests covering scalar/optional/tuple NotBlankStr and validate_unique_strings. |
| tests/unit/core/test_task.py | Updates error-message assertions and adds empty-deadline rejection coverage. |
| tests/unit/core/test_role.py | Updates error-message assertions to align with type-level validation. |
| tests/unit/core/test_project.py | Updates error-message assertions and adds empty-deadline rejection coverage. |
| tests/unit/core/test_company.py | Updates tuple-entry error-message assertions to match new validation sources. |
| tests/unit/core/test_artifact.py | Updates whitespace error-message assertions for NotBlankStr fields. |
| tests/unit/core/test_agent.py | Updates tuple-entry error-message assertions for NotBlankStr fields. |
| tests/unit/communication/test_config.py | Updates error-message assertions for channel/participant tuple validation. |
| tests/unit/communication/test_channel.py | Updates error-message assertions for subscriber tuple validation. |
| tests/unit/budget/test_config.py | Updates error-message assertions for downgrade_map aliases validated via NotBlankStr. |
| src/ai_company/observability/config.py | Switches log_dir to NotBlankStr and removes redundant blank-check validator logic. |
| src/ai_company/core/types.py | Adds NotBlankStr and renames uniqueness helper to validate_unique_strings. |
| src/ai_company/core/task.py | Migrates identifiers and tuple fields to NotBlankStr; simplifies validators to deadline/uniqueness logic. |
| src/ai_company/core/role.py | Migrates identifier and tuple fields to NotBlankStr; removes redundant whitespace validators. |
| src/ai_company/core/project.py | Migrates identifiers and tuple fields to NotBlankStr; simplifies validators to deadline/uniqueness logic. |
| src/ai_company/core/company.py | Migrates identifiers and tuple fields to NotBlankStr; removes redundant whitespace validators. |
| src/ai_company/core/artifact.py | Migrates identifier/path fields to NotBlankStr; removes redundant whitespace validators. |
| src/ai_company/core/agent.py | Migrates identifiers and tuple fields to NotBlankStr; removes redundant whitespace validators. |
| src/ai_company/core/init.py | Updates exports/imports to validate_unique_strings. |
| src/ai_company/communication/config.py | Migrates tuple fields to tuple[NotBlankStr, ...] and updates uniqueness helper usage. |
| src/ai_company/communication/channel.py | Migrates subscribers to tuple[NotBlankStr, ...] and updates uniqueness helper usage. |
| src/ai_company/budget/spending_summary.py | Migrates identifier fields to NotBlankStr and removes redundant whitespace validators. |
| src/ai_company/budget/hierarchy.py | Migrates team/department names to NotBlankStr and removes redundant whitespace validators. |
| src/ai_company/budget/cost_record.py | Migrates identifier fields to NotBlankStr and removes redundant whitespace validators. |
| src/ai_company/budget/config.py | Migrates downgrade_map tuple entries to NotBlankStr. |
| DESIGN_SPEC.md | Marks NotBlankStr string validation convention as adopted and updates current-state notes. |
| CLAUDE.md | Updates documented conventions to reflect NotBlankStr adoption (scalar/optional/tuple variants). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| NotBlankStr = Annotated[ | ||
| str, | ||
| StringConstraints(min_length=1), | ||
| AfterValidator(_check_not_whitespace), | ||
| ] | ||
| """A string that must be non-empty and not consist solely of whitespace.""" |
There was a problem hiding this comment.
The standalone string literal after the NotBlankStr alias (line 22) is not a docstring for the alias and has no effect at runtime; it’s easy to miss and can be flagged as a “pointless statement” by linters. Consider turning this into a normal comment above the alias (or using a TypeAlias assignment with an adjacent comment) so the intent is clear.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/ai_company/budget/config.py (1)
97-108:⚠️ Potential issue | 🟠 MajorGuard malformed
downgrade_mapentries in the pre-validator.This
mode="before"normalizer assumes every item is a 2-tuple of strings. Inputs like[(123, "sonnet")]or[("opus", None)]will fail inside.strip()before Pydantic can report a field-levelValidationError, which makes config errors escape as raw runtime exceptions instead of structured validation failures.Suggested fix
`@model_validator`(mode="before") `@classmethod` def _normalize_downgrade_map(cls, data: Any) -> Any: """Strip whitespace from downgrade_map alias strings.""" if isinstance(data, dict) and "downgrade_map" in data: raw_map = data["downgrade_map"] if isinstance(raw_map, (list, tuple)): + normalized: list[Any] = [] + for item in raw_map: + if ( + isinstance(item, (list, tuple)) + and len(item) == 2 + and isinstance(item[0], str) + and isinstance(item[1], str) + ): + normalized.append((item[0].strip(), item[1].strip())) + else: + normalized.append(item) return { **data, - "downgrade_map": tuple((s.strip(), t.strip()) for s, t in raw_map), + "downgrade_map": tuple(normalized), } return dataAs per coding guidelines: Validate at system boundaries: user input, external APIs, config files.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ai_company/budget/config.py` around lines 97 - 108, The pre-validator _normalize_downgrade_map currently assumes every entry in downgrade_map is a 2-tuple of strings and calls .strip() unguarded; modify _normalize_downgrade_map so it only transforms entries when each item is an iterable of length 2 and both elements are str (e.g., check isinstance(item, (list, tuple)) and len(item) == 2 and isinstance(s, str) and isinstance(t, str)), otherwise do not attempt to strip and return the original data unchanged so Pydantic can surface a proper field-level ValidationError for malformed entries in downgrade_map.src/ai_company/core/__init__.py (1)
53-100:⚠️ Potential issue | 🟠 MajorKeep a backwards-compatible export for
validate_non_blank_unique_strings.Removing the old symbol from
ai_company.coremakes this a breaking package-API change for any downstreamfrom ai_company.core import validate_non_blank_unique_strings. Please keep a temporary alias for at least one deprecation cycle, or explicitly document the break in release notes.Suggested compatibility shim
from ai_company.core.types import NotBlankStr, validate_unique_strings + +validate_non_blank_unique_strings = validate_unique_strings __all__ = [ @@ "validate_transition", + "validate_non_blank_unique_strings", "validate_unique_strings", ]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ai_company/core/__init__.py` around lines 53 - 100, The export removed the old symbol validate_non_blank_unique_strings causing a breaking API change; restore backwards compatibility by adding a temporary alias in ai_company.core.__init__.py that sets validate_non_blank_unique_strings = validate_unique_strings and include "validate_non_blank_unique_strings" in __all__; optionally emit a DeprecationWarning in the alias (e.g., using warnings.warn) so callers are notified while keeping the symbol for one deprecation cycle.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@DESIGN_SPEC.md`:
- Line 109: The DESIGN_SPEC.md line describing field types omits the optional
variant; update the sentence referencing NotBlankStr and tuple[NotBlankStr, ...]
to also document the optional form NotBlankStr | None (and tuple[NotBlankStr,
...] | None where appropriate) so the spec matches the implementation and
CLAUDE.md; mention AgentIdentity (frozen Pydantic model in core/agent.py) as
using NotBlankStr, NotBlankStr | None for optional identifier/name fields and
tuple[NotBlankStr, ...] (and its optional variant) for tuple fields to keep
documentation consistent.
---
Outside diff comments:
In `@src/ai_company/budget/config.py`:
- Around line 97-108: The pre-validator _normalize_downgrade_map currently
assumes every entry in downgrade_map is a 2-tuple of strings and calls .strip()
unguarded; modify _normalize_downgrade_map so it only transforms entries when
each item is an iterable of length 2 and both elements are str (e.g., check
isinstance(item, (list, tuple)) and len(item) == 2 and isinstance(s, str) and
isinstance(t, str)), otherwise do not attempt to strip and return the original
data unchanged so Pydantic can surface a proper field-level ValidationError for
malformed entries in downgrade_map.
In `@src/ai_company/core/__init__.py`:
- Around line 53-100: The export removed the old symbol
validate_non_blank_unique_strings causing a breaking API change; restore
backwards compatibility by adding a temporary alias in
ai_company.core.__init__.py that sets validate_non_blank_unique_strings =
validate_unique_strings and include "validate_non_blank_unique_strings" in
__all__; optionally emit a DeprecationWarning in the alias (e.g., using
warnings.warn) so callers are notified while keeping the symbol for one
deprecation cycle.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 92df3fa7-e139-40ed-bd8c-dd13c158a4c0
📒 Files selected for processing (28)
CLAUDE.mdDESIGN_SPEC.mdsrc/ai_company/budget/config.pysrc/ai_company/budget/cost_record.pysrc/ai_company/budget/hierarchy.pysrc/ai_company/budget/spending_summary.pysrc/ai_company/communication/channel.pysrc/ai_company/communication/config.pysrc/ai_company/core/__init__.pysrc/ai_company/core/agent.pysrc/ai_company/core/artifact.pysrc/ai_company/core/company.pysrc/ai_company/core/project.pysrc/ai_company/core/role.pysrc/ai_company/core/task.pysrc/ai_company/core/types.pysrc/ai_company/observability/config.pytests/unit/budget/test_config.pytests/unit/communication/test_channel.pytests/unit/communication/test_config.pytests/unit/core/test_agent.pytests/unit/core/test_artifact.pytests/unit/core/test_company.pytests/unit/core/test_project.pytests/unit/core/test_role.pytests/unit/core/test_task.pytests/unit/core/test_types.pytests/unit/observability/test_config.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 (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python files — Python 3.14 has PEP 649 native lazy annotations
Use PEP 758 except syntax withexcept A, B:(no parentheses) for multiple exception handlers in Python 3.14
Include type hints on all public functions in Python
Use Google-style docstrings (required on public classes and functions, enforced by ruff D rules)
Create new objects for immutability — never mutate existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction andMappingProxyTypefor read-only enforcement
Usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization) fordict/listfields in frozen Pydantic models
Use frozen Pydantic models for config and identity; use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves
Use Pydantic v2 conventions:BaseModel,model_validator,computed_field,ConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields
UseNotBlankStrfromcore.typesfor non-optional identifier and name fields in Pydantic models instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations (multiple tool invocations, parallel agent calls) instead of barecreate_task
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly — never silently swallow exceptions
Validate at system boundaries: user input, external APIs, config files
Set line length to 88 characters (enforced by ruff)
Files:
tests/unit/core/test_artifact.pytests/unit/communication/test_config.pytests/unit/core/test_role.pysrc/ai_company/observability/config.pysrc/ai_company/budget/config.pysrc/ai_company/budget/cost_record.pysrc/ai_company/core/project.pytests/unit/communication/test_channel.pysrc/ai_company/core/__init__.pysrc/ai_company/communication/channel.pysrc/ai_company/budget/hierarchy.pysrc/ai_company/budget/spending_summary.pysrc/ai_company/communication/config.pytests/unit/core/test_task.pytests/unit/budget/test_config.pysrc/ai_company/core/types.pytests/unit/core/test_project.pytests/unit/core/test_company.pytests/unit/core/test_types.pysrc/ai_company/core/role.pytests/unit/core/test_agent.pysrc/ai_company/core/company.pysrc/ai_company/core/artifact.pytests/unit/observability/test_config.pysrc/ai_company/core/agent.pysrc/ai_company/core/task.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow
Use vendor-agnostic fixture identifiers (e.g.,test-haiku-001,test-provider) — never use real vendor model IDs to avoid coupling tests to external providers
Files:
tests/unit/core/test_artifact.pytests/unit/communication/test_config.pytests/unit/core/test_role.pytests/unit/communication/test_channel.pytests/unit/core/test_task.pytests/unit/budget/test_config.pytests/unit/core/test_project.pytests/unit/core/test_company.pytests/unit/core/test_types.pytests/unit/core/test_agent.pytests/unit/observability/test_config.py
src/ai_company/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/ai_company/**/*.py: Every module with business logic must import logger withfrom ai_company.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging,logging.getLogger(), orprint()in application code — use the project's logger setup
Always useloggeras the variable name for the logger, not_loggerorlog
Use constants fromai_company.observability.eventsfor event names (e.g., PROVIDER_CALL_START, BUDGET_RECORD_ADDED, TOOL_INVOKE_START), imported directly
Use structured logging:logger.info(EVENT, key=value)— never uselogger.info("msg %s", val)
Log all error paths at WARNING or ERROR with context before raising
Log all state transitions at INFO level
Use DEBUG logging for object creation, internal flow, and entry/exit of key functions
Files:
src/ai_company/observability/config.pysrc/ai_company/budget/config.pysrc/ai_company/budget/cost_record.pysrc/ai_company/core/project.pysrc/ai_company/core/__init__.pysrc/ai_company/communication/channel.pysrc/ai_company/budget/hierarchy.pysrc/ai_company/budget/spending_summary.pysrc/ai_company/communication/config.pysrc/ai_company/core/types.pysrc/ai_company/core/role.pysrc/ai_company/core/company.pysrc/ai_company/core/artifact.pysrc/ai_company/core/agent.pysrc/ai_company/core/task.py
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to **/*.py : Use `NotBlankStr` from `core.types` for non-optional identifier and name fields in Pydantic models instead of manual whitespace validators
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to **/*.py : Use `NotBlankStr` from `core.types` for non-optional identifier and name fields in Pydantic models instead of manual whitespace validators
Applied to files:
src/ai_company/observability/config.pysrc/ai_company/budget/config.pysrc/ai_company/core/project.pysrc/ai_company/budget/hierarchy.pysrc/ai_company/budget/spending_summary.pytests/unit/core/test_types.pyDESIGN_SPEC.mdsrc/ai_company/core/role.pysrc/ai_company/core/artifact.pysrc/ai_company/core/task.py
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to src/ai_company/**/*.py : Use constants from `ai_company.observability.events` for event names (e.g., PROVIDER_CALL_START, BUDGET_RECORD_ADDED, TOOL_INVOKE_START), imported directly
Applied to files:
src/ai_company/observability/config.pysrc/ai_company/core/__init__.pysrc/ai_company/core/task.py
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to **/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields
Applied to files:
src/ai_company/observability/config.pyDESIGN_SPEC.mdCLAUDE.md
📚 Learning: 2026-01-24T09:54:45.426Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/instructions/agents.instructions.md:0-0
Timestamp: 2026-01-24T09:54:45.426Z
Learning: Applies to agents/*.py : Validate all LLM outputs by checking required fields, verifying data types, handling malformed responses, and using Pydantic models for structured data
Applied to files:
src/ai_company/budget/cost_record.pyCLAUDE.mdsrc/ai_company/core/agent.py
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to **/*.py : Use frozen Pydantic models for config and identity; use separate mutable-via-copy models with `model_copy(update=...)` for runtime state that evolves
Applied to files:
DESIGN_SPEC.mdCLAUDE.md
📚 Learning: 2026-01-24T16:33:29.354Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-24T16:33:29.354Z
Learning: Applies to {src/memory/**/*.py,src/services/**/*.py} : Story state is maintained through `src/memory/story_state.py` module using Pydantic models for validation (StoryState, Character, Chapter, etc.)
Applied to files:
DESIGN_SPEC.md
📚 Learning: 2026-02-26T17:43:50.902Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-26T17:43:50.902Z
Learning: Applies to src/agents/**/*.py : Agents must extend `BaseAgent`, use retry logic, and implement configurable timeout via settings.
Applied to files:
DESIGN_SPEC.mdCLAUDE.md
📚 Learning: 2026-01-26T08:59:32.818Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-26T08:59:32.818Z
Learning: Applies to src/memory/story_state.py : Story state must be maintained through `src/memory/story_state.py` module using Pydantic models for validation (StoryState, Character, Chapter, etc.)
Applied to files:
DESIGN_SPEC.md
📚 Learning: 2026-01-24T09:54:45.426Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/instructions/agents.instructions.md:0-0
Timestamp: 2026-01-24T09:54:45.426Z
Learning: Applies to agents/*.py : Use `StoryState` from `memory/story_state.py` for context management and balance context size vs. token limits when passing story context
Applied to files:
DESIGN_SPEC.md
📚 Learning: 2026-01-24T09:54:45.426Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/instructions/agents.instructions.md:0-0
Timestamp: 2026-01-24T09:54:45.426Z
Learning: Applies to agents/*.py : Update story state by directly modifying fields on Pydantic model instances (append to chapters list, find and modify characters by ID)
Applied to files:
DESIGN_SPEC.md
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization) for `dict`/`list` fields in frozen Pydantic models
Applied to files:
DESIGN_SPEC.mdCLAUDE.md
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to **/*.py : Create new objects for immutability — never mutate existing ones. For non-Pydantic internal collections, use `copy.deepcopy()` at construction and `MappingProxyType` for read-only enforcement
Applied to files:
DESIGN_SPEC.mdCLAUDE.md
📚 Learning: 2026-01-26T08:59:32.818Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-26T08:59:32.818Z
Learning: Applies to **/*.py : Use type hints where appropriate. Use Pydantic models for data validation in `src/memory/story_state.py`, dataclasses in `src/settings.py`.
Applied to files:
DESIGN_SPEC.mdCLAUDE.md
📚 Learning: 2026-02-26T17:43:50.902Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-26T17:43:50.902Z
Learning: When making changes that affect architecture, services, key files, settings, or workflows, update the relevant sections of existing documentation (CLAUDE.md, README.md, etc.) to reflect those changes.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to **/*.py : Use Google-style docstrings (required on public classes and functions, enforced by ruff D rules)
Applied to files:
CLAUDE.md
📚 Learning: 2026-01-26T08:59:32.818Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-26T08:59:32.818Z
Learning: Applies to **/*.py : Line length: 100 characters (enforced by Ruff)
Applied to files:
CLAUDE.md
📚 Learning: 2026-02-26T17:43:50.902Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-26T17:43:50.902Z
Learning: Applies to **/*.py : Maintain line length at 100 characters per Ruff formatting rules.
Applied to files:
CLAUDE.md
📚 Learning: 2026-01-24T09:54:45.426Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/instructions/agents.instructions.md:0-0
Timestamp: 2026-01-24T09:54:45.426Z
Learning: Applies to agents/test*.py : Agent tests should cover: successful generation with valid output, handling malformed LLM responses, error conditions (network errors, timeouts), output format validation, and integration with story state
Applied to files:
tests/unit/core/test_agent.py
📚 Learning: 2026-03-06T08:46:25.159Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-06T08:46:25.159Z
Learning: Applies to src/ai_company/**/*.py : Every module with business logic must import logger with `from ai_company.observability import get_logger` then `logger = get_logger(__name__)`
Applied to files:
src/ai_company/core/task.py
🪛 LanguageTool
DESIGN_SPEC.md
[style] ~109-~109: Consider using the typographical ellipsis character here instead.
Context: ... whitespace rejection; tuple fields use tuple[NotBlankStr, ...] for per-element validation. ```yaml ...
(ELLIPSIS)
[style] ~1442-~1442: Consider using the typographical ellipsis character here instead.
Context: ...lds use NotBlankStr; tuple fields use tuple[NotBlankStr, ...] for per-element validation. | | **Sha...
(ELLIPSIS)
CLAUDE.md
[style] ~71-~71: Consider using the typographical ellipsis character here instead.
Context: ...ional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitesp...
(ELLIPSIS)
🔇 Additional comments (32)
src/ai_company/budget/cost_record.py (1)
11-11: The non-blank constraint is cleanly centralized here.Using
NotBlankStron the identifier/provider fields keeps field-level validation in one place and leaves the model validator focused on token consistency only.Also applies to: 34-37
src/ai_company/communication/channel.py (1)
8-10: Validation responsibility is cleanly separated.
tuple[NotBlankStr, ...]now handles blank/whitespace subscriber entries, whilevalidate_unique_strings()keeps the model validator scoped to uniqueness only.Also applies to: 30-39
tests/unit/communication/test_channel.py (1)
47-53: These assertions match the new two-path validation behavior.The updated expectations now cover both the empty-string branch and the whitespace-only branch for subscriber entries.
tests/unit/communication/test_config.py (1)
47-52: The updated expectations are consistent across channels and participants.These assertions now track the separate empty-string and whitespace-only failures introduced by the shared non-blank type.
Also applies to: 156-168
src/ai_company/core/agent.py (1)
17-17: The type-level migration is consistent across the nested agent models.This moves the non-blank rule to the field/item types for scalar, optional, and tuple cases without reintroducing per-model whitespace checks.
Also applies to: 33-40, 65-72, 88-104, 146-153, 196-198
tests/unit/core/test_agent.py (1)
75-83: Good coverage update for tuple-item validation.These assertions now exercise the separate empty-string and whitespace-only failures across traits, skills, and tool names.
Also applies to: 119-127, 344-352
tests/unit/observability/test_config.py (1)
233-235: This assertion now lines up with the shared non-blank validator.Matching the whitespace-only branch here keeps the observability config tests consistent with the rest of the migration.
tests/unit/core/test_artifact.py (1)
32-34: The artifact tests now match the shared validation contract.Using the same whitespace-only expectation across these identifier and path fields keeps this suite aligned with the type-level validator.
Also applies to: 91-133
tests/unit/core/test_types.py (1)
14-128: Good coverage for the new type-level contract.These tests pin down the scalar, optional, and tuple forms of
NotBlankStr, plus the duplicate-reporting behavior ofvalidate_unique_strings, which is the main regression surface in this refactor.src/ai_company/core/project.py (4)
10-10: LGTM on import.The
# noqa: TC001is appropriate here sinceNotBlankStris used in runtime type annotations for Pydantic fields.
34-51: LGTM on NotBlankStr field adoption.The field type changes correctly apply
NotBlankStrto identifier and name fields (id,name,lead,team,task_ids), aligning with the coding guidelines. The optionalleadfield correctly usesNotBlankStr | None.
66-78: LGTM on deadline validation.The validator correctly:
- Checks for whitespace-only strings before format parsing
- Validates ISO 8601 format using
datetime.fromisoformat- Uses
from Noneto suppress the chained exception for cleaner error messages
80-91: LGTM on collection uniqueness validation.The validator is correctly narrowed to uniqueness-only checks since
NotBlankStrnow handles the non-blank validation at the type level for each tuple element.src/ai_company/core/company.py (7)
28-33: LGTM on Team field updates.The
name,lead, andmembersfields correctly useNotBlankStrandtuple[NotBlankStr, ...]for identifier fields.
35-42: LGTM on duplicate member validation.The validator correctly focuses only on uniqueness since
NotBlankStrhandles non-blank validation at the type level.
61-62: LGTM on Department field updates.
nameandheadcorrectly useNotBlankStrfor identifier fields.
108-115: LGTM on CompanyConfig field updates.
communication_patternandtool_access_defaultcorrectly useNotBlankStrvariants for pattern names and tool identifiers.
132-143: LGTM on HRRegistry field updates.The tuple fields correctly use
tuple[NotBlankStr, ...]. The design decision to allow duplicates inavailable_rolesandhiring_queue(per the docstring) while enforcing uniqueness only onactive_agentsis sensible and correctly implemented.
145-153: LGTM on active agents uniqueness validation.The validator correctly enforces uniqueness only for
active_agentsas documented, leavingavailable_rolesandhiring_queueto allow duplicates for multiple openings.
175-175: LGTM on Company.name field update.The
namefield correctly usesNotBlankStr. Theidfield appropriately remains asUUIDsince it's not a string identifier.src/ai_company/core/role.py (6)
25-25: LGTM on Skill.name field update.The
namefield correctly usesNotBlankStrfor the skill identifier.
45-56: LGTM on Authority field updates.The fields correctly use
NotBlankStrvariants:can_approveandcan_delegate_toas tuples,reports_toas optional. This ensures task types and role references are non-blank.
77-85: LGTM on SeniorityInfo field updates.The
authority_scope,typical_model_tier, andcost_tierfields correctly useNotBlankStrfor descriptive string fields that should never be blank.
103-122: LGTM on Role field updates.The field type changes correctly apply
NotBlankStrtoname,required_skills,tool_access, andsystem_prompt_template. The optionalsystem_prompt_templatecorrectly usesNotBlankStr | None.
147-166: LGTM on CustomRole field updates.The field type changes correctly apply
NotBlankStrvariants toname,required_skills,system_prompt_template, andsuggested_model.
168-178: LGTM on department validator retention.The
_department_not_emptyvalidator is correctly retained forCustomRole.departmentsince it's a union typeDepartmentName | str.NotBlankStrcannot be applied directly to a union with an enum, so manual validation for thestrbranch is appropriate.src/ai_company/core/task.py (6)
29-31: LGTM on AcceptanceCriterion.description field update.The
descriptionfield correctly usesNotBlankStrto ensure criterion text is never blank.
66-93: LGTM on Task identifier field updates.The field type changes correctly apply
NotBlankStrto all identifier and text fields:id,title,description,project,created_by,assigned_to,reviewers, anddependencies. The optionalassigned_tocorrectly usesNotBlankStr | None.
120-132: LGTM on deadline validation.The validator correctly matches the pattern in
project.py:
- Whitespace-only check before parsing
- ISO 8601 format validation via
datetime.fromisoformat- Clean exception handling with
from NoneThis consistency across
ProjectandTaskdeadline validation is good.
134-148: LGTM on collection validation.The validator correctly:
- Prevents self-dependency
- Enforces uniqueness for both
dependenciesandreviewersNon-blank validation is now handled at the type level by
NotBlankStr.
150-171: LGTM on assignment consistency validation.The validator correctly enforces status-based assignment rules. This is business logic orthogonal to the
NotBlankStrmigration.
173-206: LGTM on with_transition method.The method correctly:
- Prevents
statusoverride in favor of explicittargetparameter- Validates transitions via
validate_transition- Uses
model_validateto run all validators on the new instance- Uses structured logging with the
TASK_STATUS_CHANGEDconstantThis aligns with the coding guidelines for logging. Based on learnings: "Use constants from
ai_company.observability.eventsfor event names".
DESIGN_SPEC.md
Outdated
| - **Runtime state (mutable-via-copy)**: current status, active task, conversation history, execution metrics. Evolves during agent operation. Represented as Pydantic models using `model_copy(update=...)` for state transitions — never mutated in place. | ||
|
|
||
| > **Current state (M2):** Only the config layer exists as `AgentIdentity` (frozen Pydantic model in `core/agent.py`). The runtime state layer will be introduced in M3 when the agent execution engine is implemented. Non-optional identifier fields currently use `str` with `Field(min_length=1)` + a manual `@model_validator`; migration to `NotBlankStr` (from `core.types`) is planned. | ||
| > **Current state (M2):** Only the config layer exists as `AgentIdentity` (frozen Pydantic model in `core/agent.py`). The runtime state layer will be introduced in M3 when the agent execution engine is implemented. All identifier/name fields use `NotBlankStr` (from `core.types`) for automatic whitespace rejection; tuple fields use `tuple[NotBlankStr, ...]` for per-element validation. |
There was a problem hiding this comment.
Document the optional NotBlankStr | None variant here too.
These two spec updates now mention scalar and tuple usage, but the adopted convention in this PR also covers optional fields. Since DESIGN_SPEC.md is the required source of truth, leaving that variant out here will keep it out of sync with CLAUDE.md and the implementation.
Based on learnings: When making changes that affect architecture, services, key files, settings, or workflows, update the relevant sections of existing documentation (CLAUDE.md, README.md, etc.) to reflect those changes.
Also applies to: 1442-1442
🧰 Tools
🪛 LanguageTool
[style] ~109-~109: Consider using the typographical ellipsis character here instead.
Context: ... whitespace rejection; tuple fields use tuple[NotBlankStr, ...] for per-element validation. ```yaml ...
(ELLIPSIS)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@DESIGN_SPEC.md` at line 109, The DESIGN_SPEC.md line describing field types
omits the optional variant; update the sentence referencing NotBlankStr and
tuple[NotBlankStr, ...] to also document the optional form NotBlankStr | None
(and tuple[NotBlankStr, ...] | None where appropriate) so the spec matches the
implementation and CLAUDE.md; mention AgentIdentity (frozen Pydantic model in
core/agent.py) as using NotBlankStr, NotBlankStr | None for optional
identifier/name fields and tuple[NotBlankStr, ...] (and its optional variant)
for tuple fields to keep documentation consistent.
…ilot - Guard malformed downgrade_map entries in before-validator (CodeRabbit) - Clarify _normalize_downgrade_map docstring purpose (comment-analyzer) - Use NotBlankStr for logger_levels logger name (type-design-analyzer) - Add NotBlankStr | None optional variant to DESIGN_SPEC.md (CodeRabbit) - Fix CostRecord JSON example removing non-existent total_tokens (docs-consistency) - Rename misleading test names: empty → whitespace (pr-test-analyzer) - Remove redundant "(must be non-blank)" from ToolDefinition docstring (comment-analyzer) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 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
@model_validatorwhitespace checks withNotBlankStracross all 15 source modules incore/,budget/,communication/, andobservability/validate_non_blank_unique_strings→validate_unique_strings— blank-check responsibility moved to theNotBlankStrtype annotation; the helper now only enforces uniquenessNotBlankStrtype alias incore/types.py:Annotated[str, StringConstraints(min_length=1), AfterValidator(_check_not_whitespace)]— rejects both empty and whitespace-only strings with distinct error messagesNotBlankStrconvention as "Adopted" (was "Planned"), update §3.1 current state noteNotBlankStrto "Adopted" convention, document all variants (scalar, optional, tuple)communication/that still said "non-blank and unique" after blank-check moved to type leveltests/unit/core/test_types.pyfor scalar, optional, and tupleNotBlankStrvariantsProjectandTaskTest plan
uv run ruff check src/ tests/— all checks passeduv run ruff format src/ tests/— no changes neededuv run mypy src/ tests/— no issues in 200 source filesuv run pytest tests/ -n auto --cov=ai_company --cov-fail-under=80— 1811 passed, 94.98% coverageReview coverage
Pre-reviewed by 9 agents: code-reviewer, python-reviewer, pr-test-analyzer, silent-failure-hunter, comment-analyzer, type-design-analyzer, logging-audit, resilience-audit, docs-consistency. 6 findings addressed, 0 skipped.
Closes #108