Skip to content

feat: token-based personality trimming via PromptProfile.max_personality_tokens#1059

Merged
Aureliolo merged 10 commits intomainfrom
feat/personality-trimming
Apr 4, 2026
Merged

feat: token-based personality trimming via PromptProfile.max_personality_tokens#1059
Aureliolo merged 10 commits intomainfrom
feat/personality-trimming

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

Activate the previously reserved max_personality_tokens field as a hard cap on the personality section's token count. When the personality section exceeds the profile's budget, progressive trimming applies:

  1. Tier 1 -- Drop enums: override mode to "condensed" (removes behavioral enum fields)
  2. Tier 2 -- Truncate description: shorten personality_description at word boundary with "..." suffix
  3. Tier 3 -- Minimal fallback: override mode to "minimal" (communication_style only)

Token trimming is secondary to personality_mode -- the mode selects which fields are included, the token limit enforces a hard cap.

Changes

Core trimming engine

  • Add _estimate_personality_tokens(), _try_condensed(), _try_truncate_description(), _truncate_description() helpers in engine/_prompt_helpers.py
  • Add _trim_personality() orchestrator with progressive 3-tier trimming
  • Add PersonalityTrimInfo model with cross-field model_validator and @computed_field budget_met
  • build_core_context() returns tuple[dict, PersonalityTrimInfo | None] -- trimming applied automatically

Prompt layer integration

  • SystemPrompt.personality_trim_info field exposes trimming metadata to callers
  • build_system_prompt() new params: personality_trimming_enabled (master switch), max_personality_tokens_override (runtime override)
  • _trim_sections() skips redundant personality trimming on re-renders (perf)

Runtime settings

  • New ENGINE settings namespace with 3 settings:
    • personality_trimming_enabled (bool, default: true)
    • personality_trimming_notify (bool, default: true)
    • personality_max_tokens_override (int, default: 0 = use profile defaults)

Notifications & observability

  • PERSONALITY_TRIMMED WebSocket event type for dashboard notifications
  • PROMPT_PERSONALITY_TRIMMED observability event constant
  • Engine logs at INFO with agent context; helpers log at DEBUG

Documentation

  • Updated docs/design/engine.md: profiles table with Max Personality Tokens column, new Personality Trimming subsection, pipeline step 3, invariants
  • Updated CLAUDE.md package structure
  • Updated PromptProfile.max_personality_tokens docstring

Test plan

  • 48 new tests across 2 files (29 trimming + 19 review-driven additions)
  • TestEstimatePersonalityTokens (4): full > condensed > minimal estimation
  • TestTrimPersonality (8): all 3 tiers, edge cases (empty desc, word boundary, already minimal)
  • TestTrimPersonalityLogging (2): event presence/absence
  • TestTierLimits (3): small=80, medium=200, large=500 token caps
  • TestBuildSystemPromptIntegration (7): end-to-end, disable, override, negative override
  • TestTruncateDescription (8): parametrized edge cases + property test
  • TestPersonalityTrimInfoValidation (6): field constraints + cross-field invariants
  • TestAdditionalEdgeCases (4): condensed direct entry, tier2->tier3, disabled+profile, WS enum
  • TestEngineSettingDefinitions (5): namespace, types, defaults, count
  • Full suite: 13553 passed, 9 skipped

Review coverage

Pre-reviewed by 6 agents (docs-consistency, code-reviewer, type-design-analyzer, test-analyzer, conventions-enforcer, issue-resolution-verifier). 19 findings addressed.

Closes #1045

Copilot AI review requested due to automatic review settings April 4, 2026 01:16
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

Walkthrough

Implements token-based personality trimming in the prompt pipeline: adds a frozen Pydantic PersonalityTrimInfo model, token-estimation helpers, and a 3-tier progressive trimming flow that can force condensed, truncate personality_description, or fall back to minimal to enforce PromptProfile.max_personality_tokens. Updates build_core_context and rendering APIs to accept trimming flags and return trim info, surfaces personality_trim_info on SystemPrompt, emits a prompt.personality.trimmed event, adds ENGINE settings and namespace entries with runtime overrides, updates docs, and adds unit tests.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature: token-based personality trimming activated via PromptProfile.max_personality_tokens, which is the core objective of this PR.
Description check ✅ Passed The description provides a comprehensive overview of the feature implementation, including the 3-tier trimming strategy, core engine changes, settings integration, and test coverage, all related to the changeset.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #1045: token estimation for personality section, progressive 3-tier trimming logic, PersonalityTrimInfo model, SystemPrompt integration, observability logging, unit tests covering all tiers, and updated PromptProfile documentation. All deliverables are addressed.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing personality token trimming or supporting the feature (settings definitions, observability events, test coverage, documentation). No unrelated code modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 97.06% which is sufficient. The required threshold is 40.00%.

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


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

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 4, 2026 01:18 — with GitHub Actions Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

Dependency Review

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

Snapshot Warnings

⚠️: No snapshots were found for the head SHA c662162.
Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.

Scanned Files

None

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request implements a three-tier progressive personality trimming system to manage token budgets for system prompts, including new settings and observability metadata. Feedback identifies a logic error where personality trimming is reverted during iterative section-level trimming, a bug in the description truncation function that adds unnecessary ellipses, and an inconsistency caused by hardcoding the token estimator.

context_budget=context_budget,
currency=currency,
profile=profile,
trimming_enabled=False,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

In _trim_sections, trimming_enabled=False is passed to _render_and_estimate. Since _render_and_estimate builds a fresh template context from the original agent identity, this causes any personality trimming applied in the initial render to be lost.

As a result, the personality section reverts to its full size during iterative section trimming. This can lead to the prompt exceeding the total token budget even after all optional sections are removed, or cause the engine to aggressively trim other critical sections (like the task or policies) because it incorrectly perceives the personality section as taking up its full original budget. _trim_sections should respect the personality trimming decisions made in the first pass.

Comment on lines +215 to +221
budget = max_chars - len(ellipsis)
truncated = description[:budget]
# Find last space to avoid splitting mid-word.
last_space = truncated.rfind(" ")
if last_space > 0:
truncated = truncated[:last_space]
return truncated.rstrip() + ellipsis
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The _truncate_description function always appends an ellipsis, even if the input string already fits within the max_chars limit. This results in unnecessary truncation markers and slightly increased token counts for descriptions that don't actually need trimming. Additionally, it causes an empty description to be returned as "...", which is likely unintended.

Note that the unit test test_empty_input in tests/unit/engine/test_personality_trimming.py currently asserts this buggy behavior and should be updated accordingly.

    if len(description) <= max_chars:
        return description

    budget = max_chars - len(ellipsis)
    truncated = description[:budget]
    # Find last space to avoid splitting mid-word.
    last_space = truncated.rfind(" ")
    if last_space > 0:
        truncated = truncated[:last_space]
    return truncated.rstrip() + ellipsis

:class:`PersonalityTrimInfo` when trimming was applied, or
``None`` when the section was already within budget.
"""
estimator = DefaultTokenEstimator()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

_trim_personality instantiates a DefaultTokenEstimator internally, which ignores any custom token_estimator (such as a more accurate Tiktoken-based implementation) that may have been passed to build_system_prompt. This leads to inconsistent token counting where the personality section is trimmed based on a character-count heuristic while the rest of the prompt might be measured using actual model-specific encoding. The estimator should be passed down through the call chain from build_system_prompt.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements enforcement of PromptProfile.max_personality_tokens by introducing progressive, token-estimate-based trimming of the personality section, and exposes trimming metadata through the prompt-building API and observability hooks.

Changes:

  • Added a 3-tier personality trimming pipeline (condense → truncate description → minimal fallback) with PersonalityTrimInfo metadata.
  • Integrated trimming metadata into SystemPrompt, added prompt-builder controls for enabling trimming and overriding the token cap, and prevented redundant trimming during section re-renders.
  • Introduced ENGINE settings namespace + definitions, new observability constant, and a WS event type; added comprehensive unit tests and updated docs.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/unit/settings/test_engine_settings.py Validates ENGINE namespace settings registration and constraints.
tests/unit/engine/test_prompt.py Updates expectations for build_core_context() now returning (ctx, trim_info).
tests/unit/engine/test_personality_trimming.py Adds unit + integration tests for token estimation, tiered trimming, logging, overrides, and WS enum.
src/synthorg/settings/enums.py Adds SettingNamespace.ENGINE.
src/synthorg/settings/definitions/engine.py Registers 3 engine settings for personality trimming behavior.
src/synthorg/settings/definitions/init.py Ensures engine definitions module is imported/registered.
src/synthorg/observability/events/prompt.py Adds PROMPT_PERSONALITY_TRIMMED event constant.
src/synthorg/engine/prompt.py Plumbs trimming controls/metadata through prompt rendering and result model.
src/synthorg/engine/prompt_profiles.py Updates max_personality_tokens docstring to reflect active enforcement.
src/synthorg/engine/agent_engine.py Logs trimming occurrence at engine level with agent context.
src/synthorg/engine/_prompt_helpers.py Implements token estimation + progressive trimming + PersonalityTrimInfo; refactors autonomy formatting; changes build_core_context return type.
src/synthorg/api/ws_models.py Adds WsEventType.PERSONALITY_TRIMMED.
docs/design/engine.md Documents profile token caps, trimming tiers, and new invariants/logging.
CLAUDE.md Updates package structure notes to mention personality trimming.

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

Comment on lines +285 to +288
estimator = DefaultTokenEstimator()
max_tokens = profile.max_personality_tokens
mode: PersonalityMode = ctx["personality_mode"]
before = _estimate_personality_tokens(ctx, mode, estimator)
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

Personality trimming always instantiates and uses DefaultTokenEstimator here. If a caller supplies a custom token_estimator to build_system_prompt (e.g., a tokenizer-backed estimator), the personality “hard cap” can become inconsistent with the estimator used for the overall prompt, and trimming may under/over-trim. Consider threading a PromptTokenEstimator into build_core_context/_trim_personality/_estimate_personality_tokens and using the same estimator instance that prompt.py uses for estimation.

Copilot uses AI. Check for mistakes.
Comment on lines +200 to +208
``"..."`` as a suffix. Returns an empty string when *max_chars*
is too small to hold any meaningful content.

Args:
description: Original description text.
max_chars: Maximum character count for the result.

Returns:
Truncated description or empty string.
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

_truncate_description() returns "..." when the input description is empty (as implemented/tested), but the docstring implies the function returns either a truncated description or an empty string. Either adjust the implementation to return "" for empty input, or update the docstring to document the empty-input behavior explicitly so callers don’t accidentally render an ellipsis-only description.

Suggested change
``"..."`` as a suffix. Returns an empty string when *max_chars*
is too small to hold any meaningful content.
Args:
description: Original description text.
max_chars: Maximum character count for the result.
Returns:
Truncated description or empty string.
``"..."`` as a suffix. Returns an empty string only when
*max_chars* is too small to hold at least one character plus the
ellipsis. If *description* is empty and there is sufficient room,
the result is ``"..."``.
Args:
description: Original description text.
max_chars: Maximum character count for the result.
Returns:
Truncated description with an ellipsis suffix, ``"..."`` for an
empty description when space permits, or an empty string when
*max_chars* is too small.

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 4, 2026

Codecov Report

❌ Patch coverage is 97.10145% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.76%. Comparing base (7b54262) to head (c662162).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/engine/_prompt_helpers.py 96.66% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1059      +/-   ##
==========================================
+ Coverage   90.74%   90.76%   +0.01%     
==========================================
  Files         710      711       +1     
  Lines       40396    40521     +125     
  Branches     4035     4056      +21     
==========================================
+ Hits        36659    36780     +121     
- Misses       3038     3040       +2     
- Partials      699      701       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/synthorg/engine/agent_engine.py (1)

862-872: ⚠️ Potential issue | 🟠 Major

Runtime trimming controls are still not applied in engine execution path.

At Line 865, build_system_prompt(...) is called without personality_trimming_enabled / max_personality_tokens_override, and Lines 862-864 leave this as TODO. That makes the new ENGINE settings ineffective for normal AgentEngine.run() prompt builds.

Suggested fix direction
-        system_prompt = build_system_prompt(
+        system_prompt = build_system_prompt(
             agent=identity,
             task=task,
             available_tools=tool_defs,
             effective_autonomy=effective_autonomy,
             currency=cur_code,
             model_tier=identity.model.model_tier,
+            personality_trimming_enabled=resolved_engine_settings.personality_trimming_enabled,
+            max_personality_tokens_override=resolved_engine_settings.personality_max_tokens_override,
         )

Pass values resolved from ConfigResolver (or equivalent injected settings source) before merge.

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

In `@src/synthorg/engine/agent_engine.py` around lines 862 - 872, The system
prompt is built without applying runtime trimming settings so
personality_trimming_enabled and personality_max_tokens_override are ignored;
update the AgentEngine.run path to fetch the resolved settings (e.g., via
ConfigResolver or injected settings object) and pass them into
build_system_prompt by adding the personality_trimming_enabled and
personality_max_tokens_override arguments (use the resolved boolean and token
limit variables) when calling build_system_prompt with agent=identity,
task=task, available_tools=tool_defs, effective_autonomy=effective_autonomy,
currency=cur_code, model_tier=identity.model.model_tier so the engine honors the
runtime trimming controls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/engine/_prompt_helpers.py`:
- Around line 196-221: The function _truncate_description currently appends an
ellipsis even when the input description is empty; add an early check at the top
of _truncate_description to return an empty string for empty or whitespace-only
description (e.g., if not description.strip(): return "") before computing
ellipsis, budget, truncated, etc., so that ellipsis is only added when there is
actual content to truncate.

In `@src/synthorg/engine/prompt_profiles.py`:
- Around line 41-44: The docstring for the max_personality_tokens field is
inconsistent (it calls the value a "Hard cap" earlier but still says "Soft
limit" around the current text); update the wording so all occurrences
consistently state that max_personality_tokens is a hard cap enforced by
build_core_context via progressive trimming (drop enums, truncate description,
fallback to minimal mode). Search for max_personality_tokens and the surrounding
lines (around the current 58-61 region) in the class docstring and replace any
"Soft limit" phrasing with a clear "Hard cap" description and mention
build_core_context as the enforcing function.

---

Outside diff comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 862-872: The system prompt is built without applying runtime
trimming settings so personality_trimming_enabled and
personality_max_tokens_override are ignored; update the AgentEngine.run path to
fetch the resolved settings (e.g., via ConfigResolver or injected settings
object) and pass them into build_system_prompt by adding the
personality_trimming_enabled and personality_max_tokens_override arguments (use
the resolved boolean and token limit variables) when calling build_system_prompt
with agent=identity, task=task, available_tools=tool_defs,
effective_autonomy=effective_autonomy, currency=cur_code,
model_tier=identity.model.model_tier so the engine honors the runtime trimming
controls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 664bc135-819d-4028-be15-645c0526956f

📥 Commits

Reviewing files that changed from the base of the PR and between 67650c5 and 91037e3.

📒 Files selected for processing (14)
  • CLAUDE.md
  • docs/design/engine.md
  • src/synthorg/api/ws_models.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/settings/enums.py
  • tests/unit/engine/test_personality_trimming.py
  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.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). (5)
  • GitHub Check: Agent
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations — Python 3.14 has PEP 649
PEP 758 except syntax: use except A, B: (no parentheses) — ruff enforces this on Python 3.14
Type hints: all public functions, mypy strict mode
Docstrings: Google style, required on public classes/functions (enforced by ruff D rules)

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/api/ws_models.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/engine/prompt_profiles.py
  • tests/unit/engine/test_prompt.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • tests/unit/engine/test_personality_trimming.py
  • src/synthorg/engine/_prompt_helpers.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
Async concurrency: 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.
Line length: 88 characters (ruff)

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/api/ws_models.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/api/ws_models.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Config vs runtime state: frozen Pydantic models for config/identity; 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.
Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time.
Models: use @computed_field for derived values instead of storing + validating redundant fields (e.g., TokenUsage.total_tokens)
Models: use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitespace validators
Functions: < 50 lines, files < 800 lines
Errors: handle explicitly, never silently swallow
Validate: at system boundaries (user input, external APIs, config files)
Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__)
Never use import logging / logging.getLogger() / print() in application code (exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr) for handler construction, bootstrap, and error reporting code)
Variable name: always logger (not _logger, not log)
Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
Structured kwargs: always 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 crea...

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/api/ws_models.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Async: asyncio_mode = "auto" — no manual @pytest.mark.asyncio needed
Timeout: 30 seconds per test (global in pyproject.toml — do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed)
Parametrize: Prefer @pytest.mark.parametrize for testing similar cases
Tests must use test-provider, test-small-001, etc. (vendor-agnostic naming)
Property-based testing: Python uses Hypothesis (@given + @settings). Hypothesis profiles configured in tests/conftest.py: ci (deterministic, max_examples=10 + derandomize=True), dev (1000 examples), fuzz (10,000 examples, no deadline), extreme (500,000 examples, no deadline).
Flaky tests: NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins.
For tasks that must block indefinitely until cancelled (e.g. simulating a slow provider or stubborn coroutine), use asyncio.Event().wait() instead of asyncio.sleep(large_number) — it is cancellation-safe and carries no timing assumptions.

Files:

  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_prompt.py
  • tests/unit/engine/test_personality_trimming.py

⚙️ CodeRabbit configuration file

Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare @settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which @given() honors automatically.

Files:

  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_prompt.py
  • tests/unit/engine/test_personality_trimming.py
🧠 Learnings (30)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)

Applied to files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.

Applied to files:

  • src/synthorg/settings/enums.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • src/synthorg/engine/agent_engine.py
  • docs/design/engine.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/engine/coordination/**/*.py : Task coordination uses multi-agent pipeline with 4 dispatchers (SAS/centralized/decentralized/context-dependent), wave execution, and workspace lifecycle integration.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to src/synthorg/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields at validation time.

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-02T11:49:21.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T11:49:21.202Z
Learning: Applies to **/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`) with `allow_inf_nan=False` in all `ConfigDict` declarations

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields at validation time

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).

Applied to files:

  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
🔇 Additional comments (27)
src/synthorg/settings/enums.py (1)

22-22: ENGINE namespace enum addition is correct.

This aligns with the new settings namespace wiring and follows existing enum conventions.

CLAUDE.md (1)

99-99: Documentation update is aligned with the feature.

The engine package summary now accurately mentions personality trimming support.

src/synthorg/observability/events/prompt.py (1)

17-17: New prompt event constant looks good.

PROMPT_PERSONALITY_TRIMMED matches the existing observability event convention.

src/synthorg/api/ws_models.py (1)

63-64: WebSocket event type addition is clean.

PERSONALITY_TRIMMED fits the existing event taxonomy and enum structure.

src/synthorg/settings/definitions/__init__.py (1)

13-13: Engine settings definitions are wired correctly at package level.

Adding both the import and __all__ export keeps registration and public module exposure consistent.

Also applies to: 26-26

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

1221-1224: Test update correctly validates the new return contract.

Unpacking (ctx, trim_info) plus asserting trim_info is None is the right non-trimming baseline check.

Also applies to: 1229-1229

tests/unit/settings/test_engine_settings.py (1)

1-53: LGTM!

Clean and comprehensive test coverage for the ENGINE namespace settings. Tests properly verify namespace registration, individual setting types/defaults/constraints, and total setting count. Appropriate use of @pytest.mark.unit marker.

src/synthorg/settings/definitions/engine.py (1)

1-47: LGTM!

Settings definitions follow established patterns with appropriate types, defaults, and constraints. The descriptions clearly explain behavior (especially the 0 = use profile defaults semantic for the override setting).

src/synthorg/engine/prompt.py (4)

85-118: LGTM!

SystemPrompt model correctly adds the personality_trim_info field with appropriate docstring update. The model maintains frozen semantics and allow_inf_nan=False as required.


183-189: LGTM!

Override logic correctly guards against non-positive values before applying model_copy(update=...), ensuring 0 and negative values fall back to profile defaults as documented.


490-542: LGTM!

_trim_sections correctly passes trimming_enabled=False on section-level re-renders to avoid redundant personality trimming work. This is a good optimization since personality trimming already ran on the initial render.


573-636: LGTM!

_render_with_trimming correctly threads the trimming_enabled flag through to _render_and_estimate and properly captures and propagates trim_info to the final SystemPrompt via _build_prompt_result.

docs/design/engine.md (2)

711-715: LGTM!

Updated profile table correctly documents the max_personality_tokens budget for each tier, aligning with the implementation values.


717-759: LGTM!

The new "Personality Trimming" section accurately documents the three-tier progressive trimming strategy, the PersonalityTrimInfo metadata fields, the ENGINE namespace runtime controls, and the observability event. This aligns well with the implementation in _prompt_helpers.py.

tests/unit/engine/test_personality_trimming.py (8)

1-103: LGTM!

Well-structured test fixtures with clear helper functions. The _verbose_agent() factory correctly creates an agent at the 500-char description limit to test trimming behavior, and _build_ctx() properly disables trimming for manual trimming tests.


108-165: LGTM!

Token estimation tests correctly verify the relative ordering of token counts across modes (full > condensed > minimal) and that empty descriptions reduce estimates. Good coverage of the estimation logic.


170-292: LGTM!

Comprehensive trimming tests covering all tiers, edge cases (empty description, already minimal), and validation of PersonalityTrimInfo fields. The tier transition logic tests are particularly well-designed.


297-329: LGTM!

Good use of structlog.testing.capture_logs() to verify logging behavior. Tests correctly assert that PROMPT_PERSONALITY_TRIMMED is logged exactly once when trimming occurs and not at all when within budget.


393-488: LGTM!

Excellent integration tests via build_system_prompt covering the full flow including personality_trimming_enabled, max_personality_tokens_override semantics (including zero and negative values), and personality_trim_info propagation to SystemPrompt.


541-603: LGTM!

Thorough Pydantic validation tests for PersonalityTrimInfo including cross-field invariants (before_tokens > max_tokens, after_tokens <= before_tokens) and the budget_met computed field. Good coverage of rejection cases.


663-668: LGTM!

Good verification that WsEventType.PERSONALITY_TRIMMED exists with the expected value, ensuring the WebSocket event infrastructure is properly wired.


525-528: Test correctly reflects implementation behavior.

The test accurately verifies that _truncate_description("", 100) returns "...". The implementation is designed to always append the ellipsis suffix to the result, and the test properly documents this edge case behavior. With max_chars=100 and description="", the function budgets 97 characters for content, finds no content to truncate, and appends "..." to the empty string, yielding "...". The test is correctly marked with @pytest.mark.unit and is compliant with all testing guidelines.

src/synthorg/engine/_prompt_helpers.py (5)

39-81: LGTM!

PersonalityTrimInfo is well-designed with proper ConfigDict(frozen=True, allow_inf_nan=False), cross-field validation ensuring trimming invariants, and @computed_field for the derived budget_met property. The docstring clearly explains tier 3 best-effort semantics.


146-193: LGTM!

Token estimation correctly mirrors the Jinja2 template rendering logic for each personality mode, ensuring accurate budget calculations. The assembly of markdown-formatted text before estimation is a clean approach.


264-316: LGTM!

Progressive trimming logic is well-structured with clear tier progression. The early return when within budget (line 290-291) avoids unnecessary work, and each tier helper returns None to signal budget not met, enabling clean fallthrough to the next tier.


319-342: LGTM!

_make_trim_info provides a clean factory that handles both logging (at DEBUG level) and model construction. The docstring correctly notes that engine-layer logging at INFO happens elsewhere.


345-408: LGTM!

build_core_context correctly integrates trimming into the context-building flow. The trimming_enabled flag provides proper control, and the tuple return type cleanly separates the context dict from optional trim metadata. Good use of _format_autonomy extraction for readability.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/synthorg/engine/prompt.py (1)

500-513: ⚠️ Potential issue | 🟠 Major

_trim_sections() ignores personality_trimming_enabled on re-renders.

If a caller sets personality_trimming_enabled=False and the first render still exceeds max_tokens, these re-renders call _render_and_estimate() without forwarding the flag. That falls back to True, so the final prompt can still have a trimmed personality, and because the re-rendered trim_info is discarded, SystemPrompt.personality_trim_info can remain None. This breaks the new API contract on the max_tokens path.

Suggested fix
 def _trim_sections(  # noqa: PLR0913
     *,
@@
     effective_autonomy: EffectiveAutonomy | None = None,
     context_budget: str | None = None,
     currency: str = DEFAULT_CURRENCY,
     profile: PromptProfile | None = None,
+    trimming_enabled: bool = True,
 ) -> tuple[
     str,
     int,
     Task | None,
     Company | None,
     tuple[str, ...],
+    PersonalityTrimInfo | None,
 ]:
@@
     trimmed_sections: list[str] = []
+    trim_info: PersonalityTrimInfo | None = None

     for section in _TRIMMABLE_SECTIONS:
-        content, estimated, _ = _render_and_estimate(
+        content, estimated, trim_info = _render_and_estimate(
             template_str,
             agent,
             role,
@@
             effective_autonomy=effective_autonomy,
             context_budget=context_budget,
             currency=currency,
             profile=profile,
+            trimming_enabled=trimming_enabled,
         )
@@
-        content, estimated, _ = _render_and_estimate(
+        content, estimated, trim_info = _render_and_estimate(
             template_str,
             agent,
             role,
@@
             effective_autonomy=effective_autonomy,
             context_budget=context_budget,
             currency=currency,
             profile=profile,
+            trimming_enabled=trimming_enabled,
         )

     _log_trim_results(agent, max_tokens, estimated, trimmed_sections)

-    return content, estimated, task, company, org_policies
+    return content, estimated, task, company, org_policies, trim_info
@@
-        content, estimated, task, company, org_policies = _trim_sections(
+        content, estimated, task, company, org_policies, trim_info = _trim_sections(
             template_str=template_str,
             agent=agent,
             role=role,
@@
             effective_autonomy=effective_autonomy,
             context_budget=context_budget_indicator,
             currency=currency,
             profile=profile,
+            trimming_enabled=trimming_enabled,
         )

Also applies to: 533-546, 595-640

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

In `@src/synthorg/engine/prompt.py` around lines 500 - 513, The bug is that
_trim_sections() can trigger re-renders which call _render_and_estimate()
without propagating the personality_trimming_enabled flag, causing re-renders to
default to trimming and discarding trim_info; update every call site that
re-invokes _render_and_estimate (including the shown call and the other
instances around the 533-546 and 595-640 regions) to pass the
personality_trimming_enabled parameter through unchanged, and make sure the
returned trim_info from _render_and_estimate is captured and assigned to
SystemPrompt.personality_trim_info (or otherwise preserved) so the final prompt
honors the original personality_trimming_enabled choice.
🤖 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/design/engine.md`:
- Around line 711-715: The table row for the "standard" / medium profile
incorrectly lists "Bullet list" for Acceptance Criteria; update that cell to
"Nested list" to match the actual behavior controlled by the _STANDARD_PROFILE
flag (which keeps simplify_acceptance_criteria=False). Locate the "standard"
profile row in the table and change the Acceptance Criteria column to "Nested
list" so the docs reflect _STANDARD_PROFILE's nested-list output.

In `@src/synthorg/engine/_prompt_helpers.py`:
- Around line 42-45: The validator must reject over-budget states for trim_tier
1 and 2: add a guard in the personality-trimming validation (using the existing
symbols max_personality_tokens, after_tokens, and trim_tier) that raises/returns
an error when trim_tier is 1 or 2 and after_tokens > max_personality_tokens;
leave trim_tier==3 as best-effort. Apply the same check in the other validator
block that handles the communication_style/personality fallback (the section
referenced around lines 66-74) so public metadata cannot represent states the
trimming algorithm should never produce.
- Around line 151-157: The personality token estimator currently omits the
section heading and the template's static wrapper, making after_tokens and
budget_met optimistic; update the helper in _prompt_helpers.py so it either (a)
fully renders the Jinja2 template (including heading and static wrapper) and
passes that rendered string into the tokenizer/estimator, or (b) explicitly adds
the token cost of the known static wrapper and heading to after_tokens before
comparing to max_tokens/budget_met; apply the same fix to the other estimator
usage noted around the lines referenced (the other occurrence at the same
module), and ensure you update references to after_tokens, max_tokens and
budget_met accordingly so the final check reflects the whole rendered prompt.

In `@src/synthorg/engine/agent_engine.py`:
- Around line 889-900: The PROMPT_PERSONALITY_TRIMMED log emitted when
system_prompt.personality_trim_info is present is missing task context; update
the logger.info call inside the if block (where
system_prompt.personality_trim_info is assigned to ti) to include task_id as an
additional structured field alongside agent_id, agent_name (identity.name),
before_tokens, after_tokens, max_tokens, trim_tier, and budget_met so the trim
event can be correlated with the current task.

---

Outside diff comments:
In `@src/synthorg/engine/prompt.py`:
- Around line 500-513: The bug is that _trim_sections() can trigger re-renders
which call _render_and_estimate() without propagating the
personality_trimming_enabled flag, causing re-renders to default to trimming and
discarding trim_info; update every call site that re-invokes
_render_and_estimate (including the shown call and the other instances around
the 533-546 and 595-640 regions) to pass the personality_trimming_enabled
parameter through unchanged, and make sure the returned trim_info from
_render_and_estimate is captured and assigned to
SystemPrompt.personality_trim_info (or otherwise preserved) so the final prompt
honors the original personality_trimming_enabled choice.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b322c912-560c-4d1a-81cc-858785b87e94

📥 Commits

Reviewing files that changed from the base of the PR and between 91037e3 and 60a7e06.

📒 Files selected for processing (10)
  • docs/design/engine.md
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/settings/definitions/engine.py
  • tests/unit/engine/test_personality_trimming.py
  • tests/unit/settings/test_engine_settings.py
  • web/src/api/types.ts
  • web/src/utils/constants.ts
📜 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). (7)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (7)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-*)) for colors. NEVER hardcode hex values or rgba() in .tsx/.ts files
Use font-sans or font-mono for typography (maps to Geist tokens). NEVER set fontFamily directly in styles
Use density-aware spacing tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders
Do NOT use rgba() with hardcoded values -- use design token variables
Do NOT hardcode Framer Motion transition durations -- use @/lib/motion presets
Use /// <reference types="vite/client" /> to enable side-effect imports for CSS in Storybook 10 with TypeScript 6
Ensure all TypeScript code passes type checking via npm --prefix web run type-check
Bundle size must be auditable via npm --prefix web run analyze (generates stats.html treemap)
Achieve Lighthouse performance audit target of 90+ via npm --prefix web run lighthouse

web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones
Web Dashboard Design System (MANDATORY): See web/CLAUDE.md for the full component inventory, design token rules, and post-training references (TS6, Storybook 10).

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Run ESLint with zero warnings enforced via npm --prefix web run lint

Line length: 88 characters (ruff) for JavaScript/TypeScript files

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and @/lib/motion presets

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations — Python 3.14 has PEP 649
PEP 758 except syntax: use except A, B: (no parentheses) — ruff enforces this on Python 3.14
Type hints: all public functions, mypy strict mode
Docstrings: Google style, required on public classes/functions (enforced by ruff D rules)

Files:

  • src/synthorg/engine/prompt_profiles.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
  • tests/unit/engine/test_personality_trimming.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
Async concurrency: 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.
Line length: 88 characters (ruff)

Files:

  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Config vs runtime state: frozen Pydantic models for config/identity; 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.
Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time.
Models: use @computed_field for derived values instead of storing + validating redundant fields (e.g., TokenUsage.total_tokens)
Models: use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitespace validators
Functions: < 50 lines, files < 800 lines
Errors: handle explicitly, never silently swallow
Validate: at system boundaries (user input, external APIs, config files)
Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__)
Never use import logging / logging.getLogger() / print() in application code (exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr) for handler construction, bootstrap, and error reporting code)
Variable name: always logger (not _logger, not log)
Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
Structured kwargs: always 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 crea...

Files:

  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Async: asyncio_mode = "auto" — no manual @pytest.mark.asyncio needed
Timeout: 30 seconds per test (global in pyproject.toml — do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed)
Parametrize: Prefer @pytest.mark.parametrize for testing similar cases
Tests must use test-provider, test-small-001, etc. (vendor-agnostic naming)
Property-based testing: Python uses Hypothesis (@given + @settings). Hypothesis profiles configured in tests/conftest.py: ci (deterministic, max_examples=10 + derandomize=True), dev (1000 examples), fuzz (10,000 examples, no deadline), extreme (500,000 examples, no deadline).
Flaky tests: NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins.
For tasks that must block indefinitely until cancelled (e.g. simulating a slow provider or stubborn coroutine), use asyncio.Event().wait() instead of asyncio.sleep(large_number) — it is cancellation-safe and carries no timing assumptions.

Files:

  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_personality_trimming.py

⚙️ CodeRabbit configuration file

Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare @settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which @given() honors automatically.

Files:

  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_personality_trimming.py
🧠 Learnings (27)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)

Applied to files:

  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to tests/**/*.py : Property-based testing: Python uses Hypothesis (`given` + `settings`). Hypothesis profiles configured in `tests/conftest.py`: `ci` (deterministic, `max_examples=10` + `derandomize=True`), `dev` (1000 examples), `fuzz` (10,000 examples, no deadline), `extreme` (500,000 examples, no deadline).

Applied to files:

  • tests/unit/settings/test_engine_settings.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.

Applied to files:

  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)

Applied to files:

  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).

Applied to files:

  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).

Applied to files:

  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • docs/design/engine.md
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-31T21:07:37.469Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.469Z
Learning: Applies to src/synthorg/providers/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in `ProviderConfig`

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-03T21:18:15.006Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T21:18:15.006Z
Learning: Applies to src/synthorg/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields at validation time.

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-02T11:49:21.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T11:49:21.202Z
Learning: Applies to **/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`) with `allow_inf_nan=False` in all `ConfigDict` declarations

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields at validation time

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.

Applied to files:

  • src/synthorg/engine/prompt.py

@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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/design/engine.md`:
- Around line 729-733: The docs currently mention
SystemPrompt.personality_trim_info and the PersonalityTrimInfo model plus the
ENGINE runtime flags personality_trimming_enabled and
personality_max_tokens_override but omit the operator-facing notification
toggle; update this section to document the third ENGINE control
personality_trimming_notify, describe its purpose (enables/disables operator
notifications when trimming occurs), expected values/type (e.g., boolean), and
how it interacts with personality_trimming_enabled and
personality_max_tokens_override so operators can fully configure trimming
behavior.

In `@src/synthorg/engine/_prompt_helpers.py`:
- Around line 275-281: The code currently computes max_chars as remaining * 4
which ignores the provided PromptTokenEstimator; instead use the estimator to
convert remaining tokens to characters (e.g., call the provided
PromptTokenEstimator method, such as token_estimator.tokens_to_chars(remaining))
and assign that to max_chars before calling _truncate_description(saved_desc,
max_chars) so tier-2 truncation respects non-default/tokenizer-specific
token->char ratios and avoids dropping to tier-3 unnecessarily.

In `@src/synthorg/engine/agent_engine.py`:
- Around line 867-877: The code currently calls
self._config_resolver.get_bool("engine","personality_trimming_enabled") and
get_int("engine","personality_max_tokens_override") which raise if keys are
absent, so wrap those two calls in a try/except (catch the resolver's
missing-key error or a broad Exception) and only override the local defaults
(trimming_enabled and tokens_override) when the resolver returns values
successfully; if an exception is raised, leave trimming_enabled and
tokens_override as previously initialized (i.e., True / None) so settings
rollout falls back to local defaults instead of crashing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 75c0ab6a-df0f-4ae1-b9c1-182bc3f604a7

📥 Commits

Reviewing files that changed from the base of the PR and between 60a7e06 and de91d02.

📒 Files selected for processing (4)
  • docs/design/engine.md
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations
Use PEP 758 except syntax: except A, B: (no parentheses) — ruff enforces this on Python 3.14
All public functions must have type hints with mypy strict mode
Public classes and functions must have Google-style docstrings (enforced by ruff D rules)
Use NotBlankStr (from core.types) for all identifier/name fields, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and 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.
In all Pydantic ConfigDict declarations, use allow_inf_nan=False to reject NaN/Inf in numeric fields at validation time
Use @computed_field for derived values in Pydantic models instead of storing and validating redundant fields (e.g., TokenUsage.total_tokens)
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.
Functions must be less than 50 lines; files must be less than 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Every module with business logic must import from synthorg.observability import get_logger and set logger = get_logger(__name__). Never use import logging or logging.getLogger() in application code.
Always use variable name logger (not _logger, not log) for structured logging
Line length must be 88 characters (enforced by ruff)

Files:

  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Use event name constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
All logger calls must use structured kwargs: logger.info(EVENT, key=value) — never use string formatting like 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 level
Use DEBUG level for object creation, internal flow, and entry/exit of key functions
Never implement retry logic in driver subclasses or calling code — retry is handled automatically by BaseCompletionProvider. Set RetryConfig and RateLimiterConfig per-provider in ProviderConfig.
Never use vendor-specific names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, or comments. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001. Vendor names only appear in: (1) docs/design/operations.md, (2) .claude/ skill files, (3) third-party import paths, (4) src/synthorg/providers/presets.py (user-facing runtime data).
Create new objects instead of mutating existing ones. Immutability is enforced throughout the codebase.

Files:

  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
src/**/*.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Always read the relevant docs/design/ page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior.

Files:

  • docs/design/engine.md
🧠 Learnings (21)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • src/synthorg/engine/agent_engine.py
  • docs/design/engine.md
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-31T21:07:37.469Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.469Z
Learning: Applies to src/synthorg/providers/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-02T11:49:21.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T11:49:21.202Z
Learning: Applies to **/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`) with `allow_inf_nan=False` in all `ConfigDict` declarations

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields at validation time

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
🔇 Additional comments (1)
src/synthorg/engine/prompt.py (1)

184-188: Good use of model_copy() for the override.

Since get_prompt_profile() returns shared built-in profiles, copying here keeps the override per-call instead of leaking it across later prompt builds.

Comment on lines +729 to +733
Trimming metadata is attached to `SystemPrompt.personality_trim_info`
(`PersonalityTrimInfo` model with `before_tokens`, `after_tokens`,
`max_tokens`, `trim_tier`, and `budget_met` computed field). Runtime
settings in the `ENGINE` namespace control trimming
(`personality_trimming_enabled`, `personality_max_tokens_override`).
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document the notification toggle here as well.

This section lists only two of the three ENGINE controls introduced by the feature. Without personality_trimming_notify, the operator-facing runtime surface is incomplete.

📝 Suggested doc tweak
- settings in the `ENGINE` namespace control trimming
- (`personality_trimming_enabled`, `personality_max_tokens_override`).
+ settings in the `ENGINE` namespace control trimming and trim notifications
+ (`personality_trimming_enabled`, `personality_trimming_notify`,
+ `personality_max_tokens_override`).
📝 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.

Suggested change
Trimming metadata is attached to `SystemPrompt.personality_trim_info`
(`PersonalityTrimInfo` model with `before_tokens`, `after_tokens`,
`max_tokens`, `trim_tier`, and `budget_met` computed field). Runtime
settings in the `ENGINE` namespace control trimming
(`personality_trimming_enabled`, `personality_max_tokens_override`).
Trimming metadata is attached to `SystemPrompt.personality_trim_info`
(`PersonalityTrimInfo` model with `before_tokens`, `after_tokens`,
`max_tokens`, `trim_tier`, and `budget_met` computed field). Runtime
settings in the `ENGINE` namespace control trimming and trim notifications
(`personality_trimming_enabled`, `personality_trimming_notify`,
`personality_max_tokens_override`).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/engine.md` around lines 729 - 733, The docs currently mention
SystemPrompt.personality_trim_info and the PersonalityTrimInfo model plus the
ENGINE runtime flags personality_trimming_enabled and
personality_max_tokens_override but omit the operator-facing notification
toggle; update this section to document the third ENGINE control
personality_trimming_notify, describe its purpose (enables/disables operator
notifications when trimming occurs), expected values/type (e.g., boolean), and
how it interacts with personality_trimming_enabled and
personality_max_tokens_override so operators can fully configure trimming
behavior.

Comment on lines +275 to +281
remaining = max_tokens - tokens_without
if remaining > 0:
max_chars = remaining * 4 # Inverse of char/4 heuristic.
ctx["personality_description"] = _truncate_description(
saved_desc,
max_chars,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use the provided estimator for tier-2 truncation.

remaining * 4 hardcodes the default char/4 heuristic even though this path accepts any PromptTokenEstimator. With a real tokenizer or any non-default estimator, tier 2 can miss the budget and drop straight to tier 3 even when a shorter truncated description would fit.

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

In `@src/synthorg/engine/_prompt_helpers.py` around lines 275 - 281, The code
currently computes max_chars as remaining * 4 which ignores the provided
PromptTokenEstimator; instead use the estimator to convert remaining tokens to
characters (e.g., call the provided PromptTokenEstimator method, such as
token_estimator.tokens_to_chars(remaining)) and assign that to max_chars before
calling _truncate_description(saved_desc, max_chars) so tier-2 truncation
respects non-default/tokenizer-specific token->char ratios and avoids dropping
to tier-3 unnecessarily.

@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/synthorg/engine/prompt.py (2)

500-515: ⚠️ Potential issue | 🟡 Minor

Section trimming re-applies personality trimming on every pass.

Each _render_and_estimate() call rebuilds the core context from raw agent data with trimming_enabled=True, so prompts that also need max_tokens trimming can emit multiple PROMPT_PERSONALITY_TRIMMED helper logs and repeated tier-3 warnings for a single build. Reuse the first trimmed context, or suppress trim side effects during these re-renders.

Also applies to: 535-548

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

In `@src/synthorg/engine/prompt.py` around lines 500 - 515, The loop over
_TRIMMABLE_SECTIONS repeatedly calls _render_and_estimate with
trimming_enabled=True, causing personality trimming side-effects (multiple
PROMPT_PERSONALITY_TRIMMED logs and warnings) on each re-render; fix by
performing one initial call with trimming_enabled=True to obtain the trimmed
core/context (store its content/estimated result) and then reuse that trimmed
context for subsequent iterations or call _render_and_estimate with
trimming_enabled=False for all later re-renders (update the loop around
_render_and_estimate and references to the returned values to use the cached
trimmed result instead of re-trimming).

232-247: ⚠️ Potential issue | 🟠 Major

Skip personality-cap trimming for custom templates until it is template-aware.

The trim decision is made in _build_core_context() before Jinja rendering, using the default personality block estimator. With custom_template, that can trim fields that never render, or emit personality_trim_info / trim events even when the template has no personality section at all.

Suggested fix
-        result = _render_with_trimming(
+        apply_personality_trimming = (
+            personality_trimming_enabled and custom_template is None
+        )
+        result = _render_with_trimming(
             template_str=template_str,
             agent=agent,
             role=role,
             task=task,
             available_tools=available_tools,
@@
             effective_autonomy=effective_autonomy,
             context_budget_indicator=context_budget_indicator,
             currency=currency,
             profile=profile,
-            trimming_enabled=personality_trimming_enabled,
+            trimming_enabled=apply_personality_trimming,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/prompt.py` around lines 232 - 247, The current call to
_render_with_trimming passes trimming_enabled=personality_trimming_enabled which
allows pre-render trimming even for custom_template; change the callsite so that
when a custom_template is supplied you disable personality-cap trimming (pass
trimming_enabled=False) until the trimming logic becomes template-aware; update
the invocation that sets trimming_enabled (in the block calling
_render_with_trimming) to use False when custom_template is present to avoid
trimming fields that never render and emitting spurious
personality_trim_info/trim events.
♻️ Duplicate comments (2)
docs/design/engine.md (1)

729-733: ⚠️ Potential issue | 🟡 Minor

Document personality_trimming_notify alongside the other ENGINE knobs.

This section still lists only the enable and override settings, so the operator-facing runtime surface is incomplete.

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

In `@docs/design/engine.md` around lines 729 - 733, The docs currently describe
trimming settings (SystemPrompt.personality_trim_info / PersonalityTrimInfo and
ENGINE knobs personality_trimming_enabled and personality_max_tokens_override)
but omit the runtime flag personality_trimming_notify; update the ENGINE runtime
settings list to include personality_trimming_notify with a short description of
its purpose and behavior (how/when notifications are emitted and interaction
with personality_trimming_enabled), and ensure the mention appears next to
personality_trimming_enabled and personality_max_tokens_override so the
operator-facing surface is complete.
src/synthorg/engine/_prompt_helpers.py (1)

275-281: ⚠️ Potential issue | 🟠 Major

Tier-2 truncation still hardcodes the char/4 heuristic.

remaining * 4 ignores the injected PromptTokenEstimator, so tokenizer-backed estimators can miss a tier-2 description length that fits and fall straight to tier 3. Pick the truncation point by re-estimating with estimator instead of assuming 4 chars/token.

Suggested fix
     remaining = max_tokens - tokens_without
     if remaining > 0:
-        max_chars = remaining * 4  # Inverse of char/4 heuristic.
-        ctx["personality_description"] = _truncate_description(
-            saved_desc,
-            max_chars,
-        )
+        low, high = 0, len(saved_desc)
+        best = ""
+        while low <= high:
+            max_chars = (low + high) // 2
+            candidate = _truncate_description(saved_desc, max_chars)
+            ctx["personality_description"] = candidate
+            if _estimate_personality_tokens(ctx, mode, estimator) <= max_tokens:
+                best = candidate
+                low = max_chars + 1
+            else:
+                high = max_chars - 1
+        ctx["personality_description"] = best
     else:
         ctx["personality_description"] = ""
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 879-884: The current except block in agent_engine.py logs a
ConfigResolver fallback as PROMPT_PERSONALITY_TRIMMED; change this to an
appropriate engine/settings error event constant (e.g., ENGINE_SETTINGS_ERROR or
ENGINE_SETTINGS_LOAD_FAILED) so the logger.warning call uses that event instead
of PROMPT_PERSONALITY_TRIMMED, keep the same message ("failed to read ENGINE
settings, using defaults") and exc_info=True to preserve exception details, and
ensure any metrics/telemetry emitted by the logger reflect an error event for
settings load rather than a trim event.

---

Outside diff comments:
In `@src/synthorg/engine/prompt.py`:
- Around line 500-515: The loop over _TRIMMABLE_SECTIONS repeatedly calls
_render_and_estimate with trimming_enabled=True, causing personality trimming
side-effects (multiple PROMPT_PERSONALITY_TRIMMED logs and warnings) on each
re-render; fix by performing one initial call with trimming_enabled=True to
obtain the trimmed core/context (store its content/estimated result) and then
reuse that trimmed context for subsequent iterations or call
_render_and_estimate with trimming_enabled=False for all later re-renders
(update the loop around _render_and_estimate and references to the returned
values to use the cached trimmed result instead of re-trimming).
- Around line 232-247: The current call to _render_with_trimming passes
trimming_enabled=personality_trimming_enabled which allows pre-render trimming
even for custom_template; change the callsite so that when a custom_template is
supplied you disable personality-cap trimming (pass trimming_enabled=False)
until the trimming logic becomes template-aware; update the invocation that sets
trimming_enabled (in the block calling _render_with_trimming) to use False when
custom_template is present to avoid trimming fields that never render and
emitting spurious personality_trim_info/trim events.

---

Duplicate comments:
In `@docs/design/engine.md`:
- Around line 729-733: The docs currently describe trimming settings
(SystemPrompt.personality_trim_info / PersonalityTrimInfo and ENGINE knobs
personality_trimming_enabled and personality_max_tokens_override) but omit the
runtime flag personality_trimming_notify; update the ENGINE runtime settings
list to include personality_trimming_notify with a short description of its
purpose and behavior (how/when notifications are emitted and interaction with
personality_trimming_enabled), and ensure the mention appears next to
personality_trimming_enabled and personality_max_tokens_override so the
operator-facing surface is complete.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a7655ffa-abad-4ebc-803c-5b04d4c28866

📥 Commits

Reviewing files that changed from the base of the PR and between de91d02 and e09ccda.

📒 Files selected for processing (15)
  • CLAUDE.md
  • docs/design/engine.md
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/settings/enums.py
  • tests/unit/engine/test_personality_trimming.py
  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • web/src/api/types.ts
  • web/src/utils/constants.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations
Use PEP 758 except syntax: except A, B: (no parentheses) — ruff enforces this on Python 3.14
All public functions must have type hints with mypy strict mode
Public classes and functions must have Google-style docstrings (enforced by ruff D rules)
Use NotBlankStr (from core.types) for all identifier/name fields, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and 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.
In all Pydantic ConfigDict declarations, use allow_inf_nan=False to reject NaN/Inf in numeric fields at validation time
Use @computed_field for derived values in Pydantic models instead of storing and validating redundant fields (e.g., TokenUsage.total_tokens)
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.
Functions must be less than 50 lines; files must be less than 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Every module with business logic must import from synthorg.observability import get_logger and set logger = get_logger(__name__). Never use import logging or logging.getLogger() in application code.
Always use variable name logger (not _logger, not log) for structured logging
Line length must be 88 characters (enforced by ruff)

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • tests/unit/engine/test_prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/engine/prompt.py
  • tests/unit/engine/test_personality_trimming.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Use event name constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
All logger calls must use structured kwargs: logger.info(EVENT, key=value) — never use string formatting like 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 level
Use DEBUG level for object creation, internal flow, and entry/exit of key functions
Never implement retry logic in driver subclasses or calling code — retry is handled automatically by BaseCompletionProvider. Set RetryConfig and RateLimiterConfig per-provider in ProviderConfig.
Never use vendor-specific names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, or comments. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001. Vendor names only appear in: (1) docs/design/operations.md, (2) .claude/ skill files, (3) third-party import paths, (4) src/synthorg/providers/presets.py (user-facing runtime data).
Create new objects instead of mutating existing ones. Immutability is enforced throughout the codebase.

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
src/**/*.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Always reuse existing components from web/src/components/ui/ before creating new ones
Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and @/lib/motion presets

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write to web/src/
Use property-based testing with fast-check (fc.assert + fc.property) for React component tests

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,js,jsx}: Always use createLogger from @/lib/logger -- never bare console.warn/console.error/console.debug in application code
Variable name for logger: always log (e.g. const log = createLogger('module-name'))
Use log.debug() (DEV-only, stripped in production), log.warn(), log.error() logging levels
Pass dynamic/untrusted values as separate args to logger (not interpolated into the message string) so they go through sanitizeArg
Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding in log messages

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{tsx,ts}: Do NOT create complex (>8 line) JSX inside .map() -- extract to a shared component
Colors: use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files
Typography: use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly
Spacing: use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing
Shadows/Borders: use token variables (var(--so-shadow-card-hover), border-border, border-bright)
Do NOT use rgba() with hardcoded values -- use design token variables
Hardcoded hex colors and rgba values in web/src/ files are automatically caught by a PostToolUse hook and must be fixed
Hardcoded font-family declarations in web/src/ files are automatically caught by a PostToolUse hook and must be fixed
Hardcoded Framer Motion transition durations (should use @/lib/motion presets) are automatically caught by a PostToolUse hook and must be fixed
Duplicate patterns that should use existing shared components in web/src/ are automatically caught by a PostToolUse hook and must be fixed
Complex .map() blocks that should be extracted to components in web/src/ are automatically caught by a PostToolUse hook and must be fixed
CSS side-effect imports need type declarations (Vite's /// <reference types="vite/client" /> covers this) as noUncheckedSideEffectImports defaults to true in TypeScript 6

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Mark all tests with pytest markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, or @pytest.mark.slow
Maintain 80% minimum test coverage (enforced in CI) with pytest --cov=synthorg --cov-fail-under=80
Use asyncio_mode = 'auto' (configured in pyproject.toml) — no manual @pytest.mark.asyncio needed
Tests have a global 30-second timeout per test (configured in pyproject.toml). Non-default overrides like timeout(60) are allowed.
Always include -n 8 when running pytest locally for parallelism via pytest-xdist. Never run tests sequentially.
Prefer @pytest.mark.parametrize for testing similar cases instead of multiple test functions
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, or tests. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases, or test-provider, test-small-001 in tests.
Never skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() instead of widening timing margins. For indefinite blocking, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_personality_trimming.py

⚙️ CodeRabbit configuration file

Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare @settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which @given() honors automatically.

Files:

  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_personality_trimming.py
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Always read the relevant docs/design/ page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior.

Files:

  • docs/design/engine.md
🧠 Learnings (35)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)

Applied to files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.

Applied to files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • docs/design/engine.md
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/engine/coordination/**/*.py : Task coordination uses multi-agent pipeline with 4 dispatchers (SAS/centralized/decentralized/context-dependent), wave execution, and workspace lifecycle integration.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-02T11:49:21.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T11:49:21.202Z
Learning: Applies to **/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`) with `allow_inf_nan=False` in all `ConfigDict` declarations

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields at validation time

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).

Applied to files:

  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).

Applied to files:

  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-31T21:07:37.469Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.469Z
Learning: Applies to src/synthorg/providers/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)

Applied to files:

  • src/synthorg/engine/agent_engine.py
🔇 Additional comments (9)
src/synthorg/settings/enums.py (1)

22-22: LGTM!

The ENGINE namespace is correctly added to the SettingNamespace enum, following the existing pattern. This enables the settings persistence layer to recognize the new engine namespace for personality trimming settings.

src/synthorg/observability/events/prompt.py (1)

17-17: LGTM!

The PROMPT_PERSONALITY_TRIMMED event constant follows the established naming convention (prompt.<subsystem>.<action>) and is correctly typed with Final[str].

src/synthorg/settings/definitions/engine.py (2)

1-38: LGTM!

The engine settings definitions are well-structured and follow the established pattern from other namespace modules. Both settings have appropriate types, defaults, bounds, and grouping.


9-38: The setting personality_trimming_notify does not exist in the codebase and has no implementation or references anywhere. A comprehensive search finds zero matches. The file engine.py contains exactly two settings: personality_trimming_enabled and personality_max_tokens_override.

If the PR summary mentions a third setting, that reference appears to be incorrect or refers to work deferred beyond this PR. Verify the PR objectives are aligned with the implementation.

			> Likely an incorrect or invalid review comment.
src/synthorg/settings/definitions/__init__.py (1)

13-13: LGTM!

The engine module is correctly imported and exported, maintaining alphabetical ordering consistent with the other namespace definition modules.

Also applies to: 26-26

web/src/utils/constants.ts (1)

70-70: LGTM!

The engine namespace is correctly added to both NAMESPACE_ORDER and NAMESPACE_DISPLAY_NAMES, following the existing patterns. The display name "Engine" is appropriately capitalized consistent with other entries.

Also applies to: 84-84

web/src/api/types.ts (1)

1448-1448: LGTM!

The 'engine' literal is correctly added to the SettingNamespace union type, maintaining alignment with the backend SettingNamespace.ENGINE enum member.

tests/unit/settings/test_engine_settings.py (1)

1-45: LGTM!

The test suite provides comprehensive coverage of the ENGINE namespace settings:

  • Namespace registration verification
  • Type and default value assertions for each setting
  • Bounds validation for the integer setting
  • Count verification to catch unintended additions/removals

Well-structured with proper @pytest.mark.unit marker and type hints.

CLAUDE.md (1)

99-99: LGTM!

The documentation accurately reflects the new personality trimming feature within the engine package description, maintaining consistency with the existing documentation style.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 4, 2026 09:37 — with GitHub Actions Inactive
@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 `@src/synthorg/engine/agent_engine.py`:
- Around line 870-889: The try/except around config reads
(_config_resolver.get_bool/get_int) currently swallows system-level errors
(e.g., MemoryError, RecursionError, KeyboardInterrupt) and falls back to
defaults; modify the except block to re-raise system/fatal exceptions and only
log/handle regular exceptions: catch Exception as e and if isinstance(e,
(MemoryError, RecursionError, KeyboardInterrupt, SystemExit)): raise, otherwise
call logger.warning(...) as before so trimming_enabled/tokens_override fallback
only occurs for non-fatal errors.
- Around line 901-913: The code dereferences
system_prompt.personality_trim_info.budget_met although PersonalityTrimInfo
(from _prompt_helpers.py) doesn't define budget_met; either stop referencing it
here or add a safe guard. Fix by updating this call site in agent_engine.py:
when system_prompt.personality_trim_info is not None set ti =
system_prompt.personality_trim_info and remove budget_met from the
PROMPT_PERSONALITY_TRIMMED logger args (or wrap it with getattr(ti,
"budget_met", None) if you intend to support an optional field). Reference
symbols: system_prompt.personality_trim_info, ti, PROMPT_PERSONALITY_TRIMMED,
PersonalityTrimInfo.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ae8c4445-e0af-4810-b5b3-45c962d0f55e

📥 Commits

Reviewing files that changed from the base of the PR and between bd38186 and 39ab243.

📒 Files selected for processing (1)
  • src/synthorg/engine/agent_engine.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations
Use PEP 758 except syntax: except A, B: (no parentheses) — ruff enforces this on Python 3.14
All public functions must have type hints with mypy strict mode
Public classes and functions must have Google-style docstrings (enforced by ruff D rules)
Use NotBlankStr (from core.types) for all identifier/name fields, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and 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.
In all Pydantic ConfigDict declarations, use allow_inf_nan=False to reject NaN/Inf in numeric fields at validation time
Use @computed_field for derived values in Pydantic models instead of storing and validating redundant fields (e.g., TokenUsage.total_tokens)
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.
Functions must be less than 50 lines; files must be less than 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Every module with business logic must import from synthorg.observability import get_logger and set logger = get_logger(__name__). Never use import logging or logging.getLogger() in application code.
Always use variable name logger (not _logger, not log) for structured logging
Line length must be 88 characters (enforced by ruff)

Files:

  • src/synthorg/engine/agent_engine.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Use event name constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
All logger calls must use structured kwargs: logger.info(EVENT, key=value) — never use string formatting like 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 level
Use DEBUG level for object creation, internal flow, and entry/exit of key functions
Never implement retry logic in driver subclasses or calling code — retry is handled automatically by BaseCompletionProvider. Set RetryConfig and RateLimiterConfig per-provider in ProviderConfig.
Never use vendor-specific names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, or comments. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001. Vendor names only appear in: (1) docs/design/operations.md, (2) .claude/ skill files, (3) third-party import paths, (4) src/synthorg/providers/presets.py (user-facing runtime data).
Create new objects instead of mutating existing ones. Immutability is enforced throughout the codebase.

Files:

  • src/synthorg/engine/agent_engine.py
src/**/*.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/engine/agent_engine.py
🧠 Learnings (10)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-31T21:07:37.469Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.469Z
Learning: Applies to src/synthorg/providers/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/engine/agent_engine.py
🔇 Additional comments (1)
src/synthorg/engine/agent_engine.py (1)

231-233: Nice threading of the optional settings resolver.

The constructor docs, typing, and stored field make the new runtime settings hook explicit while keeping the no-resolver path unchanged.

Also applies to: 265-265, 335-335

Comment on lines +870 to +889
if self._config_resolver is not None:
try:
trimming_enabled = await self._config_resolver.get_bool(
"engine",
"personality_trimming_enabled",
)
raw_override = await self._config_resolver.get_int(
"engine",
"personality_max_tokens_override",
)
if raw_override > 0:
tokens_override = raw_override
except Exception:
logger.warning(
EXECUTION_ENGINE_ERROR,
agent_id=agent_id,
task_id=task_id,
msg="failed to read ENGINE settings, using defaults",
exc_info=True,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Re-raise system errors before falling back to defaults.

Line 882 will also catch MemoryError and RecursionError, unlike the other broad catches in this module. That turns a non-recoverable failure in config resolution into a default fallback instead of aborting cleanly.

Suggested fix
         if self._config_resolver is not None:
             try:
                 trimming_enabled = await self._config_resolver.get_bool(
                     "engine",
                     "personality_trimming_enabled",
                 )
                 raw_override = await self._config_resolver.get_int(
                     "engine",
                     "personality_max_tokens_override",
                 )
                 if raw_override > 0:
                     tokens_override = raw_override
+            except MemoryError, RecursionError:
+                raise
             except Exception:
                 logger.warning(
                     EXECUTION_ENGINE_ERROR,
                     agent_id=agent_id,
                     task_id=task_id,
                     msg="failed to read ENGINE settings, using defaults",
                     exc_info=True,
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/agent_engine.py` around lines 870 - 889, The try/except
around config reads (_config_resolver.get_bool/get_int) currently swallows
system-level errors (e.g., MemoryError, RecursionError, KeyboardInterrupt) and
falls back to defaults; modify the except block to re-raise system/fatal
exceptions and only log/handle regular exceptions: catch Exception as e and if
isinstance(e, (MemoryError, RecursionError, KeyboardInterrupt, SystemExit)):
raise, otherwise call logger.warning(...) as before so
trimming_enabled/tokens_override fallback only occurs for non-fatal errors.

@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/synthorg/engine/agent_engine.py (1)

871-890: ⚠️ Potential issue | 🟠 Major

Don't partially apply ENGINE settings on fallback.

trimming_enabled is assigned before personality_max_tokens_override finishes resolving. If get_bool() succeeds and get_int() then raises, the warning on Line 889 says defaults are being used, but the run still keeps the non-default trimming_enabled value from the first lookup. Read both settings into temporaries and only commit them after both calls succeed.

Proposed fix
         if self._config_resolver is not None:
             try:
-                trimming_enabled = await self._config_resolver.get_bool(
+                resolved_trimming_enabled = await self._config_resolver.get_bool(
                     "engine",
                     "personality_trimming_enabled",
                 )
                 raw_override = await self._config_resolver.get_int(
                     "engine",
                     "personality_max_tokens_override",
                 )
-                if raw_override > 0:
-                    tokens_override = raw_override
+                trimming_enabled = resolved_trimming_enabled
+                tokens_override = raw_override if raw_override > 0 else None
             except MemoryError, RecursionError:
                 raise
             except Exception:
                 logger.warning(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/agent_engine.py` around lines 871 - 890, The current
logic calls self._config_resolver.get_bool(...) and assigns trimming_enabled
before calling get_int(...), causing a partial application if get_int fails;
change the code in the method containing this snippet (the agent engine
configuration load) to first read both values into local temporaries (e.g.,
tmp_trimming_enabled and tmp_raw_override using self._config_resolver.get_bool
and get_int), and only after both calls complete without exception assign
trimming_enabled and tokens_override from those temporaries (and apply the >0
check to tmp_raw_override); keep the same exception handling and logging but
ensure no state is mutated until both lookups succeed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 871-890: The current logic calls
self._config_resolver.get_bool(...) and assigns trimming_enabled before calling
get_int(...), causing a partial application if get_int fails; change the code in
the method containing this snippet (the agent engine configuration load) to
first read both values into local temporaries (e.g., tmp_trimming_enabled and
tmp_raw_override using self._config_resolver.get_bool and get_int), and only
after both calls complete without exception assign trimming_enabled and
tokens_override from those temporaries (and apply the >0 check to
tmp_raw_override); keep the same exception handling and logging but ensure no
state is mutated until both lookups succeed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4df1ed70-60db-44f8-9d72-f35a31866287

📥 Commits

Reviewing files that changed from the base of the PR and between 39ab243 and 3a0deb4.

📒 Files selected for processing (1)
  • src/synthorg/engine/agent_engine.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations.
Use except A, B: syntax (no parentheses) per PEP 758 for Python 3.14 exception handling, enforced by ruff.
All public functions and classes require type hints and Google-style docstrings. Type hints must satisfy mypy strict mode.
Use line length of 88 characters, enforced by ruff.

Files:

  • src/synthorg/engine/agent_engine.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Create new objects rather than mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
For non-Pydantic internal collections and non-frozen Pydantic models, use copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
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 allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields.
Use @computed_field for derived values instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens).
Use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) 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.
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly; never silently swallow exceptions.
Validate at system boundaries (user input, external APIs, config files).

Files:

  • src/synthorg/engine/agent_engine.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/engine/agent_engine.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__).
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and stderr print for handler construction and bootstrap.
Always use logger as the variable name (not _logger, not log).
Use event name constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly and use logger.info(EVENT, key=value).
Always use structured kwargs in logging: logger.info(EVENT, key=value) — never use logger.info("msg %s", val) style formatting.
All error paths must log at WARNING or ERROR with context before raising.
All state transitions must log at INFO level.
Use DEBUG level for object creation, internal flow, and entry/exit of key functions.
Maintain 80% minimum test coverage, enforced in CI with --cov=synthorg --cov-fail-under=80.

Files:

  • src/synthorg/engine/agent_engine.py
src/synthorg/{providers,engine}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/synthorg/engine/agent_engine.py
{src,tests}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases. Vendor names may only appear in: (1) Operations design page provider list, (2) .claude/ files, (3) third-party import paths, (4) provider presets. Tests use test-provider, test-small-001, etc.

Files:

  • src/synthorg/engine/agent_engine.py
🧠 Learnings (18)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-01T17:49:14.133Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T17:49:14.133Z
Learning: Applies to src/synthorg/{providers,engine}/**/*.py : Retryable errors are `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`; non-retryable errors raise immediately; `RetryExhaustedError` signals all retries failed

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry. `RetryExhaustedError` signals that all retries failed — the engine layer catches this to trigger fallback chains. Rate limiter respects `RateLimitError.retry_after` from providers — automatically pauses future requests.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to src/synthorg/{providers,engine}/**/*.py : `RetryExhaustedError` signals that all retries failed — the engine layer catches this to trigger fallback chains.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to src/synthorg/providers/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-31T21:07:37.469Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.469Z
Learning: Applies to src/synthorg/providers/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/engine/agent_engine.py
🔇 Additional comments (1)
src/synthorg/engine/agent_engine.py (1)

903-915: Nice trim observability payload.

This includes the right agent/task context plus before/after budget fields, which should make prompt-budget regressions much easier to trace.

Aureliolo added 10 commits April 4, 2026 13:00
…ity_tokens

Activate the previously reserved max_personality_tokens field as a hard
cap on the personality section's token count. When the personality section
exceeds the profile's budget, progressive trimming applies:

1. Tier 1: Drop behavioral enum fields (switch to condensed mode)
2. Tier 2: Truncate personality_description at word boundary
3. Tier 3: Fall back to minimal mode (communication_style only)

Token trimming is secondary to personality_mode -- the mode selects
which fields are included, the token limit enforces a hard cap.

Changes:
- Add _estimate_personality_tokens() and _trim_personality() in
  engine/_prompt_helpers.py with progressive trimming logic
- Add PersonalityTrimInfo model on SystemPrompt for callers to react
- Add personality_trimming_enabled and max_personality_tokens_override
  params to build_system_prompt() for runtime control
- Add ENGINE settings namespace with 3 settings: trimming enabled,
  notification toggle, and global token override
- Add PERSONALITY_TRIMMED WebSocket event type for dashboard notifications
- Add PROMPT_PERSONALITY_TRIMMED observability event constant
- Log trimming in agent_engine._prepare_context() when trim info present
- Update PromptProfile.max_personality_tokens docstring

Closes #1045
- Move PersonalityTrimInfo to _prompt_helpers.py (prompt.py 826->787 lines)
- Add cross-field model_validator (before > max, after <= before)
- Add @computed_field budget_met for tier-3 best-effort detection
- Split _trim_personality into tier helpers (95->45 lines each)
- Extract _format_autonomy from build_core_context (75->48 lines)
- Fix double logging: _make_trim_info logs at DEBUG, engine at INFO
- Pass trimming_enabled=False in _trim_sections re-renders (perf)
- Add _truncate_description parametrized tests (6 cases + property)
- Add PersonalityTrimInfo validation boundary tests
- Add condensed direct-entry, tier2->tier3 fallthrough, WS enum tests
- Add negative override test, build_core_context disabled test
- Add TODO for ENGINE settings wiring in agent_engine
- Update docs/design/engine.md: profiles table, trimming section
- Update CLAUDE.md: engine/ package structure

Pre-reviewed by 6 agents, 19 findings addressed
…nd CodeRabbit

Critical fixes:
- Fix personality trimming reverted during section-level re-rendering
  (_trim_sections now re-applies trimming instead of skipping it)
- Fix _truncate_description: handle empty input (return '') and
  already-fitting input (return unchanged) instead of always appending '...'
- Fix 'Soft limit' -> 'Hard cap' in PromptProfile.max_personality_tokens
  Field description to match docstring and implementation
- Remove dead personality_trimming_notify setting and
  WsEventType.PERSONALITY_TRIMMED (no code consumed them)
- Add 'engine' to frontend SettingNamespace type and display names

Major fixes:
- Thread token estimator through build_core_context/_trim_personality
  instead of hardcoding DefaultTokenEstimator (consistency with caller)
- Remove dead code: 'full' in tier 2 mode guard was unreachable
- Wire ConfigResolver into AgentEngine for ENGINE settings
  (personality_trimming_enabled, personality_max_tokens_override)
- Add WARNING log when tier 3 budget not met (budget_met=False)
- Document _try_condensed/_try_truncate_description side-effects
- Parametrize 3 identical tier-limit tests per CLAUDE.md convention
- Add test for full->tier1-fail->tier2-succeed path
- Clarify build_system_prompt docstring: <=0 override values ignored
- Add warning log for negative/zero max_personality_tokens_override
- Add yaml_path to engine settings definitions

Medium/minor fixes:
- Use consistent .get() for all ctx keys in _estimate_personality_tokens
- Fix docstring imprecisions (tier 1 only for full mode, markdown overhead)
- Add frozen immutability test for PersonalityTrimInfo
- Strengthen assertion in test_full_mode_includes_all_fields (>= 20)
- Tighten test_condensed_over_budget assertion to exact tier 2
- Update truncation test cases for new already-fits behavior
- Update engine.md to remove personality_trimming_notify reference
- Fix docs/design/engine.md: standard profile Acceptance Criteria
  column 'Bullet list' -> 'Nested list' (simplify_acceptance_criteria=False)
- Add tier 1/2 budget-met invariant to PersonalityTrimInfo validator
  (after_tokens must not exceed max_tokens for tiers 1 and 2; only
  tier 3 is best-effort)
- Include '## Personality' heading in _estimate_personality_tokens so
  budget_met reflects actual rendered section cost
- Add task_id to PROMPT_PERSONALITY_TRIMMED log in agent_engine for
  task correlation
- Propagate trimming_enabled through _trim_sections so re-renders
  honor the original personality_trimming_enabled flag instead of
  defaulting to True
ConfigResolver.get_bool/get_int raise SettingNotFoundError if keys
are absent. Wrap both calls so _prepare_context falls back to
defaults (trimming_enabled=True, tokens_override=None) instead of
crashing the agent run.
The except block for ConfigResolver failures was using
PROMPT_PERSONALITY_TRIMMED which is semantically wrong for a
settings-read failure. Use EXECUTION_ENGINE_ERROR instead.
Bare 'except Exception' swallows MemoryError and RecursionError
which inherit from Exception. Re-raise them per project convention
(PEP 758 syntax).
Read both settings into temporaries and only assign to
trimming_enabled/tokens_override in the else block after both
calls succeed. Prevents a state where get_bool succeeds (overwriting
trimming_enabled) but get_int fails (tokens_override stays default).
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/synthorg/engine/_prompt_helpers.py (1)

272-285: ⚠️ Potential issue | 🟠 Major

Don't hardcode the default char/token ratio in tier 2.

remaining * 4 only matches DefaultTokenEstimator. With any tokenizer-backed PromptTokenEstimator, this can miss a description length that actually fits and fall through to tier 3 unnecessarily.

🛠️ Suggested fix
     remaining = max_tokens - tokens_without
     if remaining > 0:
-        max_chars = remaining * 4  # Inverse of char/4 heuristic.
-        ctx["personality_description"] = _truncate_description(
-            saved_desc,
-            max_chars,
-        )
+        low = 0
+        high = len(saved_desc)
+        best = ""
+        while low <= high:
+            mid = (low + high) // 2
+            candidate = _truncate_description(saved_desc, mid)
+            ctx["personality_description"] = candidate
+            if _estimate_personality_tokens(ctx, mode, estimator) <= max_tokens:
+                best = candidate
+                low = mid + 1
+            else:
+                high = mid - 1
+        ctx["personality_description"] = best
     else:
         ctx["personality_description"] = ""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/_prompt_helpers.py` around lines 272 - 285, The code
currently uses a hardcoded char/token ratio (remaining * 4) when computing
max_chars, which only matches DefaultTokenEstimator; instead use the provided
estimator to find the largest description slice that fits remaining tokens by
replacing the fixed multiplier with logic that calls
_estimate_personality_tokens during a small binary search over description
length: keep saved_desc, set ctx["personality_description"] to a truncated
candidate via _truncate_description, call _estimate_personality_tokens(ctx,
mode, estimator) to check if it ≤ remaining, and adjust the search bounds until
you find the maximal char length that fits, then set
ctx["personality_description"] to that candidate (or "" if none fit). Ensure you
reference and use _estimate_personality_tokens, _truncate_description,
saved_desc, remaining and avoid any hardcoded ratio.
src/synthorg/settings/definitions/engine.py (1)

9-38: ⚠️ Potential issue | 🟠 Major

Add the missing personality_trimming_notify setting.

The new trimming surface still exposes only two ENGINE keys. Without personality_trimming_notify, operators cannot suppress trim notifications while leaving trimming enabled, and AgentEngine._prepare_context() has nothing to consult before emitting PROMPT_PERSONALITY_TRIMMED.

🛠️ Suggested addition
 _r.register(
     SettingDefinition(
         namespace=SettingNamespace.ENGINE,
         key="personality_trimming_enabled",
         type=SettingType.BOOLEAN,
         default="true",
         description=(
             "Enable token-based personality trimming when section exceeds budget"
         ),
         group="Personality Trimming",
         yaml_path="engine.personality_trimming_enabled",
     )
 )
 
+_r.register(
+    SettingDefinition(
+        namespace=SettingNamespace.ENGINE,
+        key="personality_trimming_notify",
+        type=SettingType.BOOLEAN,
+        default="true",
+        description="Emit personality trimming notifications when trimming occurs",
+        group="Personality Trimming",
+        yaml_path="engine.personality_trimming_notify",
+    )
+)
+
 _r.register(
     SettingDefinition(
         namespace=SettingNamespace.ENGINE,
         key="personality_max_tokens_override",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/settings/definitions/engine.py` around lines 9 - 38, Add a new
SettingDefinition named "personality_trimming_notify" in the same block as the
other ENGINE settings: use namespace SettingNamespace.ENGINE,
key="personality_trimming_notify", type=SettingType.BOOLEAN, group="Personality
Trimming", yaml_path="engine.personality_trimming_notify" and set a sensible
default (e.g., "true") so operators can toggle trim notifications independently
of trimming; ensure the new setting is read where AgentEngine._prepare_context
emits PROMPT_PERSONALITY_TRIMMED so notification behavior can be suppressed when
the setting is false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 870-893: The current logic uses one try/except around both
_config_resolver.get_bool and get_int, so a failure in get_int discards a
successful get_bool; change to resolve each ENGINE knob independently by calling
_config_resolver.get_bool("engine","personality_trimming_enabled") inside its
own try/except (re-raising MemoryError/RecursionError, logging other exceptions
and leaving trimming_enabled as the default) and then calling
_config_resolver.get_int("engine","personality_max_tokens_override") in a
separate try/except (same re-raise/log semantics) and only set tokens_override
if resolved_override > 0; keep use of trimming_enabled and tokens_override as
before.

---

Duplicate comments:
In `@src/synthorg/engine/_prompt_helpers.py`:
- Around line 272-285: The code currently uses a hardcoded char/token ratio
(remaining * 4) when computing max_chars, which only matches
DefaultTokenEstimator; instead use the provided estimator to find the largest
description slice that fits remaining tokens by replacing the fixed multiplier
with logic that calls _estimate_personality_tokens during a small binary search
over description length: keep saved_desc, set ctx["personality_description"] to
a truncated candidate via _truncate_description, call
_estimate_personality_tokens(ctx, mode, estimator) to check if it ≤ remaining,
and adjust the search bounds until you find the maximal char length that fits,
then set ctx["personality_description"] to that candidate (or "" if none fit).
Ensure you reference and use _estimate_personality_tokens,
_truncate_description, saved_desc, remaining and avoid any hardcoded ratio.

In `@src/synthorg/settings/definitions/engine.py`:
- Around line 9-38: Add a new SettingDefinition named
"personality_trimming_notify" in the same block as the other ENGINE settings:
use namespace SettingNamespace.ENGINE, key="personality_trimming_notify",
type=SettingType.BOOLEAN, group="Personality Trimming",
yaml_path="engine.personality_trimming_notify" and set a sensible default (e.g.,
"true") so operators can toggle trim notifications independently of trimming;
ensure the new setting is read where AgentEngine._prepare_context emits
PROMPT_PERSONALITY_TRIMMED so notification behavior can be suppressed when the
setting is false.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4e4dbc6c-7597-4c2e-9b2b-c211bdfed290

📥 Commits

Reviewing files that changed from the base of the PR and between 3a0deb4 and c662162.

📒 Files selected for processing (15)
  • CLAUDE.md
  • docs/design/engine.md
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/settings/enums.py
  • tests/unit/engine/test_personality_trimming.py
  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • web/src/api/types.ts
  • web/src/utils/constants.ts
📜 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). (7)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations.
Use except A, B: syntax (no parentheses) per PEP 758 for Python 3.14 exception handling, enforced by ruff.
All public functions and classes require type hints and Google-style docstrings. Type hints must satisfy mypy strict mode.
Use line length of 88 characters, enforced by ruff.

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
  • tests/unit/engine/test_personality_trimming.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Create new objects rather than mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
For non-Pydantic internal collections and non-frozen Pydantic models, use copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
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 allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields.
Use @computed_field for derived values instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens).
Use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) 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.
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly; never silently swallow exceptions.
Validate at system boundaries (user input, external APIs, config files).

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__).
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and stderr print for handler construction and bootstrap.
Always use logger as the variable name (not _logger, not log).
Use event name constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly and use logger.info(EVENT, key=value).
Always use structured kwargs in logging: logger.info(EVENT, key=value) — never use logger.info("msg %s", val) style formatting.
All error paths must log at WARNING or ERROR with context before raising.
All state transitions must log at INFO level.
Use DEBUG level for object creation, internal flow, and entry/exit of key functions.
Maintain 80% minimum test coverage, enforced in CI with --cov=synthorg --cov-fail-under=80.

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
{src,tests}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases. Vendor names may only appear in: (1) Operations design page provider list, (2) .claude/ files, (3) third-party import paths, (4) provider presets. Tests use test-provider, test-small-001, etc.

Files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/observability/events/prompt.py
  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
  • tests/unit/engine/test_personality_trimming.py
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always reuse existing components from web/src/components/ui/ before creating new ones.

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (e.g., text-foreground, bg-card, text-accent, text-success, bg-danger) or CSS variables (e.g., var(--so-accent)) for colors; never hardcode hex values in .tsx/.ts files
Use font-sans or font-mono for typography; never set fontFamily directly
Use density-aware tokens (e.g., p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing
Use token variables (e.g., var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders; never hardcode values
Import cn from @/lib/utils for conditional class merging in component files
Do not use rgba() with hardcoded values; use design token variables instead
Do not use hardcoded Framer Motion transition durations; use presets from @/lib/motion instead

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and @/lib/motion presets.

Files:

  • web/src/api/types.ts
  • web/src/utils/constants.ts
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow for test markers.
Configure asyncio_mode = "auto" in pytest — no manual @pytest.mark.asyncio needed for async tests.
Default test timeout is 30 seconds per test (global in pyproject.toml). Non-default overrides like timeout(60) are allowed, but do not add per-file pytest.mark.timeout(30) markers.
Prefer @pytest.mark.parametrize for testing similar cases.
Python uses Hypothesis for property-based testing (@given + @settings). Hypothesis profiles: ci (deterministic, max_examples=10 + derandomize=True), dev (1000 examples), fuzz (10,000 examples, no deadline), extreme (500,000 examples, no deadline). Controlled via HYPOTHESIS_PROFILE env var.
Failing Hypothesis examples are persisted to ~/.synthorg/hypothesis-examples/ via _WriteOnlyDatabase in tests/conftest.py. When Hypothesis finds a failure, it is a real bug — fix the underlying bug and add an explicit @example(...) decorator to permanently cover the case in CI.
Never skip, dismiss, or ignore flaky tests — always fix them fully. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic. For tasks that must block indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_personality_trimming.py

⚙️ CodeRabbit configuration file

Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare @settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which @given() honors automatically.

Files:

  • tests/unit/engine/test_prompt.py
  • tests/unit/settings/test_engine_settings.py
  • tests/unit/engine/test_personality_trimming.py
src/synthorg/{providers,engine}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/synthorg/engine/prompt_profiles.py
  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
  • src/synthorg/engine/agent_engine.py
🧠 Learnings (41)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)

Applied to files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/__init__.py
  • tests/unit/settings/test_engine_settings.py
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.

Applied to files:

  • src/synthorg/settings/enums.py
  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • src/synthorg/settings/definitions/engine.py
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • docs/design/engine.md
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/engine/coordination/**/*.py : Task coordination uses multi-agent pipeline with 4 dispatchers (SAS/centralized/decentralized/context-dependent), wave execution, and workspace lifecycle integration.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-02T11:49:21.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T11:49:21.202Z
Learning: Applies to **/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`) with `allow_inf_nan=False` in all `ConfigDict` declarations

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields.

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `allow_inf_nan=False` in all `ConfigDict` declarations to reject `NaN`/`Inf` in numeric fields at validation time

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.

Applied to files:

  • src/synthorg/engine/prompt.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

  • src/synthorg/engine/prompt.py
  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).

Applied to files:

  • src/synthorg/settings/definitions/engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)

Applied to files:

  • src/synthorg/engine/_prompt_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-01T17:49:14.133Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T17:49:14.133Z
Learning: Applies to src/synthorg/{providers,engine}/**/*.py : Retryable errors are `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`; non-retryable errors raise immediately; `RetryExhaustedError` signals all retries failed

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry. `RetryExhaustedError` signals that all retries failed — the engine layer catches this to trigger fallback chains. Rate limiter respects `RateLimitError.retry_after` from providers — automatically pauses future requests.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to src/synthorg/{providers,engine}/**/*.py : `RetryExhaustedError` signals that all retries failed — the engine layer catches this to trigger fallback chains.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to src/synthorg/providers/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-31T21:07:37.469Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.469Z
Learning: Applies to src/synthorg/providers/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)

Applied to files:

  • src/synthorg/engine/agent_engine.py
🔇 Additional comments (8)
src/synthorg/observability/events/prompt.py (1)

17-17: Event constant addition is clean and consistent.

Line 17 introduces a correctly scoped prompt-domain event constant and aligns with the existing observability event naming pattern.

src/synthorg/settings/enums.py (1)

22-22: Namespace extension looks correct.

Line 22 cleanly adds the ENGINE namespace in the same format as existing persisted namespaces.

CLAUDE.md (1)

99-99: Docs update aligns with the feature behavior.

Line 99 accurately reflects that engine prompt profiles now include personality trimming via max_personality_tokens.

web/src/api/types.ts (1)

1438-1449: Type union update is correct and necessary.

Adding 'engine' to SettingNamespace keeps frontend API typing in sync with the backend settings namespace set.

src/synthorg/settings/definitions/__init__.py (1)

13-13: Definitions aggregator update is complete.

Including engine in both the import list and __all__ ensures engine settings definitions are registered and exported with the rest of the namespaces.

Also applies to: 26-26

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

1221-1224: Test adjustment correctly tracks the new return contract.

The tuple unpacking and trim_info is None assertion accurately validate the profile=None default path after the build_core_context signature change.

Also applies to: 1229-1229

web/src/utils/constants.ts (1)

62-71: Settings constants are correctly synchronized with the new namespace.

Adding engine to both namespace order and display-name mapping keeps the settings UI consistent with the expanded SettingNamespace type.

Also applies to: 74-85

tests/unit/settings/test_engine_settings.py (1)

1-45: Strong targeted coverage for engine settings registration.

This test module validates namespace wiring and key metadata (type/default/bounds) for both engine settings, which is exactly the right guardrail for this feature.

Comment on lines +870 to +893
if self._config_resolver is not None:
try:
resolved_enabled = await self._config_resolver.get_bool(
"engine",
"personality_trimming_enabled",
)
resolved_override = await self._config_resolver.get_int(
"engine",
"personality_max_tokens_override",
)
except MemoryError, RecursionError:
raise
except Exception:
logger.warning(
EXECUTION_ENGINE_ERROR,
agent_id=agent_id,
task_id=task_id,
msg="failed to read ENGINE settings, using defaults",
exc_info=True,
)
else:
trimming_enabled = resolved_enabled
if resolved_override > 0:
tokens_override = resolved_override
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Resolve the ENGINE knobs independently.

A failure in the get_int() call currently discards a successful get_bool() result. That means an explicit personality_trimming_enabled=false can be silently ignored whenever personality_max_tokens_override is missing or invalid. These controls should fall back independently instead of behaving as an all-or-nothing bundle.

🛠️ Suggested fix
         if self._config_resolver is not None:
             try:
-                resolved_enabled = await self._config_resolver.get_bool(
+                trimming_enabled = await self._config_resolver.get_bool(
                     "engine",
                     "personality_trimming_enabled",
                 )
-                resolved_override = await self._config_resolver.get_int(
-                    "engine",
-                    "personality_max_tokens_override",
-                )
             except MemoryError, RecursionError:
                 raise
             except Exception:
                 logger.warning(
                     EXECUTION_ENGINE_ERROR,
                     agent_id=agent_id,
                     task_id=task_id,
-                    msg="failed to read ENGINE settings, using defaults",
+                    msg=(
+                        "failed to read ENGINE personality_trimming_enabled, "
+                        "using default"
+                    ),
                     exc_info=True,
                 )
-            else:
-                trimming_enabled = resolved_enabled
+
+            try:
+                resolved_override = await self._config_resolver.get_int(
+                    "engine",
+                    "personality_max_tokens_override",
+                )
+            except MemoryError, RecursionError:
+                raise
+            except Exception:
+                logger.warning(
+                    EXECUTION_ENGINE_ERROR,
+                    agent_id=agent_id,
+                    task_id=task_id,
+                    msg=(
+                        "failed to read ENGINE personality_max_tokens_override, "
+                        "using default"
+                    ),
+                    exc_info=True,
+                )
+            else:
                 if resolved_override > 0:
                     tokens_override = resolved_override
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/agent_engine.py` around lines 870 - 893, The current
logic uses one try/except around both _config_resolver.get_bool and get_int, so
a failure in get_int discards a successful get_bool; change to resolve each
ENGINE knob independently by calling
_config_resolver.get_bool("engine","personality_trimming_enabled") inside its
own try/except (re-raising MemoryError/RecursionError, logging other exceptions
and leaving trimming_enabled as the default) and then calling
_config_resolver.get_int("engine","personality_max_tokens_override") in a
separate try/except (same re-raise/log semantics) and only set tokens_override
if resolved_override > 0; keep use of trimming_enabled and tokens_override as
before.

@Aureliolo Aureliolo merged commit 75afd52 into main Apr 4, 2026
34 checks passed
@Aureliolo Aureliolo deleted the feat/personality-trimming branch April 4, 2026 12:28
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 4, 2026 12:28 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Apr 4, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.6.1](v0.6.0...v0.6.1)
(2026-04-04)


### Features

* capability-aware prompt profiles for model tier adaptation
([#1047](#1047))
([67650c5](67650c5)),
closes [#805](#805)
* implement procedural memory auto-generation from agent failures
([#1048](#1048))
([55f5206](55f5206)),
closes [#420](#420)
* implement quality scoring Layers 2+3 -- LLM judge and human override
([#1057](#1057))
([4a8adfe](4a8adfe)),
closes [#230](#230)
* token-based personality trimming via
PromptProfile.max_personality_tokens
([#1059](#1059))
([75afd52](75afd52)),
closes [#1045](#1045)
* workflow execution lifecycle + editor improvements
([#1058](#1058))
([7b54262](7b54262)),
closes [#1029](#1029)
[#1042](#1042)


### Refactoring

* **web:** address complexity and logging issues in dashboard
([#1056](#1056))
([ada997b](ada997b)),
closes [#1055](#1055)


### Documentation

* comprehensive documentation refresh
([#1050](#1050))
([c7a4259](c7a4259))


### Tests

* fix Hypothesis fuzzing infra and speed up slow unit tests
([#1044](#1044))
([1111602](1111602))


### Maintenance

* add text=auto catch-all to .gitattributes
([#1051](#1051))
([fc65d72](fc65d72))
* bump defu from 6.1.4 to 6.1.6 in /site
([#1062](#1062))
([f0cc439](f0cc439))

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: token-based personality trimming via PromptProfile.max_personality_tokens

2 participants