feat: add pluggable MemoryBackend protocol with models, config, and events#180
feat: add pluggable MemoryBackend protocol with models, config, and events#180
Conversation
…vents (#32) Implement the abstract memory system interface per DESIGN_SPEC §7.1-7.3: - Rename MemoryType → MemoryLevel (fixes naming collision with memory categories); add MemoryCategory (5 types) and ConsolidationInterval enums - Add MemoryBackend protocol (connect/disconnect/store/retrieve/get/delete/count) - Add MemoryCapabilities protocol for runtime capability discovery - Add SharedKnowledgeStore protocol for cross-agent shared knowledge - Add frozen Pydantic models: MemoryMetadata, MemoryStoreRequest, MemoryEntry, MemoryQuery (with since/until validation) - Add CompanyMemoryConfig (backend selection + storage + options), integrated into RootConfig - Add MemoryError hierarchy (7 error classes) - Add memory event constants (20 events) for structured logging - Add placeholder factory (raises MemoryConfigError for mem0 — concrete impl in #41) - Add 114 unit tests with fake backend verifying protocol compliance, per-agent isolation, and model validation Closes #32
Pre-reviewed by 10 agents, 34 findings addressed: - Fix misleading MEMORY_BACKEND_CREATED log in factory (use warning) - Add logger + validation logging to agent.py, models.py, schema.py - Add Raises sections to protocol.py and shared.py docstrings - Clarify health_check() semantics in protocol - Add MEMORY_ENTRY_FETCHED/FETCH_FAILED event constants - Fix events module docstring to cover all log levels - Add allow_inf_nan=False to MemoryStoreRequest - Add tag deduplication validator to MemoryMetadata - Add updated_at >= created_at validator to MemoryEntry - Document MemoryLevel spec deviation (persistent vs full) - Document dead else branch in factory as defensive guard - Improve data_dir field description (Docker mount default) - Add logging to RetryConfig._validate_delay_ordering - Add enum member count/value tests for MemoryCategory, ConsolidationInterval - Add MemoryQuery roundtrip test assertions for all fields - Add NaN rejection tests for relevance_score, min_relevance - Add timestamp ordering tests for MemoryEntry - Add negative protocol compliance tests (capabilities, protocol, shared) - Add __all__ re-export importability test - Add path traversal edge case tests - Add memory event value assertion tests - Update DESIGN_SPEC §7.5 with MemoryBackend protocol docs - Update DESIGN_SPEC §15.3 with actual memory/ file listing - Fix DESIGN_SPEC §7.3 level: "full" -> "persistent" - Update implementation snapshot
|
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 (5)
📝 WalkthroughSummary by CodeRabbit
WalkthroughIntroduces a pluggable Memory subsystem: public protocols (MemoryBackend, MemoryCapabilities, SharedKnowledgeStore), data models/config/factory, a MemoryError hierarchy, observability events, core enum changes (MemoryLevel, MemoryCategory, ConsolidationInterval), and extensive unit tests and config integration. Changes
Sequence Diagram(s)mermaid Agent->>Backend: connect() Agent->>Backend: store(agent_id, MemoryStoreRequest) Agent->>Backend: retrieve(agent_id, MemoryQuery) Agent->>Shared: publish(agent_id, MemoryStoreRequest) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 agent memory system by introducing a pluggable architecture. It defines clear protocols for memory backends, capability discovery, and shared knowledge, alongside robust Pydantic models for memory data and configuration. This foundational work ensures that the memory layer is extensible, well-validated, and properly integrated into the overall system, paving the way for future backend implementations like Mem0. 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 is an excellent and comprehensive pull request that introduces a pluggable memory backend system. The design is robust, featuring clear protocols, well-defined data models, thorough configuration options, and a logical error hierarchy. The code quality is high, with strong validation, consistent logging, and extensive test coverage. I've identified a few minor areas for improvement to enhance documentation consistency and code clarity, but overall, this is a very strong contribution to the framework.
|
|
||
| @property | ||
| def supported_types(self) -> frozenset[MemoryType]: ... | ||
| def supported_types(self) -> frozenset[MemoryCategory]: ... |
There was a problem hiding this comment.
There's an inconsistency in the property name for the MemoryCapabilities protocol. Here it's supported_types, but in the implementation (src/ai_company/memory/capabilities.py) and DESIGN_SPEC.md, it's supported_categories. Using supported_categories is more descriptive and consistent with the MemoryCategory enum.
Additionally, the protocol definition in this ADR is incomplete compared to the final implementation. It's missing the supports_shared_access and max_memories_per_agent properties. Please update this ADR to align with the implementation for consistency.
| MemoryCapabilityError, | ||
| MemoryConfigError, | ||
| MemoryConnectionError, | ||
| MemoryError, # noqa: A004 |
There was a problem hiding this comment.
The noqa comment here is incorrect. A004 is for a "star import used" warning, but the issue here is shadowing the built-in MemoryError, which corresponds to code A001. Using the correct suppression code improves maintainability and ensures the right check is being silenced.
| MemoryError, # noqa: A004 | |
| MemoryError, # noqa: A001 |
| @model_validator(mode="after") | ||
| def _deduplicate_tags(self) -> Self: | ||
| """Remove duplicate tags while preserving order.""" | ||
| seen: set[str] = set() | ||
| deduped: list[str] = [] | ||
| for tag in self.tags: | ||
| if tag not in seen: | ||
| seen.add(tag) | ||
| deduped.append(tag) | ||
| if len(deduped) != len(self.tags): | ||
| object.__setattr__(self, "tags", tuple(deduped)) | ||
| return self |
There was a problem hiding this comment.
The implementation of _deduplicate_tags is correct but can be made more concise and Pythonic. Using dict.fromkeys() is a more idiomatic way to get unique items from a list while preserving order (since Python 3.7). This would improve readability and maintainability.
| @model_validator(mode="after") | |
| def _deduplicate_tags(self) -> Self: | |
| """Remove duplicate tags while preserving order.""" | |
| seen: set[str] = set() | |
| deduped: list[str] = [] | |
| for tag in self.tags: | |
| if tag not in seen: | |
| seen.add(tag) | |
| deduped.append(tag) | |
| if len(deduped) != len(self.tags): | |
| object.__setattr__(self, "tags", tuple(deduped)) | |
| return self | |
| @model_validator(mode="after") | |
| def _deduplicate_tags(self) -> Self: | |
| """Remove duplicate tags while preserving order.""" | |
| if not self.tags: | |
| return self | |
| # dict.fromkeys preserves insertion order since Python 3.7. | |
| unique_tags = tuple(dict.fromkeys(self.tags)) | |
| if len(unique_tags) != len(self.tags): | |
| object.__setattr__(self, "tags", unique_tags) | |
| return self |
Greptile SummaryThis PR introduces the pluggable Key remaining items to address:
Confidence Score: 4/5
Important Files Changed
Class Diagram%%{init: {'theme': 'neutral'}}%%
classDiagram
class MemoryBackend {
<<Protocol>>
+is_connected: bool
+backend_name: str
+connect() None
+disconnect() None
+health_check() bool
+store(agent_id, request) NotBlankStr
+retrieve(agent_id, query) tuple[MemoryEntry, ...]
+get(agent_id, memory_id) MemoryEntry | None
+delete(agent_id, memory_id) bool
+count(agent_id, category) int
}
class MemoryCapabilities {
<<Protocol>>
+supported_categories: frozenset[MemoryCategory]
+supports_graph: bool
+supports_temporal: bool
+supports_vector_search: bool
+supports_shared_access: bool
+max_memories_per_agent: int | None
}
class SharedKnowledgeStore {
<<Protocol>>
+publish(agent_id, request) NotBlankStr
+search_shared(query, exclude_agent) tuple[MemoryEntry, ...]
+retract(agent_id, memory_id) bool
}
class MemoryEntry {
+id: NotBlankStr
+agent_id: NotBlankStr
+category: MemoryCategory
+content: NotBlankStr
+metadata: MemoryMetadata
+created_at: AwareDatetime
+updated_at: AwareDatetime | None
+expires_at: AwareDatetime | None
+relevance_score: float | None
}
class MemoryStoreRequest {
+category: MemoryCategory
+content: NotBlankStr
+metadata: MemoryMetadata
+expires_at: AwareDatetime | None
}
class MemoryQuery {
+text: NotBlankStr | None
+categories: frozenset[MemoryCategory] | None
+tags: tuple[NotBlankStr, ...]
+min_relevance: float
+limit: int
+since: AwareDatetime | None
+until: AwareDatetime | None
}
class CompanyMemoryConfig {
+backend: NotBlankStr
+level: MemoryLevel
+storage: MemoryStorageConfig
+options: MemoryOptionsConfig
}
class MemoryError {
<<Exception>>
}
class MemoryConnectionError
class MemoryStoreError
class MemoryRetrievalError
class MemoryNotFoundError
class MemoryConfigError
class MemoryCapabilityError
MemoryBackend ..> MemoryStoreRequest : store() accepts
MemoryBackend ..> MemoryQuery : retrieve() accepts
MemoryBackend ..> MemoryEntry : retrieve()/get() returns
SharedKnowledgeStore ..> MemoryStoreRequest : publish() accepts
SharedKnowledgeStore ..> MemoryEntry : search_shared() returns
CompanyMemoryConfig --> MemoryBackend : factory creates
MemoryConnectionError --|> MemoryError
MemoryStoreError --|> MemoryError
MemoryRetrievalError --|> MemoryError
MemoryNotFoundError --|> MemoryError
MemoryConfigError --|> MemoryError
MemoryCapabilityError --|> MemoryError
Last reviewed commit: 377d4d2 |
There was a problem hiding this comment.
Pull request overview
Introduces the foundational, pluggable agent memory interface layer (protocols + frozen models + config + events) and wires it into core configuration, enabling future concrete backends (e.g., Mem0 adapter in #41) without coupling application code to an implementation.
Changes:
- Added
MemoryBackend,MemoryCapabilities, andSharedKnowledgeStoreruntime-checkable protocols plus a placeholdercreate_memory_backend()factory. - Added frozen Pydantic memory models and company-level memory config with validation + new structured logging event constants.
- Updated core enums/config/docs/tests to reflect the new memory system (
MemoryLevel,MemoryCategory, etc.) and to validate/cover the new API surface.
Reviewed changes
Copilot reviewed 29 out of 30 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/observability/test_events.py | Extends event-module discovery expectations and asserts MEMORY_* constants. |
| tests/unit/memory/test_shared.py | Adds protocol compliance + basic behavior tests for SharedKnowledgeStore. |
| tests/unit/memory/test_protocol.py | Adds protocol compliance + basic behavior tests for MemoryBackend. |
| tests/unit/memory/test_models.py | Adds validation and JSON roundtrip tests for memory domain models. |
| tests/unit/memory/test_init.py | Verifies ai_company.memory re-exports via __all__. |
| tests/unit/memory/test_factory.py | Tests create_memory_backend() placeholder error behavior + config validation. |
| tests/unit/memory/test_errors.py | Tests the memory error hierarchy invariants. |
| tests/unit/memory/test_config.py | Tests memory config defaults, immutability, validation (incl. traversal checks), and JSON roundtrip. |
| tests/unit/memory/test_capabilities.py | Adds protocol compliance tests for MemoryCapabilities. |
| tests/unit/memory/init.py | Introduces memory unit test package marker. |
| tests/unit/core/test_enums.py | Updates enum coverage for new memory enums and renames usage from MemoryType to MemoryLevel. |
| tests/unit/core/test_agent.py | Updates agent memory config tests to use MemoryLevel. |
| tests/unit/core/conftest.py | Updates factories to use MemoryLevel. |
| tests/unit/config/conftest.py | Extends RootConfigFactory to include company memory config. |
| src/ai_company/observability/events/memory.py | Adds structured logging event constants for memory lifecycle and operations. |
| src/ai_company/memory/shared.py | Adds SharedKnowledgeStore protocol definition. |
| src/ai_company/memory/protocol.py | Adds MemoryBackend protocol definition (lifecycle + CRUD). |
| src/ai_company/memory/models.py | Adds frozen Pydantic models for entries/requests/queries with validation + logging on validation failures. |
| src/ai_company/memory/factory.py | Adds placeholder backend factory (currently raises). |
| src/ai_company/memory/errors.py | Adds memory error hierarchy. |
| src/ai_company/memory/config.py | Adds frozen company memory config models + traversal/back-end validation. |
| src/ai_company/memory/capabilities.py | Adds MemoryCapabilities protocol definition. |
| src/ai_company/memory/init.py | Adds memory package re-exports for public API ergonomics. |
| src/ai_company/core/enums.py | Introduces MemoryLevel, MemoryCategory, ConsolidationInterval enums and removes MemoryType. |
| src/ai_company/core/agent.py | Updates agent MemoryConfig to use MemoryLevel and adds validation logging. |
| src/ai_company/core/init.py | Re-exports new memory-related enums from the core package. |
| src/ai_company/config/schema.py | Adds memory: CompanyMemoryConfig into RootConfig and adds validation logging in retry config. |
| src/ai_company/config/defaults.py | Adds default memory section to generated default config dict. |
| docs/decisions/ADR-001-memory-layer.md | Updates ADR protocol snippet type to MemoryCategory. |
| DESIGN_SPEC.md | Documents the new memory protocol section and updates section numbering/index references. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| @property | ||
| def supported_types(self) -> frozenset[MemoryType]: ... | ||
| def supported_types(self) -> frozenset[MemoryCategory]: ... |
There was a problem hiding this comment.
In the ADR protocol snippet, the property is shown as supported_types, but the implemented MemoryCapabilities protocol in src/ai_company/memory/capabilities.py exposes supported_categories. This mismatch will mislead readers/implementers; update the ADR snippet to use the correct property name (or rename the protocol property if the ADR is intended as the source of truth).
| def supported_types(self) -> frozenset[MemoryCategory]: ... | |
| def supported_categories(self) -> frozenset[MemoryCategory]: ... |
| if config.backend == "mem0": | ||
| msg = "mem0 backend not yet implemented" | ||
| logger.warning( | ||
| MEMORY_BACKEND_UNKNOWN, | ||
| backend="mem0", | ||
| reason=msg, | ||
| ) |
There was a problem hiding this comment.
create_memory_backend() logs MEMORY_BACKEND_UNKNOWN for the mem0 backend even though it is a known backend that is merely not implemented yet. This will make observability/alerting ambiguous. Consider logging a more accurate event (e.g., introduce a MEMORY_BACKEND_NOT_IMPLEMENTED constant) and reserve MEMORY_BACKEND_UNKNOWN for truly unknown backend names.
tests/unit/core/test_enums.py
Outdated
| @@ -47,7 +49,7 @@ def test_creativity_level_has_3_members(self) -> None: | |||
| assert len(CreativityLevel) == 3 | |||
|
|
|||
| def test_memory_type_has_4_members(self) -> None: | |||
There was a problem hiding this comment.
Test name test_memory_type_has_4_members no longer matches what it's asserting (MemoryLevel). Renaming the test to reflect MemoryLevel will keep the suite self-describing and avoid confusion when MemoryType no longer exists.
| def test_memory_type_has_4_members(self) -> None: | |
| def test_memory_level_has_4_members(self) -> None: |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/decisions/ADR-001-memory-layer.md`:
- Around line 411-413: The ADR declares a property named supported_types but the
implementation exposes supported_categories; update the ADR to use the same
property name supported_categories (and keep the declared return type
frozenset[MemoryCategory]) so the design document matches the code (also scan
for any references in ADR-001-memory-layer.md to supported_types and rename them
to supported_categories).
In `@src/ai_company/config/schema.py`:
- Around line 540-543: The RootConfig currently instantiates a
CompanyMemoryConfig by default (the memory Field with
default_factory=CompanyMemoryConfig) but create_memory_backend() rejects the
only accepted backend ("mem0"), leaving the top-level config unbuildable; change
the default to make memory opt-in by either returning a disabled
CompanyMemoryConfig (e.g., default_factory that returns
CompanyMemoryConfig(enabled=False)) or by making the memory field Optional
(default None) so backend creation is deferred, and ensure
create_memory_backend() is only called when CompanyMemoryConfig indicates
enabled; reference CompanyMemoryConfig, the memory Field on RootConfig, and
create_memory_backend() when implementing the change.
In `@src/ai_company/memory/models.py`:
- Around line 173-176: MemoryQuery currently doesn't deduplicate tags while
MemoryMetadata does via _deduplicate_tags; add a validator on MemoryQuery.tags
that mirrors MemoryMetadata._deduplicate_tags to remove duplicate tags (preserve
order or enforce a deterministic order), and register it so it runs before the
existing _validate_time_range validator (or combine deduplication into the same
validator) to ensure consistent, deduplicated tag filters across models.
In `@tests/unit/core/test_enums.py`:
- Around line 51-52: Rename the test function to match the enum's current name:
change the test function name test_memory_type_has_4_members to
test_memory_level_has_4_members so it clearly references MemoryLevel; update any
test references/imports if present and ensure the test still asserts
len(MemoryLevel) == 4 in the test_enums module.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5094ff48-5819-4419-b719-739c947ee946
📒 Files selected for processing (30)
DESIGN_SPEC.mddocs/decisions/ADR-001-memory-layer.mdsrc/ai_company/config/defaults.pysrc/ai_company/config/schema.pysrc/ai_company/core/__init__.pysrc/ai_company/core/agent.pysrc/ai_company/core/enums.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/capabilities.pysrc/ai_company/memory/config.pysrc/ai_company/memory/errors.pysrc/ai_company/memory/factory.pysrc/ai_company/memory/models.pysrc/ai_company/memory/protocol.pysrc/ai_company/memory/shared.pysrc/ai_company/observability/events/memory.pytests/unit/config/conftest.pytests/unit/core/conftest.pytests/unit/core/test_agent.pytests/unit/core/test_enums.pytests/unit/memory/__init__.pytests/unit/memory/test_capabilities.pytests/unit/memory/test_config.pytests/unit/memory/test_errors.pytests/unit/memory/test_factory.pytests/unit/memory/test_init.pytests/unit/memory/test_models.pytests/unit/memory/test_protocol.pytests/unit/memory/test_shared.pytests/unit/observability/test_events.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Agent
- GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Use Python 3.14+ with PEP 649 native lazy annotations
Do NOT usefrom __future__ import annotations— Python 3.14 has PEP 649
Use PEP 758 except syntax: useexcept A, B:(no parentheses) — ruff enforces this on Python 3.14
Files:
tests/unit/memory/test_factory.pytests/unit/config/conftest.pysrc/ai_company/core/enums.pysrc/ai_company/memory/factory.pytests/unit/memory/test_capabilities.pysrc/ai_company/memory/capabilities.pysrc/ai_company/memory/protocol.pytests/unit/memory/test_shared.pysrc/ai_company/memory/models.pytests/unit/memory/test_init.pytests/unit/core/test_agent.pysrc/ai_company/config/defaults.pytests/unit/memory/test_protocol.pysrc/ai_company/config/schema.pytests/unit/memory/test_config.pysrc/ai_company/core/agent.pytests/unit/memory/test_models.pysrc/ai_company/memory/errors.pysrc/ai_company/observability/events/memory.pysrc/ai_company/core/__init__.pytests/unit/observability/test_events.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/config.pysrc/ai_company/memory/shared.pytests/unit/memory/test_errors.pytests/unit/core/conftest.pytests/unit/core/test_enums.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowto categorize tests
Configure asyncio_mode = 'auto' for pytest — no manual@pytest.mark.asyncioneeded
Set test timeout to 30 seconds per test
Prefer@pytest.mark.parametrizefor testing similar cases
Use generic test provider names (test-provider, test-small-001, etc.) in tests
Files:
tests/unit/memory/test_factory.pytests/unit/config/conftest.pytests/unit/memory/test_capabilities.pytests/unit/memory/test_shared.pytests/unit/memory/test_init.pytests/unit/core/test_agent.pytests/unit/memory/test_protocol.pytests/unit/memory/test_config.pytests/unit/memory/test_models.pytests/unit/observability/test_events.pytests/unit/memory/test_errors.pytests/unit/core/conftest.pytests/unit/core/test_enums.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Add type hints to all public functions and classes; mypy strict mode is enforced
Use Google-style docstrings required on public classes and functions (enforced by ruff D rules)
Create new objects instead of mutating existing ones (immutability); for non-Pydantic internal collections use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves — never mix static config fields with mutable runtime fields in one model
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use@computed_fieldfor derived values; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) — prefer structured concurrency over bare create_task
Enforce 88 character line length (ruff)
Keep functions to less than 50 lines and files to less than 800 lines
Handle errors explicitly, never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Files:
src/ai_company/core/enums.pysrc/ai_company/memory/factory.pysrc/ai_company/memory/capabilities.pysrc/ai_company/memory/protocol.pysrc/ai_company/memory/models.pysrc/ai_company/config/defaults.pysrc/ai_company/config/schema.pysrc/ai_company/core/agent.pysrc/ai_company/memory/errors.pysrc/ai_company/observability/events/memory.pysrc/ai_company/core/__init__.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/config.pysrc/ai_company/memory/shared.py
src/ai_company/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/ai_company/**/*.py: Every module with business logic MUST have: from ai_company.observability import get_logger then logger = get_logger(name)
Never useimport logging,logging.getLogger(), orprint()in application code — use the structured logger from ai_company.observability
Always use variable namelogger(not_logger, notlog) for the logger instance
Always use event name constants from ai_company.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider); import directly: from ai_company.observability.events. import EVENT_CONSTANT
Always use structured kwargs in logging: logger.info(EVENT, key=value) — never logger.info('msg %s', val)
All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Pure data models, enums, and re-exports do NOT need logging
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, or large/medium/small aliases
Files:
src/ai_company/core/enums.pysrc/ai_company/memory/factory.pysrc/ai_company/memory/capabilities.pysrc/ai_company/memory/protocol.pysrc/ai_company/memory/models.pysrc/ai_company/config/defaults.pysrc/ai_company/config/schema.pysrc/ai_company/core/agent.pysrc/ai_company/memory/errors.pysrc/ai_company/observability/events/memory.pysrc/ai_company/core/__init__.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/config.pysrc/ai_company/memory/shared.py
🧠 Learnings (9)
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/ai_company/**/*.py : Every module with business logic MUST have: from ai_company.observability import get_logger then logger = get_logger(__name__)
Applied to files:
src/ai_company/config/schema.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/ai_company/**/*.py : Never use `import logging`, `logging.getLogger()`, or `print()` in application code — use the structured logger from ai_company.observability
Applied to files:
src/ai_company/config/schema.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
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:
src/ai_company/config/schema.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
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: from ai_company.observability.events.<domain> import EVENT_CONSTANT
Applied to files:
src/ai_company/config/schema.pysrc/ai_company/observability/events/memory.pytests/unit/observability/test_events.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/ai_company/**/*.py : Pure data models, enums, and re-exports do NOT need logging
Applied to files:
src/ai_company/config/schema.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/ai_company/**/*.py : Always use variable name `logger` (not `_logger`, not `log`) for the logger instance
Applied to files:
src/ai_company/config/schema.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves — never mix static config fields with mutable runtime fields in one model
Applied to files:
src/ai_company/memory/config.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Always read DESIGN_SPEC.md before implementing any feature or planning any issue — the design spec is the starting point for architecture, data models, and behavior
Applied to files:
DESIGN_SPEC.md
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use computed_field for derived values; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
DESIGN_SPEC.md
🧬 Code graph analysis (20)
tests/unit/memory/test_factory.py (3)
src/ai_company/memory/config.py (1)
CompanyMemoryConfig(101-147)src/ai_company/memory/errors.py (1)
MemoryConfigError(33-34)src/ai_company/memory/factory.py (1)
create_memory_backend(16-49)
tests/unit/config/conftest.py (1)
src/ai_company/memory/config.py (1)
CompanyMemoryConfig(101-147)
src/ai_company/memory/factory.py (4)
src/ai_company/memory/config.py (1)
CompanyMemoryConfig(101-147)src/ai_company/memory/errors.py (1)
MemoryConfigError(33-34)src/ai_company/memory/protocol.py (1)
MemoryBackend(20-171)src/ai_company/observability/_logger.py (1)
get_logger(8-28)
tests/unit/memory/test_capabilities.py (2)
src/ai_company/core/enums.py (1)
MemoryCategory(104-111)src/ai_company/memory/capabilities.py (7)
MemoryCapabilities(13-56)supported_categories(29-31)supports_graph(34-36)supports_temporal(39-41)supports_vector_search(44-46)supports_shared_access(49-51)max_memories_per_agent(54-56)
src/ai_company/memory/protocol.py (3)
src/ai_company/core/enums.py (1)
MemoryCategory(104-111)src/ai_company/memory/models.py (3)
MemoryEntry(88-144)MemoryQuery(147-216)MemoryStoreRequest(61-85)tests/unit/memory/test_protocol.py (12)
connect(27-28)connect(93-93)disconnect(30-31)health_check(33-34)is_connected(37-38)is_connected(96-97)backend_name(41-42)store(44-57)retrieve(59-66)get(68-69)delete(71-76)count(78-87)
tests/unit/memory/test_shared.py (3)
src/ai_company/core/enums.py (1)
MemoryCategory(104-111)src/ai_company/memory/models.py (3)
MemoryEntry(88-144)MemoryQuery(147-216)MemoryStoreRequest(61-85)src/ai_company/memory/shared.py (4)
SharedKnowledgeStore(18-82)publish(26-43)search_shared(45-63)retract(65-82)
src/ai_company/memory/models.py (2)
src/ai_company/core/enums.py (1)
MemoryCategory(104-111)src/ai_company/observability/_logger.py (1)
get_logger(8-28)
tests/unit/memory/test_init.py (1)
tests/unit/core/test_enums.py (1)
test_all_exports_importable(384-388)
tests/unit/core/test_agent.py (2)
src/ai_company/core/enums.py (1)
MemoryLevel(91-101)src/ai_company/core/agent.py (1)
MemoryConfig(181-215)
tests/unit/memory/test_protocol.py (3)
src/ai_company/core/enums.py (1)
MemoryCategory(104-111)src/ai_company/memory/models.py (4)
MemoryEntry(88-144)MemoryMetadata(21-58)MemoryQuery(147-216)MemoryStoreRequest(61-85)src/ai_company/memory/protocol.py (11)
MemoryBackend(20-171)connect(31-38)disconnect(40-45)health_check(47-59)is_connected(62-64)backend_name(67-69)store(71-88)retrieve(90-110)get(112-132)delete(134-151)count(153-171)
src/ai_company/config/schema.py (1)
src/ai_company/memory/config.py (1)
CompanyMemoryConfig(101-147)
tests/unit/memory/test_config.py (2)
src/ai_company/core/enums.py (2)
ConsolidationInterval(114-120)MemoryLevel(91-101)src/ai_company/memory/config.py (3)
CompanyMemoryConfig(101-147)MemoryOptionsConfig(69-98)MemoryStorageConfig(23-66)
src/ai_company/core/agent.py (3)
src/ai_company/core/enums.py (4)
MemoryLevel(91-101)RiskTolerance(75-80)SeniorityLevel(6-21)ToolAccessLevel(264-281)src/ai_company/core/role.py (1)
Authority(33-61)src/ai_company/observability/_logger.py (1)
get_logger(8-28)
tests/unit/memory/test_models.py (2)
src/ai_company/core/enums.py (1)
MemoryCategory(104-111)src/ai_company/memory/models.py (4)
MemoryEntry(88-144)MemoryMetadata(21-58)MemoryQuery(147-216)MemoryStoreRequest(61-85)
src/ai_company/core/__init__.py (1)
src/ai_company/core/enums.py (3)
ConsolidationInterval(114-120)MemoryCategory(104-111)MemoryLevel(91-101)
src/ai_company/memory/__init__.py (7)
src/ai_company/memory/capabilities.py (1)
MemoryCapabilities(13-56)src/ai_company/memory/config.py (3)
CompanyMemoryConfig(101-147)MemoryOptionsConfig(69-98)MemoryStorageConfig(23-66)src/ai_company/memory/errors.py (7)
MemoryCapabilityError(37-38)MemoryConfigError(33-34)MemoryConnectionError(17-18)MemoryError(13-14)MemoryNotFoundError(29-30)MemoryRetrievalError(25-26)MemoryStoreError(21-22)src/ai_company/memory/factory.py (1)
create_memory_backend(16-49)src/ai_company/memory/models.py (4)
MemoryEntry(88-144)MemoryMetadata(21-58)MemoryQuery(147-216)MemoryStoreRequest(61-85)src/ai_company/memory/protocol.py (1)
MemoryBackend(20-171)src/ai_company/memory/shared.py (1)
SharedKnowledgeStore(18-82)
src/ai_company/memory/shared.py (3)
src/ai_company/memory/models.py (3)
MemoryEntry(88-144)MemoryQuery(147-216)MemoryStoreRequest(61-85)tests/unit/memory/test_shared.py (4)
publish(25-35)publish(59-60)search_shared(37-46)retract(48-53)src/ai_company/engine/parallel_models.py (1)
agent_id(79-81)
tests/unit/memory/test_errors.py (1)
src/ai_company/memory/errors.py (7)
MemoryCapabilityError(37-38)MemoryConfigError(33-34)MemoryConnectionError(17-18)MemoryError(13-14)MemoryNotFoundError(29-30)MemoryRetrievalError(25-26)MemoryStoreError(21-22)
tests/unit/core/conftest.py (1)
src/ai_company/core/enums.py (1)
MemoryLevel(91-101)
tests/unit/core/test_enums.py (1)
src/ai_company/core/enums.py (3)
ConsolidationInterval(114-120)MemoryCategory(104-111)MemoryLevel(91-101)
🔇 Additional comments (24)
src/ai_company/core/__init__.py (1)
34-41: LGTM!The new memory-related enum exports (
ConsolidationInterval,MemoryCategory,MemoryLevel) are correctly added to both the imports and__all__, maintaining alphabetical ordering. The removal ofMemoryTypeand replacement withMemoryLevelaligns with the design spec changes.Also applies to: 91-105
src/ai_company/core/enums.py (1)
91-121: LGTM!The new memory-related enums are well-defined:
MemoryLevelcorrectly replacesMemoryTypewith clear documentation of the "full" → "PERSISTENT" deviationMemoryCategorycovers the §7.2 memory type categoriesConsolidationIntervalprovides consolidation options per §7.3All enums follow the
StrEnumpattern consistent with the rest of the file and include appropriate docstrings.src/ai_company/memory/errors.py (1)
1-38: LGTM!Well-structured error hierarchy with:
- Clear documentation about shadowing the built-in
MemoryError- Appropriate
noqa: A001suppression with explanation- Granular subclasses enabling both specific and broad exception handling
- Concise docstrings for each exception type
src/ai_company/observability/events/memory.py (1)
1-47: LGTM!Comprehensive memory event constants following the established
memory.<entity>.<action>naming convention. The constants are well-organized by category (backend lifecycle, entry operations, shared knowledge, capability checks) and properly typed withFinal[str].tests/unit/observability/test_events.py (1)
179-179: LGTM!Comprehensive test coverage for memory events:
- Domain discovery test correctly updated to include
"memory"- Parametrized test covers all 27 memory event constants
- Dynamic import approach is consistent with the persistence events test pattern
Also applies to: 371-405
src/ai_company/core/agent.py (3)
16-26: LGTM!Proper integration of observability:
- Logger initialized with
get_logger(__name__)following coding guidelinesCONFIG_VALIDATION_FAILEDevent constant imported from the correct domain module
191-215: LGTM!Clean migration from
MemoryTypetoMemoryLevelwith proper validation logging:
- Structured kwargs provide full context (
model,field,memory_type,retention_days,reason)- Warning logged before raising
ValueErrorper coding guidelines
243-262: LGTM!
ToolPermissionsvalidation updated with structured logging that provides useful context (model,field,overlap,reason) before raising the validation error.src/ai_company/config/defaults.py (1)
33-33: LGTM!The
"memory": {}default placeholder is correctly positioned and follows the pattern of other optional configuration sections.tests/unit/memory/test_init.py (2)
10-14: LGTM!The
test_all_exports_importabletest follows the established pattern fromtests/unit/core/test_enums.pyand ensures all names in__all__are actually importable from the package.
16-17: The export count assertion is correct—the memory module__all__contains exactly 18 items.tests/unit/memory/test_factory.py (1)
1-23: LGTM!Test structure follows guidelines:
@pytest.mark.unitmarker, 30-second timeout, and proper imports. The test correctly expectsValidationErrorsince Pydantic wrapsValueErrorraised frommodel_validatorinto aValidationError.tests/unit/config/conftest.py (1)
23-23: LGTM!The
CompanyMemoryConfigimport and factory field addition follow the established patterns for other config components inRootConfigFactory.Also applies to: 78-78
src/ai_company/memory/capabilities.py (1)
1-56: LGTM!Clean protocol definition with proper
@runtime_checkabledecorator, type hints on all properties, and Google-style docstrings. The# noqa: TC001is correct sinceMemoryCategoryis used at runtime in thefrozensetgeneric.tests/unit/core/test_enums.py (1)
99-103: LGTM!New tests for
MemoryCategoryandConsolidationIntervalenums follow the established patterns: member count verification and parametrized value assertions. Good coverage of the new enum types.Also applies to: 232-257
tests/unit/core/test_agent.py (1)
24-24: LGTM!All
MemoryTypereferences correctly updated toMemoryLevel. The test assertions align with the new enum members (SESSION,PERSISTENT,NONE) and maintain proper coverage ofMemoryConfigvalidation logic.Also applies to: 372-372, 377-378, 394-394, 398-398, 405-405, 655-655
tests/unit/memory/test_capabilities.py (1)
1-116: LGTM!Excellent test coverage for protocol compliance:
- Positive tests verify compliant implementations pass
isinstancechecks- Negative tests confirm incomplete implementations and plain objects fail
- Variant tests cover edge cases like
Nonefor unlimited memoriesThe test doubles (
_FakeMemoryCapabilities,_FakeUnlimitedCapabilities,_IncompleteCapabilities) are minimal and purpose-built.tests/unit/core/conftest.py (1)
35-35: LGTM!Factory configuration correctly updated to use
MemoryLevel.SESSION, consistent with theMemoryConfigmodel's default value defined insrc/ai_company/core/agent.py.Also applies to: 85-85
src/ai_company/memory/protocol.py (1)
1-171: LGTM!The
MemoryBackendprotocol is well-designed with comprehensive docstrings, proper type hints usingNotBlankStrfor identifiers, and clear error semantics documented for each method. The use of@runtime_checkableenables structural subtyping checks at runtime.src/ai_company/memory/models.py (1)
1-216: LGTM overall!The memory models are well-structured with:
- Proper frozen configuration with
allow_inf_nan=Falsefor numeric safety- Consistent use of
NotBlankStrfor identifiers- Good validation patterns with structured logging before raising errors
- Appropriate bounds on numeric fields (
confidence,relevance_score,min_relevance,limit)src/ai_company/memory/config.py (1)
1-147: LGTM!The configuration models are well-designed:
- Path traversal prevention in
MemoryStorageConfighandles both Windows and POSIX path formatsMemoryOptionsConfigproperly usesallow_inf_nan=Falsefor its numeric fields- Backend validation in
CompanyMemoryConfiguses aClassVarfrozenset for the valid backends list, enabling easy extension- All validators log structured warnings with context before raising
ValueErrorThe use of frozen Pydantic models for config aligns with the project's immutability conventions. Based on learnings: "Use frozen Pydantic models for config/identity."
src/ai_company/memory/__init__.py (1)
1-52: LGTM!Clean package re-export with:
- All public symbols properly imported and listed in
__all__- Alphabetically sorted
__all__for maintainability- Appropriate
noqa: A004comment for theMemoryErrorshadow (intentional domain-specific naming)DESIGN_SPEC.md (2)
1335-1439: LGTM!The new §7.5 Memory Backend Protocol section is comprehensive and accurately documents:
- The three protocols (
MemoryBackend,MemoryCapabilities,SharedKnowledgeStore)- The enum definitions matching the code
- The error hierarchy with clear "When Raised" descriptions
- Configuration structure that aligns with the Pydantic models
The documentation properly references the related design sections (§7.2-7.4) and the ADR-001 decision record.
82-85: Implementation snapshot is accurate.The snapshot correctly reflects M5 progress with the memory interface protocol complete and Mem0 adapter pending.
| @property | ||
| def supported_types(self) -> frozenset[MemoryType]: ... | ||
| def supported_types(self) -> frozenset[MemoryCategory]: ... | ||
| @property |
There was a problem hiding this comment.
Property name mismatch between ADR and implementation.
The ADR shows supported_types but src/ai_company/memory/capabilities.py (line 29) defines supported_categories. Update the ADR to match the actual implementation.
📝 Suggested fix
`@property`
- def supported_types(self) -> frozenset[MemoryCategory]: ...
+ def supported_categories(self) -> frozenset[MemoryCategory]: ...📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @property | |
| def supported_types(self) -> frozenset[MemoryType]: ... | |
| def supported_types(self) -> frozenset[MemoryCategory]: ... | |
| @property | |
| `@property` | |
| def supported_categories(self) -> frozenset[MemoryCategory]: ... | |
| `@property` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/decisions/ADR-001-memory-layer.md` around lines 411 - 413, The ADR
declares a property named supported_types but the implementation exposes
supported_categories; update the ADR to use the same property name
supported_categories (and keep the declared return type
frozenset[MemoryCategory]) so the design document matches the code (also scan
for any references in ADR-001-memory-layer.md to supported_types and rename them
to supported_categories).
| memory: CompanyMemoryConfig = Field( | ||
| default_factory=CompanyMemoryConfig, | ||
| description="Memory backend configuration", | ||
| ) |
There was a problem hiding this comment.
Don't default every root config to an unbuildable backend.
RootConfig now eagerly creates CompanyMemoryConfig(), but the only accepted backend is "mem0" and create_memory_backend() currently raises for that value. That leaves the top-level default config in an "enabled" state that cannot actually be instantiated. Please make memory opt-in/disabled by default, or defer backend creation until the adapter lands.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/ai_company/config/schema.py` around lines 540 - 543, The RootConfig
currently instantiates a CompanyMemoryConfig by default (the memory Field with
default_factory=CompanyMemoryConfig) but create_memory_backend() rejects the
only accepted backend ("mem0"), leaving the top-level config unbuildable; change
the default to make memory opt-in by either returning a disabled
CompanyMemoryConfig (e.g., default_factory that returns
CompanyMemoryConfig(enabled=False)) or by making the memory field Optional
(default None) so backend creation is deferred, and ensure
create_memory_backend() is only called when CompanyMemoryConfig indicates
enabled; reference CompanyMemoryConfig, the memory Field on RootConfig, and
create_memory_backend() when implementing the change.
… agents - Add MEMORY_BACKEND_NOT_IMPLEMENTED event constant, use it for mem0 stub - Add expires_at >= created_at validation to MemoryEntry - Add tag deduplication to MemoryQuery for consistency with MemoryMetadata - Use dict.fromkeys() for more idiomatic tag dedup in MemoryMetadata - Fix docstring: "before" → "strictly before" in MemoryQuery time range - Update health_check() docstring to mandate logging before returning False - Add MemoryConnectionError to all CRUD method Raises sections - Add class-level connection requirement note to MemoryBackend - Fix ADR-001 supported_types → supported_categories, add missing properties - Update README to reflect completed memory protocol interface - Add default annotations to DESIGN_SPEC yaml examples - Remove outdated "full" deviation comment from MemoryLevel - Fix __init__.py module docstring to include domain models - Rename test_memory_type → test_memory_level in test_enums.py - Add 7 new tests (expires_at validation + query tag dedup)
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/decisions/ADR-001-memory-layer.md`:
- Around line 412-422: Update the ADR snippet for the MemoryBackend example to
match the actual synchronous protocol in src/ai_company/memory/protocol.py:
replace the old async store/retrieve/delete/list_memories docs with the real
synchronous API and include the methods and properties present on the protocol
(connect, disconnect, health_check, get, count, backend_name, is_connected) and
keep the updated capability properties (supported_categories, supports_graph,
supports_temporal, supports_vector_search, supports_shared_access,
max_memories_per_agent) so the ADR documents the exact contract exposed by the
MemoryBackend.
In `@src/ai_company/core/enums.py`:
- Around line 91-97: MemoryLevel now renames the serialized value
"full"→"persistent", which breaks existing configs; add a backward-compatible
alias and parsing acceptance: add a deprecated enum member (e.g.,
MemoryLevel.FULL = "full") and/or a compatibility constant alias MemoryType =
MemoryLevel, and update the config parsing logic that reads memory levels to map
the legacy string "full" to MemoryLevel.PERSISTENT (or accept both values) so
existing YAMLs and imports continue to work while emitting a deprecation notice.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4beb38e8-56ff-4ec8-bfcd-0966f68ac5e9
📒 Files selected for processing (12)
DESIGN_SPEC.mdREADME.mddocs/decisions/ADR-001-memory-layer.mdsrc/ai_company/core/enums.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/factory.pysrc/ai_company/memory/models.pysrc/ai_company/memory/protocol.pysrc/ai_company/observability/events/memory.pytests/unit/core/test_enums.pytests/unit/memory/test_models.pytests/unit/observability/test_events.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Use Python 3.14+ with PEP 649 native lazy annotations
Do NOT usefrom __future__ import annotations— Python 3.14 has PEP 649
Use PEP 758 except syntax: useexcept A, B:(no parentheses) — ruff enforces this on Python 3.14
Files:
src/ai_company/memory/models.pysrc/ai_company/memory/factory.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/protocol.pytests/unit/core/test_enums.pysrc/ai_company/observability/events/memory.pytests/unit/observability/test_events.pytests/unit/memory/test_models.pysrc/ai_company/core/enums.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Add type hints to all public functions and classes; mypy strict mode is enforced
Use Google-style docstrings required on public classes and functions (enforced by ruff D rules)
Create new objects instead of mutating existing ones (immutability); for non-Pydantic internal collections use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves — never mix static config fields with mutable runtime fields in one model
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use@computed_fieldfor derived values; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) — prefer structured concurrency over bare create_task
Enforce 88 character line length (ruff)
Keep functions to less than 50 lines and files to less than 800 lines
Handle errors explicitly, never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Files:
src/ai_company/memory/models.pysrc/ai_company/memory/factory.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/protocol.pysrc/ai_company/observability/events/memory.pysrc/ai_company/core/enums.py
src/ai_company/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/ai_company/**/*.py: Every module with business logic MUST have: from ai_company.observability import get_logger then logger = get_logger(name)
Never useimport logging,logging.getLogger(), orprint()in application code — use the structured logger from ai_company.observability
Always use variable namelogger(not_logger, notlog) for the logger instance
Always use event name constants from ai_company.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider); import directly: from ai_company.observability.events. import EVENT_CONSTANT
Always use structured kwargs in logging: logger.info(EVENT, key=value) — never logger.info('msg %s', val)
All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Pure data models, enums, and re-exports do NOT need logging
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, or large/medium/small aliases
Files:
src/ai_company/memory/models.pysrc/ai_company/memory/factory.pysrc/ai_company/memory/__init__.pysrc/ai_company/memory/protocol.pysrc/ai_company/observability/events/memory.pysrc/ai_company/core/enums.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowto categorize tests
Configure asyncio_mode = 'auto' for pytest — no manual@pytest.mark.asyncioneeded
Set test timeout to 30 seconds per test
Prefer@pytest.mark.parametrizefor testing similar cases
Use generic test provider names (test-provider, test-small-001, etc.) in tests
Files:
tests/unit/core/test_enums.pytests/unit/observability/test_events.pytests/unit/memory/test_models.py
🧠 Learnings (4)
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves — never mix static config fields with mutable runtime fields in one model
Applied to files:
src/ai_company/memory/models.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
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: from ai_company.observability.events.<domain> import EVENT_CONSTANT
Applied to files:
src/ai_company/observability/events/memory.pytests/unit/observability/test_events.py
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Always read DESIGN_SPEC.md before implementing any feature or planning any issue — the design spec is the starting point for architecture, data models, and behavior
Applied to files:
DESIGN_SPEC.md
📚 Learning: 2026-03-09T06:51:01.916Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-09T06:51:01.916Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use computed_field for derived values; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
DESIGN_SPEC.md
🧬 Code graph analysis (5)
src/ai_company/memory/models.py (2)
src/ai_company/core/enums.py (1)
MemoryCategory(100-107)src/ai_company/observability/_logger.py (1)
get_logger(8-28)
src/ai_company/memory/factory.py (4)
src/ai_company/memory/config.py (1)
CompanyMemoryConfig(101-147)src/ai_company/memory/errors.py (1)
MemoryConfigError(33-34)src/ai_company/memory/protocol.py (1)
MemoryBackend(20-182)src/ai_company/observability/_logger.py (1)
get_logger(8-28)
src/ai_company/memory/__init__.py (7)
src/ai_company/memory/capabilities.py (1)
MemoryCapabilities(13-56)src/ai_company/memory/config.py (3)
CompanyMemoryConfig(101-147)MemoryOptionsConfig(69-98)MemoryStorageConfig(23-66)src/ai_company/memory/errors.py (7)
MemoryCapabilityError(37-38)MemoryConfigError(33-34)MemoryConnectionError(17-18)MemoryError(13-14)MemoryNotFoundError(29-30)MemoryRetrievalError(25-26)MemoryStoreError(21-22)src/ai_company/memory/factory.py (1)
create_memory_backend(19-52)src/ai_company/memory/models.py (4)
MemoryEntry(83-151)MemoryMetadata(21-53)MemoryQuery(154-231)MemoryStoreRequest(56-80)src/ai_company/memory/protocol.py (1)
MemoryBackend(20-182)src/ai_company/memory/shared.py (1)
SharedKnowledgeStore(18-82)
src/ai_company/memory/protocol.py (2)
src/ai_company/core/enums.py (1)
MemoryCategory(100-107)src/ai_company/memory/models.py (3)
MemoryEntry(83-151)MemoryQuery(154-231)MemoryStoreRequest(56-80)
tests/unit/core/test_enums.py (1)
src/ai_company/core/enums.py (3)
ConsolidationInterval(110-116)MemoryCategory(100-107)MemoryLevel(91-97)
🪛 LanguageTool
README.md
[typographical] ~24-~24: To join two clauses or introduce examples, consider using an em dash.
Context: ...a migrations - Memory Interface (M5) - Pluggable MemoryBackend protocol with ...
(DASH_RULE)
[typographical] ~28-~28: To join two clauses or introduce examples, consider using an em dash.
Context: ... milestones) - Memory Backends (M5) - Mem0 adapter ([ADR-001](docs/decisions/A...
(DASH_RULE)
🔇 Additional comments (5)
src/ai_company/memory/protocol.py (1)
1-182: LGTM!The
MemoryBackendprotocol is well-designed with clear separation between lifecycle management (connect,disconnect,health_check) and CRUD operations (store,retrieve,get,delete,count). The docstrings thoroughly document error semantics and thehealth_checknote appropriately specifies logging requirements for implementations. Type hints usingNotBlankStrfor identifiers align with project conventions.src/ai_company/memory/models.py (1)
1-231: LGTM!The memory domain models are well-implemented with frozen Pydantic models, appropriate validation (timestamp ordering, tag deduplication, NaN rejection), and structured logging on validation failures. The
_deduplicate_tagsvalidator onMemoryQuery(lines 205-211) addresses the previous review feedback for consistency withMemoryMetadata.src/ai_company/memory/__init__.py (1)
1-53: LGTM!Clean re-export surface with all 18 public symbols properly imported and listed in
__all__. Thenoqa: A004comment on line 19 appropriately acknowledges the intentional shadowing of the built-inMemoryError.DESIGN_SPEC.md (2)
1335-1439: LGTM!The new §7.5 Memory Backend Protocol section comprehensively documents the memory subsystem architecture: enums,
MemoryBackendprotocol with lifecycle and CRUD methods,MemoryCapabilitiesprotocol for runtime discovery,SharedKnowledgeStoreprotocol for cross-agent operations, error hierarchy, and configuration models. The protocol signatures and error semantics align with the implementation insrc/ai_company/memory/.
2732-2741: LGTM!The project structure listing correctly reflects all new memory subsystem files:
__init__.py,capabilities.py,config.py,errors.py,factory.py,models.py,protocol.py, andshared.py.
| class MemoryLevel(StrEnum): | ||
| """Memory persistence level for an agent (§7.3).""" | ||
|
|
||
| PERSISTENT = "persistent" | ||
| PROJECT = "project" | ||
| SESSION = "session" | ||
| NONE = "none" |
There was a problem hiding this comment.
Add a compatibility path for the renamed memory level.
MemoryLevel is config-facing, so changing the serialized value from "full" to "persistent" and dropping MemoryType will break existing YAML and imports. This PR still has level: "full" examples in docs/decisions/ADR-001-memory-layer.md, so please either accept the legacy spelling during parsing and keep a deprecated alias, or treat this as an explicit breaking change with migration notes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/ai_company/core/enums.py` around lines 91 - 97, MemoryLevel now renames
the serialized value "full"→"persistent", which breaks existing configs; add a
backward-compatible alias and parsing acceptance: add a deprecated enum member
(e.g., MemoryLevel.FULL = "full") and/or a compatibility constant alias
MemoryType = MemoryLevel, and update the config parsing logic that reads memory
levels to map the legacy string "full" to MemoryLevel.PERSISTENT (or accept both
values) so existing YAMLs and imports continue to work while emitting a
deprecation notice.
…1 protocol - Replace datetime with AwareDatetime on all memory model timestamp fields to prevent TypeError from naive/aware mixing (Pydantic rejects naive at field validation before model validators run) - Add MEMORY_MODEL_INVALID event constant for runtime domain model validation failures — replaces CONFIG_VALIDATION_FAILED which is scoped to config models - Update ADR-001 MemoryBackend snippet to match the shipped protocol (lifecycle methods, properties, correct parameter types) - Add 4 new tests (naive datetime rejection + query naive since)
| class MemoryNotFoundError(MemoryError): | ||
| """Raised when a specific memory ID is not found.""" |
There was a problem hiding this comment.
MemoryNotFoundError is never raised by the protocol
MemoryNotFoundError is documented as "Raised when a specific memory ID is not found," yet the MemoryBackend protocol explicitly documents the opposite contract:
get(): "ReturnsNonewhen the entry does not exist —MemoryNotFoundErroris never raised by this method" (protocol.pyline 127–128)delete(): "ReturnsTrueif the entry was deleted,Falseif not found" (not raised)
This creates a direct contradiction for backend implementors: the error's docstring says to raise it, but the protocol says not to. Backend authors reading errors.py in isolation will raise MemoryNotFoundError from get(), breaking callers that expect None.
Either:
- Update the error's docstring to clarify the specific scenarios where backends should raise it (if any exist outside the base protocol, e.g. in a hypothetical strict-mode variant), or
- If
MemoryNotFoundErroris reserved for future use or non-protocol extension points, add a note explaining its intended scope.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ai_company/memory/errors.py
Line: 29-30
Comment:
**`MemoryNotFoundError` is never raised by the protocol**
`MemoryNotFoundError` is documented as "Raised when a specific memory ID is not found," yet the `MemoryBackend` protocol explicitly documents the opposite contract:
- `get()`: "Returns `None` when the entry does not exist — `MemoryNotFoundError` is **never** raised by this method" (`protocol.py` line 127–128)
- `delete()`: "Returns `True` if the entry was deleted, `False` if not found" (not raised)
This creates a direct contradiction for backend implementors: the error's docstring says to raise it, but the protocol says not to. Backend authors reading `errors.py` in isolation will raise `MemoryNotFoundError` from `get()`, breaking callers that expect `None`.
Either:
- Update the error's docstring to clarify the specific scenarios where backends *should* raise it (if any exist outside the base protocol, e.g. in a hypothetical strict-mode variant), or
- If `MemoryNotFoundError` is reserved for future use or non-protocol extension points, add a note explaining its intended scope.
How can I resolve this? If you propose a fix, please make it concise.| def backend_name(self) -> str: | ||
| """Human-readable backend identifier (e.g. ``"mem0"``).""" | ||
| ... |
There was a problem hiding this comment.
backend_name typed as str instead of NotBlankStr
The codebase convention (CLAUDE.md) says "use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple variants — instead of manual whitespace validators." backend_name is a human-readable backend identifier, so a blank or whitespace-only value would be semantically invalid, yet the current type allows it.
| def backend_name(self) -> str: | |
| """Human-readable backend identifier (e.g. ``"mem0"``).""" | |
| ... | |
| @property | |
| def backend_name(self) -> "NotBlankStr": | |
| """Human-readable backend identifier (e.g. ``"mem0"``).""" | |
| ... |
You'll also need to add NotBlankStr to the imports at the top of the file (it's already used elsewhere in the file, just not for this property).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ai_company/memory/protocol.py
Line: 73-75
Comment:
**`backend_name` typed as `str` instead of `NotBlankStr`**
The codebase convention (CLAUDE.md) says "use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple variants — instead of manual whitespace validators." `backend_name` is a human-readable backend identifier, so a blank or whitespace-only value would be semantically invalid, yet the current type allows it.
```suggestion
@property
def backend_name(self) -> "NotBlankStr":
"""Human-readable backend identifier (e.g. ``"mem0"``)."""
...
```
You'll also need to add `NotBlankStr` to the imports at the top of the file (it's already used elsewhere in the file, just not for this property).
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| _VALID_BACKENDS: ClassVar[frozenset[str]] = frozenset({"mem0"}) | ||
|
|
||
| backend: NotBlankStr = Field( | ||
| default="mem0", | ||
| description="Memory backend name", | ||
| ) |
There was a problem hiding this comment.
Vendor name "mem0" used directly in production source code
Per the project's code conventions (CLAUDE.md), vendor names should only appear in DESIGN_SPEC.md provider lists, .claude/ skill files, or third-party import paths — not in project-owned source code, default field values, or tests. The literal string "mem0" appears as a hardcoded value across several changed files:
src/ai_company/memory/config.py:113—_VALID_BACKENDS = frozenset({"mem0"})src/ai_company/memory/config.py:116—default="mem0"field defaultsrc/ai_company/memory/factory.py:38—if config.backend == "mem0":src/ai_company/memory/factory.py:43—backend="mem0"log kwargtests/unit/memory/test_factory.py:16—CompanyMemoryConfig(backend="mem0")tests/unit/memory/test_config.py:138,144,165,175—backend="mem0"usage
Consider using a symbolic constant (e.g., MEM0_BACKEND_NAME: Final[str] = "mem0" defined once in the memory package) so the raw vendor string is isolated to a single declaration point, and all other references use the constant.
Rule Used: CLAUDE.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ai_company/memory/config.py
Line: 113-118
Comment:
**Vendor name `"mem0"` used directly in production source code**
Per the project's code conventions (CLAUDE.md), vendor names should only appear in DESIGN_SPEC.md provider lists, `.claude/` skill files, or third-party import paths — not in project-owned source code, default field values, or tests. The literal string `"mem0"` appears as a hardcoded value across several changed files:
- `src/ai_company/memory/config.py:113` — `_VALID_BACKENDS = frozenset({"mem0"})`
- `src/ai_company/memory/config.py:116` — `default="mem0"` field default
- `src/ai_company/memory/factory.py:38` — `if config.backend == "mem0":`
- `src/ai_company/memory/factory.py:43` — `backend="mem0"` log kwarg
- `tests/unit/memory/test_factory.py:16` — `CompanyMemoryConfig(backend="mem0")`
- `tests/unit/memory/test_config.py:138`, `144`, `165`, `175` — `backend="mem0"` usage
Consider using a symbolic constant (e.g., `MEM0_BACKEND_NAME: Final[str] = "mem0"` defined once in the memory package) so the raw vendor string is isolated to a single declaration point, and all other references use the constant.
**Rule Used:** CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=6816cd03-d0e1-4fd0-9d04-2417487a584c))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| vector_store: NotBlankStr = Field( | ||
| default="qdrant", | ||
| description="Vector store backend name", | ||
| ) | ||
| history_store: NotBlankStr = Field( | ||
| default="sqlite", | ||
| description="History store backend name", | ||
| ) |
There was a problem hiding this comment.
vector_store and history_store accept any non-blank string with no validation
Both fields accept arbitrary strings (NotBlankStr), which means values like "invalid-store", "postgres", or typos like "sqlit" will pass config validation and only fail at runtime when the backend attempts to initialize. Since the set of valid store backends is small and known at this stage ("qdrant" for vector, "sqlite" for history per the design spec), using a Literal type or an enum would give config-time errors with clear messages, consistent with how backend and level are constrained.
If open extensibility is intentional (to allow future store backends without code changes), a comment explaining this design choice would prevent future reviewers from treating it as an oversight.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ai_company/memory/config.py
Line: 42-49
Comment:
**`vector_store` and `history_store` accept any non-blank string with no validation**
Both fields accept arbitrary strings (`NotBlankStr`), which means values like `"invalid-store"`, `"postgres"`, or typos like `"sqlit"` will pass config validation and only fail at runtime when the backend attempts to initialize. Since the set of valid store backends is small and known at this stage (`"qdrant"` for vector, `"sqlite"` for history per the design spec), using a `Literal` type or an enum would give config-time errors with clear messages, consistent with how `backend` and `level` are constrained.
If open extensibility is intentional (to allow future store backends without code changes), a comment explaining this design choice would prevent future reviewers from treating it as an oversight.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 31 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "/data/sub/../../../etc", | ||
| "/data/..", | ||
| "..", | ||
| "data/../secret", |
There was a problem hiding this comment.
Path traversal validation in MemoryStorageConfig is intended to handle Windows paths too (PureWindowsPath is used in the validator), but the parametrized bad_path cases only cover POSIX-style separators. Add a couple of Windows-style traversal examples (e.g. drive-letter and backslash variants) to ensure the validator behaves correctly on Windows inputs.
| "data/../secret", | |
| "data/../secret", | |
| r"C:\data\..\secret", | |
| r"..\secret", |
| 5. [Communication Architecture](#5-communication-architecture) — 5.6 Conflict Resolution, 5.7 Meeting Protocol | ||
| 6. [Task & Workflow Engine](#6-task--workflow-engine) — 6.5 Execution Loop, 6.6 Crash Recovery, **6.7 Graceful Shutdown**, **6.8 Workspace Isolation**, **6.9 Task Decomposability & Coordination Topology** | ||
| 7. [Memory & Persistence](#7-memory--persistence) — 7.4 Shared Org Memory (Research Directions), **7.5 Operational Data Persistence** | ||
| 7. [Memory & Persistence](#7-memory--persistence) — **7.5 Memory Backend Protocol**, 7.4 Shared Org Memory (Research Directions), **7.6 Operational Data Persistence** |
There was a problem hiding this comment.
The Table of Contents entry for section 7 lists 7.5 before 7.4; this is out of numeric order and doesn’t match the section numbering below. Please reorder the referenced subsections (7.4, 7.5, 7.6) so the TOC reflects the actual structure.
| 7. [Memory & Persistence](#7-memory--persistence) — **7.5 Memory Backend Protocol**, 7.4 Shared Org Memory (Research Directions), **7.6 Operational Data Persistence** | |
| 7. [Memory & Persistence](#7-memory--persistence) — 7.4 Shared Org Memory (Research Directions), **7.5 Memory Backend Protocol**, **7.6 Operational Data Persistence** |
| # ── Backend lifecycle ────────────────────────────────────────────── | ||
|
|
||
| MEMORY_BACKEND_CONNECTING: Final[str] = "memory.backend.connecting" | ||
| MEMORY_BACKEND_CONNECTED: Final[str] = "memory.backend.connected" | ||
| MEMORY_BACKEND_CONNECTION_FAILED: Final[str] = "memory.backend.connection_failed" |
There was a problem hiding this comment.
PR description says 21 MEMORY_* event constants were added, but this module currently defines 28. Please either update the PR description/test plan to match the implemented set, or trim/merge constants so the count aligns with what’s documented.
🤖 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
MemoryBackendprotocol with lifecycle management (connect/disconnect/health_check) and CRUD operations (store/retrieve/get/delete/count)MemoryCapabilitiesprotocol for runtime capability discovery andSharedKnowledgeStoreprotocol for cross-agent shared knowledgeMemoryEntry,MemoryMetadata,MemoryStoreRequest,MemoryQuerywith full validation (bounds, NaN rejection, tag dedup, timestamp ordering, time range)CompanyMemoryConfig,MemoryStorageConfig,MemoryOptionsConfigwith path traversal prevention and backend validationMemoryCategory,MemoryLevel,ConsolidationIntervalenums tocore/enums.pyMemoryErrorbase with 6 specific subclassescreate_memory_backend()factory (placeholder for Mem0 adapter Implement memory retrieval, ranking, and context injection #41)MEMORY_*structured logging event constantsMemoryConfigtoAgentIdentityandCompanyMemoryConfigtoRootConfigagent.py,models.py,schema.pyvalidatorsCloses #32
Test plan
isinstancechecks)__all__re-export importability testReview coverage
Pre-reviewed by 10 agents (code-reviewer, python-reviewer, pr-test-analyzer, silent-failure-hunter, comment-analyzer, type-design-analyzer, logging-audit, resilience-audit, security-reviewer, docs-consistency). 34 findings identified and addressed.
🤖 Generated with Claude Code