Skip to content

feat: add reporting lines, escalation paths, and workflow handoffs to templates#745

Merged
Aureliolo merged 7 commits intomainfrom
feat/template-reporting-escalation
Mar 22, 2026
Merged

feat: add reporting lines, escalation paths, and workflow handoffs to templates#745
Aureliolo merged 7 commits intomainfrom
feat/template-reporting-escalation

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Add optional subordinate_id/supervisor_id fields to ReportingLine model for disambiguating agents that share the same role name (e.g. multiple "Backend Developer" agents with different merge_ids)
  • Update self-report and unique-subordinate validators to use explicit branching (both IDs present -> compare IDs; neither -> compare names; asymmetric -> always different agents)
  • Populate reporting_lines, workflow_handoffs, and escalation_paths across 6 builtin templates:
    • startup: 3 reporting lines, 1 handoff, 2 escalations
    • dev_shop: 5 reporting lines, 2 handoffs, 2 escalations
    • research_lab: 4 reporting lines, 3 handoffs, 2 escalations
    • product_team: 5 reporting lines, 5 handoffs, 3 escalations
    • agency: 7 reporting lines, 6 handoffs (client delivery loop), 3 escalations
    • full_company: 15 reporting lines (with Jinja2 loops for dynamic devs), 8 handoffs, 5 escalations
  • Document reporting_lines with subordinate_id/supervisor_id in docs/design/organization.md
  • Budget allocations reviewed -- all existing values are reasonable for their template type (no changes needed)

Notes

  • solo_founder.yaml skipped (only 2 agents, no meaningful hierarchy)
  • full_company.yaml product department has head_role: "Product Manager" but contains CPO (c_suite) -- pre-existing issue, not addressed here
  • HierarchyResolver in communication/delegation/hierarchy.py does not yet consume the new ID fields -- tracked separately as it affects runtime hierarchy resolution in a different module

Test plan

  • 14 new unit tests for ReportingLine id fields (acceptance, self-report with ids, asymmetric cases, blank-id rejection, case-insensitive duplicate ids)
  • All 10,359 tests pass (93.68% coverage)
  • All builtin templates load and render successfully (validated by existing test_all_builtins_load_successfully and test_render_all_builtins_produce_valid_root_config)
  • mypy strict: no issues
  • ruff lint + format: clean
  • Pre-reviewed by 7 agents, 10 findings addressed

Closes #719

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 22, 2026

Dependency Review

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

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b57c4a9c-1a96-45d7-988d-9314cecbb3d3

📥 Commits

Reviewing files that changed from the base of the PR and between 6c42a8f and 6f07dea.

📒 Files selected for processing (14)
  • docs/design/organization.md
  • src/synthorg/core/company.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • tests/unit/core/conftest.py
  • tests/unit/core/test_company.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company_workflows.py
📜 Recent 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). (4)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • 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 native lazy annotations.
Use PEP 758 except syntax: 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).
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections, use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries.
Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models for runtime state that evolves. Never mix static config fields with mutable runtime fields.
Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use @computed_field for derived values. Use NotBlankStr for all identifier/name fields instead of manual whitespace validators.
Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code. Prefer structured concurrency over bare create_task.
Line length: 88 characters (ruff).
Functions: < 50 lines, files < 800 lines.
Errors: handle explicitly, never silently swallow.
Validate: at system boundaries (user input, external APIs, config files).
Variable name: always logger (not _logger, not log).

Files:

  • tests/unit/core/conftest.py
  • src/synthorg/templates/schema.py
  • src/synthorg/templates/renderer.py
  • src/synthorg/core/company.py
  • tests/unit/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Parametrize: Prefer @pytest.mark.parametrize for testing similar cases.
Tests must use test-provider, test-small-001, etc. for vendor-agnostic test code.
Property-based testing: Python uses Hypothesis (@given + @settings). Hypothesis profiles: ci (50 examples, default) and dev (1000 examples), controlled via HYPOTHESIS_PROFILE env var.
Flaky tests: NEVER skip, dismiss, or ignore -- always fix them fully and fundamentally. 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/core/conftest.py
  • tests/unit/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company.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__)
Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). 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 creation, internal flow, entry/exit of key functions.
Vendor-agnostic everywhere: 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.
Pure data models, enums, and re-exports do NOT need logging.
Use NotBlankStr from core.types for all identifier/name fields -- including optional (NotBlankStr | None) and tuple variants -- instead of manual whitespace validators.

Files:

  • src/synthorg/templates/schema.py
  • src/synthorg/templates/renderer.py
  • src/synthorg/core/company.py
src/synthorg/!(observability)/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Never use import logging / logging.getLogger() / print() in application code (exception: observability/setup.py and observability/sinks.py may use stdlib logging and print for bootstrap).

Files:

  • src/synthorg/templates/schema.py
  • src/synthorg/templates/renderer.py
  • src/synthorg/core/company.py
🧠 Learnings (22)
📓 Common learnings
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)
📚 Learning: 2026-03-22T19:37:56.694Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-22T19:37:56.694Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields -- including optional (`NotBlankStr | None`) and tuple variants -- instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.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/templates/schema.py
  • src/synthorg/core/company.py
  • tests/unit/core/test_company.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 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.py
📚 Learning: 2026-03-17T11:41:02.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T11:41:02.964Z
Learning: Applies to src/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values instead of storing + validating redundant fields. Use `NotBlankStr` for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.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 **/*.py : Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use computed_field for derived values 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/templates/schema.py
📚 Learning: 2026-03-22T19:37:56.694Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-22T19:37:56.694Z
Learning: Applies to **/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values. Use `NotBlankStr` for all identifier/name fields instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.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/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:

  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • tests/unit/core/test_company_reporting.py
  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • docs/design/organization.md
  • tests/unit/core/test_company.py
📚 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:

  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
📚 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/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
📚 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 tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases.

Applied to files:

  • tests/unit/core/test_company_workflows.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer pytest.mark.parametrize for testing similar cases.

Applied to files:

  • tests/unit/core/test_company_workflows.py
📚 Learning: 2026-03-22T19:37:56.694Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-22T19:37:56.694Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer `pytest.mark.parametrize` for testing similar cases.

Applied to files:

  • tests/unit/core/test_company_workflows.py
📚 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 docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • docs/design/organization.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:

  • docs/design/organization.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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue — DESIGN_SPEC.md is a pointer file linking to 7 design pages (Agents, Organization, Communication, Engine, Memory, Operations)

Applied to files:

  • docs/design/organization.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:

  • docs/design/organization.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/templates/**/*.py : Templates: pre-built company templates, personality presets, and builder.

Applied to files:

  • docs/design/organization.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:

  • tests/unit/core/test_company.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:

  • tests/unit/core/test_company.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Test markers: pytest.mark.unit, pytest.mark.integration, pytest.mark.e2e, pytest.mark.slow. Coverage: 80% minimum (enforced in CI).

Applied to files:

  • tests/unit/core/test_company.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 tests/**/*.py : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.

Applied to files:

  • tests/unit/core/test_company.py
🧬 Code graph analysis (3)
tests/unit/core/test_company_workflows.py (1)
src/synthorg/core/company.py (5)
  • ApprovalChain (131-170)
  • DepartmentPolicies (173-201)
  • EscalationPath (249-279)
  • ReviewRequirements (105-128)
  • WorkflowHandoff (218-246)
tests/unit/core/test_company_reporting.py (1)
src/synthorg/core/company.py (2)
  • Department (316-421)
  • ReportingLine (40-102)
tests/unit/core/test_company.py (1)
src/synthorg/core/company.py (4)
  • Department (316-421)
  • DepartmentPolicies (173-201)
  • ReviewRequirements (105-128)
  • Company (503-586)
🪛 YAMLlint (1.38.0)
src/synthorg/templates/builtins/full_company.yaml

[error] 54-54: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (27)
tests/unit/core/conftest.py (1)

131-131: LGTM!

The factory correctly declares head_id = None to align with the new optional Department.head_id field, ensuring factory-generated instances are consistent with the updated model.

src/synthorg/templates/renderer.py (1)

786-803: LGTM!

The head_merge_id handling correctly:

  1. Only populates head_id when both head_merge_id and head_role are present
  2. Logs a descriptive warning when head_merge_id is discarded due to missing head_role

This addresses the past review feedback requesting a warning for discarded head_merge_id values.

src/synthorg/templates/schema.py (1)

185-188: LGTM!

The head_merge_id field correctly uses NotBlankStr | None with default=None, ensuring whitespace-only values are rejected at schema parse time rather than during later validation. This aligns with coding guidelines for identifier fields.

src/synthorg/core/company.py (4)

25-38: LGTM!

The _identity_key helper correctly implements namespace-tagged identity comparison:

  • Returns ("id", normalized) when an explicit ID is provided
  • Returns ("name", normalized) otherwise
  • Namespacing prevents false collisions when an ID value happens to match a name from a different reporting line

60-102: LGTM!

The updated ReportingLine model correctly:

  1. Adds optional subordinate_id and supervisor_id fields using NotBlankStr | None
  2. Updates self-report validation to compare identities within the same namespace only
  3. Provides detailed error messages that include IDs when present for easier debugging

347-384: LGTM!

The Department.head_id field and _validate_head_id_requires_head validator correctly implement the constraint that head_id requires head to be set. The error message provides useful context including the head_id value and department name.


400-421: LGTM!

The updated _validate_unique_subordinates validator correctly uses _identity_key to detect duplicate subordinates, handling both name-based and ID-based identity. The "namespace:key" format in error messages clearly indicates the identity type.

src/synthorg/templates/builtins/product_team.yaml (2)

34-57: LGTM!

The department configurations correctly:

  1. Use head_merge_id: "backend-senior" matching the agent's merge_id on line 83
  2. Disambiguate the two Backend Developer agents using subordinate_id/supervisor_id
  3. Establish clear reporting hierarchies within each department

129-173: LGTM!

The workflow handoffs and escalation paths are well-structured:

  • Handoffs form a complete product development cycle with appropriate triggers and artifacts
  • Escalation paths target appropriate departments with sensible priority boosts
  • All department references match declared departments
tests/unit/core/test_company.py (2)

218-233: LGTM!

The new tests provide good coverage for Department policies:

  • test_with_policies verifies custom policy configuration is preserved
  • test_backward_compatible_policies_defaults confirms backward compatibility with default DepartmentPolicies

495-549: LGTM!

The multi-line reformatting of Company(...) constructions improves readability while preserving test semantics.

src/synthorg/templates/builtins/agency.yaml (2)

31-65: LGTM!

The department configurations correctly implement the agency hierarchy:

  • operations establishes PM and Scrum Master reporting to Project Manager
  • engineering uses head_merge_id and ID-based reporting to disambiguate Full-Stack Developer tiers (senior supervising frontend, backend, and mid-level fullstack)
  • design and creative_marketing establish clear reporting structures

146-196: LGTM!

The workflow handoffs and escalation paths model a realistic agency delivery cycle:

  • Six handoffs covering client brief → design → engineering → QA → delivery, plus marketing assets flow
  • Three escalation paths all routing to operations (the client-facing department) with appropriate priority boosts
docs/design/organization.md (4)

82-91: LGTM!

The example clearly demonstrates the new subordinate_id/supervisor_id fields for disambiguating agents with the same role name, showing both ID-based (lines 84, 87-89) and name-only (lines 90-91) reporting relationships.


117-120: LGTM!

The documentation clearly explains:

  • The head/head_id relationship and when disambiguation is needed
  • The template YAML vs runtime field name difference (head_merge_idhead_id)
  • The parallel with subordinate_id/supervisor_id in reporting_lines
  • The budget_percent clarification (task-execution budget, not provider subscriptions)

168-171: LGTM!

The CTO agent addition addresses the past review comment about the undefined agent reference. The example now has a complete agent list matching all role references in reporting_lines and head_role configurations.


192-228: LGTM!

The template example now includes:

  • Complete departments configuration with head_role, budget_percent, and reporting_lines
  • Proper use of subordinate_id for Full-Stack Developer disambiguation
  • Updated workflow_handoffs with artifacts list
  • Updated escalation_paths with priority_boost

All references are consistent with the defined agents and departments.

tests/unit/core/test_company_workflows.py (1)

1-227: LGTM!

The test file provides comprehensive coverage for the workflow-related models (ReviewRequirements, ApprovalChain, DepartmentPolicies, WorkflowHandoff, EscalationPath). All test classes properly use @pytest.mark.unit, and parametrization is appropriately applied for similar test cases as per coding guidelines.

src/synthorg/templates/builtins/full_company.yaml (2)

39-109: LGTM!

The reporting lines structure is well-organized with appropriate supervisor relationships. The dynamic developer entries correctly use subordinate_id for disambiguation, which aligns with the new ReportingLine model capabilities. The previously flagged missing Software Architect -> CTO edge has been addressed.


295-368: LGTM!

The workflow handoffs define clear cross-department transitions covering the main enterprise workflows (product→design→engineering→QA→operations, security review cycles, and analytics/marketing feedback loops). The escalation paths appropriately route critical issues to relevant departments with sensible priority_boost values within the valid 0-3 range.

src/synthorg/templates/builtins/startup.yaml (2)

31-43: LGTM!

The reporting lines correctly use subordinate_id to disambiguate the two Full-Stack Developer agents, with values matching their corresponding merge_id declarations in the agents section (fullstack-senior and fullstack-mid).


85-101: LGTM!

The workflow handoffs and escalation paths define appropriate cross-department coordination for a small startup team, with valid department pairs and priority boost values within the allowed range.

tests/unit/core/test_company_reporting.py (1)

1-350: LGTM!

Comprehensive test coverage for the new ReportingLine ID behavior and Department reporting-line validation. The tests properly cover:

  • Self-report detection with ID-aware comparison
  • Asymmetric ID presence (one side has ID, other doesn't)
  • Namespace separation between name-based and ID-based keys
  • head_id validation including the previously suggested whitespace rejection test

All test classes have @pytest.mark.unit and appropriate parametrization.

src/synthorg/templates/builtins/dev_shop.yaml (2)

31-48: LGTM!

The reporting lines correctly use subordinate_id to disambiguate the three Backend Developer agents, with values matching their corresponding merge_id declarations (backend-senior-1, backend-mid, backend-senior-2). The Frontend Developer appropriately omits an ID since there's only one instance in the template.


109-131: LGTM!

The workflow handoffs establish appropriate bidirectional coordination between engineering and QA, and the escalation paths define sensible routes for critical defects and infrastructure issues with valid priority boost values.

src/synthorg/templates/builtins/research_lab.yaml (2)

34-48: LGTM!

The head_merge_id: "analyst-primary" correctly disambiguates the department head when multiple agents share the "Data Analyst" role. The reporting lines appropriately use supervisor_id to reference the primary analyst, and the secondary Data Analyst correctly reports to the primary.


102-130: LGTM!

The workflow handoffs establish appropriate bidirectional coordination between data analytics and engineering, plus a documentation pathway to product. The escalation paths define sensible routes for infrastructure and data quality issues with valid priority boost values.


Walkthrough

Added helper _identity_key(name, id_). ReportingLine now accepts subordinate_id and supervisor_id. Department gained optional head_id and validators were updated to detect self-reporting and duplicate subordinates using namespaced identity keys; error messages reference provided IDs. TemplateDepartmentConfig adds head_merge_id and the renderer maps head_merge_id to rendered head_id when a head role is present. Multiple built-in YAML templates were populated with reporting_lines, and top-level workflow_handoffs and escalation_paths were added. Unit tests were added and existing tests updated accordingly.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main feature addition: reporting lines, escalation paths, and workflow handoffs to templates.
Description check ✅ Passed The description comprehensively explains the changes, including model updates, template modifications, test coverage, and notes about out-of-scope items.
Linked Issues check ✅ Passed The PR successfully implements all coding-related requirements from issue #719: adds subordinate_id/supervisor_id fields to ReportingLine, updates validators for ID-based disambiguation, populates reporting_lines across six templates, adds workflow_handoffs and escalation_paths, documents changes, reviews budget allocations, and includes comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #719: YAML template updates, model field additions (subordinate_id/supervisor_id), validator updates, documentation updates, and test additions. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the organizational modeling capabilities by introducing explicit reporting lines, workflow handoffs, and escalation paths within the system's templates. The core ReportingLine model was extended with optional ID fields to resolve ambiguities when multiple agents share a role, and corresponding validation logic was updated. This change allows for more detailed and accurate representation of complex organizational structures and processes, making the templates more versatile and reflective of real-world company dynamics.

Highlights

  • Enhanced ReportingLine Model: Added optional subordinate_id and supervisor_id fields to the ReportingLine model to allow disambiguation of agents sharing the same role name, improving precision in organizational definitions.
  • Updated Validation Logic: Modified the self-report and unique-subordinate validators to explicitly handle the new ID fields, ensuring correct validation when IDs are present, absent, or asymmetric.
  • Expanded Built-in Templates: Populated reporting_lines, workflow_handoffs, and escalation_paths across six built-in templates (startup, dev_shop, research_lab, product_team, agency, full_company), significantly enriching their organizational structures.
  • Documentation Update: Updated the docs/design/organization.md file to reflect the new reporting_lines structure, including the usage of subordinate_id and supervisor_id.
  • Comprehensive Testing: Introduced 14 new unit tests specifically for the ReportingLine ID fields, covering acceptance, self-report with IDs, asymmetric cases, blank ID rejection, and case-insensitive duplicate IDs, ensuring robustness of the new features.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request effectively introduces subordinate_id and supervisor_id to the ReportingLine model, enabling disambiguation of agents with the same role name. The changes are well-implemented, with corresponding updates to validators and comprehensive new unit tests that cover various edge cases. The documentation and built-in templates have also been updated accordingly. I have one suggestion for a minor refactoring in _validate_not_self_report to improve readability.

Comment on lines +55 to +65
if self.subordinate_id is not None and self.supervisor_id is not None:
sub_key = self.subordinate_id.strip().casefold()
sup_key = self.supervisor_id.strip().casefold()
elif self.subordinate_id is None and self.supervisor_id is None:
sub_key = self.subordinate.strip().casefold()
sup_key = self.supervisor.strip().casefold()
else:
# Asymmetric ID presence: one side identified by ID, the
# other by name -- they cannot be the same agent.
return self
if sub_key == sup_key:
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 logic to determine which fields to use for the self-report check can be simplified to improve readability and reduce branching. By first checking for asymmetric ID presence and returning early, the subsequent comparison logic can be streamlined.

        sub_id_present = self.subordinate_id is not None
        sup_id_present = self.supervisor_id is not None

        if sub_id_present != sup_id_present:
            # Asymmetric ID presence: one side identified by ID, the
            # other by name -- they cannot be the same agent.
            return self

        sub_key_val = self.subordinate_id if sub_id_present else self.subordinate
        sup_key_val = self.supervisor_id if sup_id_present else self.supervisor

        if sub_key_val.strip().casefold() == sup_key_val.strip().casefold():

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.

Caution

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

⚠️ Outside diff range comments (2)
docs/design/organization.md (1)

114-119: ⚠️ Potential issue | 🟡 Minor

Clarify that budget_percent only covers execution spend.

Lines 114-119 still describe budget_percent as a generic department budget split. Issue #719 asked the docs to state that this is task-execution budget, not provider subscription or seat cost, so that distinction is still missing here.

📝 Suggested wording
-- **budget_percent** -- The share of the company's total budget allocated to this department
+- **budget_percent** -- The share of the company's task-execution budget allocated to this department; it does not represent provider subscription or seat costs

As per coding guidelines, docs/design/*.md: When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

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

In `@docs/design/organization.md` around lines 114 - 119, Update the description
of budget_percent in the organization.md section so it explicitly states that
budget_percent refers only to task-execution spend (e.g., API/compute/agent-run
costs) and does not include provider subscriptions, seat/licensing costs, or
other overhead; edit the bullet for **budget_percent** (near the entries for
**head**, **teams**, and **reporting_lines**) to add a short clarifying clause
like "covers only task execution spend, not provider subscriptions or seat
costs" and ensure surrounding text still reads naturally with role names such as
head, teams, and reporting_lines untouched.
src/synthorg/core/company.py (1)

352-369: ⚠️ Potential issue | 🟠 Major

Keep name-based and ID-based duplicate checks in separate buckets.

The self-report validator now treats asymmetric ID/name references as different agents, but Lines 354-361 collapse every reporting line into one normalized string. That still flags a false duplicate when a raw subordinate name happens to equal another line’s subordinate_id (for example, subordinate="backend-1" vs. subordinate_id="backend-1"), which contradicts the new asymmetry rule.

♻️ Suggested fix
     def _validate_unique_subordinates(self) -> Self:
         """Ensure no duplicate subordinates in reporting lines."""
-        subs = [
-            (
-                r.subordinate_id.strip().casefold()
-                if r.subordinate_id is not None
-                else r.subordinate.strip().casefold()
-            )
-            for r in self.reporting_lines
-        ]
-        if len(subs) != len(set(subs)):
-            dupes = sorted(s for s, c in Counter(subs).items() if c > 1)
+        subordinate_ids = [
+            r.subordinate_id.strip().casefold()
+            for r in self.reporting_lines
+            if r.subordinate_id is not None
+        ]
+        subordinate_names = [
+            r.subordinate.strip().casefold()
+            for r in self.reporting_lines
+            if r.subordinate_id is None
+        ]
+        dupes = sorted(
+            [s for s, c in Counter(subordinate_ids).items() if c > 1]
+            + [s for s, c in Counter(subordinate_names).items() if c > 1]
+        )
+        if dupes:
             msg = (
                 f"Duplicate subordinates in reporting lines "
                 f"for department {self.name!r}: {dupes}"
             )

A regression test for a subordinate name colliding with another line’s subordinate_id would help lock this behavior in.

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

In `@src/synthorg/core/company.py` around lines 352 - 369, In
_validate_unique_subordinates, stop collapsing subordinate_id and subordinate
into one normalized string; instead build two separate normalized buckets (one
for subordinate_id values and one for subordinate name values) from
self.reporting_lines, check duplicates within each bucket independently (using
Counter or set-length comparison), and only warn/raise ValueError when
duplicates are found inside the same bucket; reference the method
_validate_unique_subordinates and the fields reporting_lines, subordinate_id,
and subordinate when making the change so name-vs-id asymmetry is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@docs/design/organization.md`:
- Around line 114-119: Update the description of budget_percent in the
organization.md section so it explicitly states that budget_percent refers only
to task-execution spend (e.g., API/compute/agent-run costs) and does not include
provider subscriptions, seat/licensing costs, or other overhead; edit the bullet
for **budget_percent** (near the entries for **head**, **teams**, and
**reporting_lines**) to add a short clarifying clause like "covers only task
execution spend, not provider subscriptions or seat costs" and ensure
surrounding text still reads naturally with role names such as head, teams, and
reporting_lines untouched.

In `@src/synthorg/core/company.py`:
- Around line 352-369: In _validate_unique_subordinates, stop collapsing
subordinate_id and subordinate into one normalized string; instead build two
separate normalized buckets (one for subordinate_id values and one for
subordinate name values) from self.reporting_lines, check duplicates within each
bucket independently (using Counter or set-length comparison), and only
warn/raise ValueError when duplicates are found inside the same bucket;
reference the method _validate_unique_subordinates and the fields
reporting_lines, subordinate_id, and subordinate when making the change so
name-vs-id asymmetry is preserved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ec2677a4-dc6c-4fcd-b37a-cc7194f1b452

📥 Commits

Reviewing files that changed from the base of the PR and between 289638f and f682885.

📒 Files selected for processing (9)
  • docs/design/organization.md
  • src/synthorg/core/company.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • tests/unit/core/test_company.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: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/organization.md
**/*.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: (no parentheses) per PEP 758 except syntax for Python 3.14.
Type hints are required on all public functions; use mypy strict mode.
Use Google-style docstrings required on public classes and functions, enforced by ruff D rules.
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and use copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization).
Never mix static config fields with mutable runtime fields in one model. Use frozen Pydantic models for config/identity and separate mutable-via-copy models for runtime state that evolves.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use @computed_field for derived values instead of storing redundant fields. Use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.

Files:

  • src/synthorg/core/company.py
  • tests/unit/core/test_company.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Create new objects instead of mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement.
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 should be less than 50 lines; files should be less than 800 lines.
Every module with business logic MUST import logger: from synthorg.observability import get_logger then logger = get_logger(__name__).
Never use import logging, logging.getLogger(), or print() in application code.
Variable name for logger is always logger (not _logger, not log).
Always use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging: 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 logging is for object creation, internal flow, and entry/exit of key functions.
Pure data models, enums, and re-exports do NOT need logging.
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases.

Files:

  • src/synthorg/core/company.py
{**/*.py,cli/**/*.go}

📄 CodeRabbit inference engine (CLAUDE.md)

Line length is 88 characters (enforced by ruff).

Files:

  • src/synthorg/core/company.py
  • tests/unit/core/test_company.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow.
Test timeout is 30 seconds per test globally in pyproject.toml—do not add per-file pytest.mark.timeout(30) markers; only non-default overrides like timeout(60) are allowed.
Prefer @pytest.mark.parametrize for testing similar cases.
In tests, use test-provider, test-small-001, etc. instead of vendor-specific names.
Use Hypothesis for property-based testing in Python with @given and @settings decorators. Use profiles: ci (50 examples, default) and dev (1000 examples).
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 slow providers), use asyncio.Event().wait() instead of asyncio.sleep(large_number)—it is cancellation-safe and carries no timing assumptions.

Files:

  • tests/unit/core/test_company.py
🧠 Learnings (3)
📚 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:

  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/product_team.yaml
📚 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:

  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
📚 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/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/startup.yaml
🧬 Code graph analysis (1)
tests/unit/core/test_company.py (1)
src/synthorg/core/company.py (2)
  • ReportingLine (25-73)
  • Department (287-370)
🪛 YAMLlint (1.38.0)
src/synthorg/templates/builtins/full_company.yaml

[error] 52-52: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (4)
src/synthorg/templates/builtins/startup.yaml (4)

31-33: LGTM!

The CTO → CEO reporting line is correctly defined within the executive department, and both roles are unique so no ID disambiguation is needed.


37-43: LGTM!

The subordinate_id values correctly match the merge_id fields defined on the corresponding agents (lines 63 and 70), properly disambiguating the two Full-Stack Developer roles.


85-91: LGTM!

The workflow handoff from engineering to product is well-defined with appropriate artifacts. For a minimal startup template, a single handoff direction is reasonable.


93-101: LGTM!

The escalation paths appropriately route blockers from engineering and product to the executive department. Both paths use consistent priority_boost: 1 values.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 22, 2026
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

🤖 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/templates/builtins/product_team.yaml`:
- Around line 31-44: The head_role field is ambiguous because both
backend-senior and backend-mid share the "Backend Developer" role; update the
template to disambiguate the team lead by adding a head_id field (e.g., head_id:
"backend-senior") alongside head_role, or alternatively replace head_role with a
specific head reference, and ensure the schema supports head_id; refer to the
head_role, head_id, backend-senior, backend-mid, and reporting_lines entries
when making the change.
🪄 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: 8b18d830-e571-4d80-9ad0-c08c63717a1e

📥 Commits

Reviewing files that changed from the base of the PR and between f682885 and 5489085.

📒 Files selected for processing (11)
  • docs/design/organization.md
  • src/synthorg/core/company.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • tests/unit/core/test_company.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company_workflows.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). (6)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (5)
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/organization.md
**/*.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: (no parentheses) per PEP 758 except syntax for Python 3.14.
Type hints are required on all public functions; use mypy strict mode.
Use Google-style docstrings required on public classes and functions, enforced by ruff D rules.
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and use copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization).
Never mix static config fields with mutable runtime fields in one model. Use frozen Pydantic models for config/identity and separate mutable-via-copy models for runtime state that evolves.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use @computed_field for derived values instead of storing redundant fields. Use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.

Files:

  • tests/unit/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company.py
  • src/synthorg/core/company.py
{**/*.py,cli/**/*.go}

📄 CodeRabbit inference engine (CLAUDE.md)

Line length is 88 characters (enforced by ruff).

Files:

  • tests/unit/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company.py
  • src/synthorg/core/company.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow.
Test timeout is 30 seconds per test globally in pyproject.toml—do not add per-file pytest.mark.timeout(30) markers; only non-default overrides like timeout(60) are allowed.
Prefer @pytest.mark.parametrize for testing similar cases.
In tests, use test-provider, test-small-001, etc. instead of vendor-specific names.
Use Hypothesis for property-based testing in Python with @given and @settings decorators. Use profiles: ci (50 examples, default) and dev (1000 examples).
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 slow providers), use asyncio.Event().wait() instead of asyncio.sleep(large_number)—it is cancellation-safe and carries no timing assumptions.

Files:

  • tests/unit/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Create new objects instead of mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement.
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 should be less than 50 lines; files should be less than 800 lines.
Every module with business logic MUST import logger: from synthorg.observability import get_logger then logger = get_logger(__name__).
Never use import logging, logging.getLogger(), or print() in application code.
Variable name for logger is always logger (not _logger, not log).
Always use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging: 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 logging is for object creation, internal flow, and entry/exit of key functions.
Pure data models, enums, and re-exports do NOT need logging.
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases.

Files:

  • src/synthorg/core/company.py
🧠 Learnings (9)
📚 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:

  • src/synthorg/templates/builtins/agency.yaml
  • docs/design/organization.md
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
📚 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/templates/**/*.py : Templates: pre-built company templates, personality presets, and builder.

Applied to files:

  • docs/design/organization.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:

  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
📚 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/templates/builtins/full_company.yaml
📚 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:

  • tests/unit/core/test_company.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:

  • tests/unit/core/test_company.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:

  • tests/unit/core/test_company.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Test markers: pytest.mark.unit, pytest.mark.integration, pytest.mark.e2e, pytest.mark.slow. Coverage: 80% minimum (enforced in CI).

Applied to files:

  • tests/unit/core/test_company.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 tests/**/*.py : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.

Applied to files:

  • tests/unit/core/test_company.py
🧬 Code graph analysis (3)
tests/unit/core/test_company_workflows.py (1)
src/synthorg/core/company.py (5)
  • ApprovalChain (131-170)
  • DepartmentPolicies (173-201)
  • EscalationPath (249-279)
  • ReviewRequirements (105-128)
  • WorkflowHandoff (218-246)
tests/unit/core/test_company_reporting.py (1)
src/synthorg/core/company.py (2)
  • Department (316-401)
  • ReportingLine (40-102)
tests/unit/core/test_company.py (1)
src/synthorg/core/company.py (4)
  • Department (316-401)
  • DepartmentPolicies (173-201)
  • ReviewRequirements (105-128)
  • Company (483-566)
🪛 YAMLlint (1.38.0)
src/synthorg/templates/builtins/full_company.yaml

[error] 52-52: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (23)
tests/unit/core/test_company.py (2)

218-232: LGTM!

The new policy tests properly verify:

  1. Custom DepartmentPolicies configuration with ReviewRequirements(min_reviewers=2)
  2. Backward-compatible default behavior when policies is omitted

These tests align with the Department model's policies field having default_factory=DepartmentPolicies.


495-549: Formatting-only changes.

The multi-line Company(...) constructor formatting improves readability without changing behavior.

src/synthorg/templates/builtins/agency.yaml (2)

31-64: LGTM!

The reporting lines correctly establish hierarchies within each department. The engineering department properly uses subordinate_id/supervisor_id to disambiguate the two Full-Stack Developer agents (fullstack-senior and fullstack-mid), with IDs matching the corresponding merge_id values in the agents section (lines 111 and 136).


145-195: LGTM!

The workflow handoffs create a logical delivery pipeline (operations → design → engineering → QA → operations) with appropriate triggers and artifacts. The escalation paths correctly route technical/QA/design issues to operations for client communication with sensible priority_boost values.

tests/unit/core/test_company_workflows.py (1)

1-239: LGTM!

This new test module provides comprehensive coverage for workflow models:

  • ReviewRequirements: defaults and custom values
  • ApprovalChain: empty approvers, min_approvals bounds, duplicate detection (case-insensitive), immutability
  • DepartmentPolicies: unique action_types across approval chains
  • WorkflowHandoff/EscalationPath: same-department rejection (case-insensitive), priority_boost bounds (0-3)

The tests properly verify the validation logic implemented in src/synthorg/core/company.py.

src/synthorg/core/company.py (3)

25-37: LGTM!

The _identity_key helper correctly implements namespace-tagged identity comparison:

  • Returns ("id", normalized_id) when an ID is provided
  • Falls back to ("name", normalized_name) otherwise
  • Namespacing prevents false collisions when an ID value coincidentally matches a name

The function is pure, well-documented, and concise.


60-102: LGTM!

The ReportingLine model updates correctly implement ID-based disambiguation:

  • subordinate_id/supervisor_id are NotBlankStr | None with sensible defaults
  • Self-report validation compares within the same namespace only (avoiding false positives across ID/name boundaries)
  • Error messages include ID values when present for easier debugging

The explicit branching logic (lines 80-85) is a sound design choice for handling asymmetric ID presence.


380-401: LGTM!

The updated _validate_unique_subordinates correctly uses _identity_key for deduplication, ensuring:

  • Same subordinate role name with different subordinate_id values are treated as distinct
  • Duplicate detection is namespace-aware (id: vs name: prefixes in error messages)
  • Case-insensitive and whitespace-trimmed comparison is preserved
docs/design/organization.md (3)

82-92: LGTM!

The example clearly demonstrates the new reporting_lines structure:

  • Basic subordinate/supervisor role name usage
  • Optional subordinate_id/supervisor_id for disambiguating agents with the same role name
  • Hierarchical relationships within the same department

118-120: LGTM!

The clarifications address Issue #719's request to document budget_percent intent:

  • Explicitly states it covers "task-execution budget" (agent compute and API costs)
  • Excludes provider subscriptions and seat licensing
  • Documents the optional ID fields for reporting_lines

187-223: LGTM!

The updated startup template example demonstrates the complete organizational structure:

  • Departments with budget_percent, head_role, and reporting_lines
  • subordinate_id usage matching agent merge_id values
  • workflow_handoffs with triggers and artifacts
  • escalation_paths with conditions and priority_boost
src/synthorg/templates/builtins/dev_shop.yaml (2)

31-48: LGTM!

The reporting lines correctly establish engineering and QA hierarchies:

  • Backend developers use subordinate_id values matching their merge_id (backend-senior-1, backend-mid, backend-senior-2 on lines 62, 69, 100)
  • Frontend developer reports directly by role name (no ID needed as it's unique)
  • QA Engineer reports to QA Lead

109-131: LGTM!

The workflow handoffs create a bidirectional code review cycle (engineering ↔ QA) with appropriate triggers and artifacts. Escalation paths correctly route critical defects to engineering and infrastructure issues to operations.

tests/unit/core/test_company_reporting.py (2)

1-141: LGTM!

Excellent coverage of ReportingLine validation:

  • Self-report rejection by name and ID (case-insensitive)
  • Same role name with different IDs accepted (correct disambiguation)
  • Asymmetric ID presence handling (subordinate-only, supervisor-only)
  • Cross-namespace non-collision (name matching other's ID is allowed)
  • @pytest.mark.parametrize appropriately used for blank ID rejection tests (lines 119-140)

143-321: LGTM!

Comprehensive coverage of Department reporting-line uniqueness validation:

  • Duplicate subordinate detection (case/whitespace insensitive)
  • Same role name with different subordinate_id values are accepted
  • Duplicate subordinate_id values are rejected
  • Mixed ID and no-ID entries coexist correctly
  • Namespace separation prevents name/ID collisions
src/synthorg/templates/builtins/full_company.yaml (2)

39-61: LGTM!

The reporting lines correctly establish hierarchies across all departments. The Jinja2 loops (lines 52-61) properly generate subordinate_id values (backend-{{ i }}, frontend-{{ i }}) that match the corresponding agent merge_id values generated in the agents section (lines 187-217).

Note: The YAMLlint error on line 52 is a false positive—YAMLlint doesn't understand Jinja2 template syntax ({% for ... %}), which is valid and will render correctly.


293-365: LGTM!

The workflow handoffs create a comprehensive cross-department delivery pipeline:

  • Product → Design → Engineering → QA → Operations (main flow)
  • Engineering ↔ Security (bidirectional for security reviews)
  • Data Analytics / Creative Marketing → Product (insights loops)

The escalation paths have appropriately differentiated priority_boost values (security vulnerabilities at 3, critical technical/QA issues at 2, operational/strategic at 1).

src/synthorg/templates/builtins/research_lab.yaml (2)

31-47: LGTM! Reporting lines correctly disambiguate duplicate roles.

The supervisor_id and subordinate_id fields properly reference the agent merge_id values (analyst-primary on line 73, analyst-secondary on line 86), enabling clear hierarchical relationships between the two Data Analyst agents.


101-129: LGTM! Workflow handoffs and escalation paths are well-structured.

The bidirectional handoffs between data_analytics and engineering reflect realistic research lab workflows, and the data_analytics → product handoff for documentation aligns with the Technical Writer role in that department.

src/synthorg/templates/builtins/startup.yaml (2)

31-43: LGTM! Reporting lines correctly use IDs to disambiguate Full-Stack Developers.

The subordinate_id values (fullstack-senior, fullstack-mid) properly match the corresponding agent merge_id fields (lines 63, 70). The cross-department supervision (CTO in executive supervising engineering agents) reflects a typical startup structure.


85-101: LGTM! Workflow and escalation paths align with startup structure.

The single handoff from engineering to product for feature review and the escalation paths to executive for blockers and scope conflicts are appropriate for a small team.

src/synthorg/templates/builtins/product_team.yaml (2)

48-56: LGTM! Design and QA reporting lines are straightforward.

Single-level hierarchies with unique role names don't require ID disambiguation.


128-172: LGTM! Comprehensive workflow cycle and prioritized escalations.

The workflow handoffs form a complete product → design → engineering → QA → product development loop. The higher priority_boost: 2 for critical QA defects appropriately reflects urgency compared to standard escalations.

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

🤖 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/organization.md`:
- Around line 187-206: The example's reporting_lines reference a missing agent
("CTO") causing inconsistency: update the example by either adding an agent
entry with id/name matching "CTO" to the agents list or change the
reporting_lines under departments->executive and departments->engineering to
reference an existing agent id/name from the agents list; specifically reconcile
the "CTO" head_role and subordinate in departments/executive.reporting_lines and
departments/engineering.head_role with the agents defined earlier so the role
referenced exists.
🪄 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: 1937fdf3-813c-4034-9790-8ba1511f958f

📥 Commits

Reviewing files that changed from the base of the PR and between 5489085 and 5393e09.

📒 Files selected for processing (7)
  • docs/design/organization.md
  • src/synthorg/core/company.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • tests/unit/core/test_company_reporting.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). (4)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Analyze (python)
🧰 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: (no parentheses) per PEP 758 except syntax for Python 3.14.
Type hints are required on all public functions; use mypy strict mode.
Use Google-style docstrings required on public classes and functions, enforced by ruff D rules.
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and use copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization).
Never mix static config fields with mutable runtime fields in one model. Use frozen Pydantic models for config/identity and separate mutable-via-copy models for runtime state that evolves.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use @computed_field for derived values instead of storing redundant fields. Use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
  • tests/unit/core/test_company_reporting.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Create new objects instead of mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement.
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 should be less than 50 lines; files should be less than 800 lines.
Every module with business logic MUST import logger: from synthorg.observability import get_logger then logger = get_logger(__name__).
Never use import logging, logging.getLogger(), or print() in application code.
Variable name for logger is always logger (not _logger, not log).
Always use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging: 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 logging is for object creation, internal flow, and entry/exit of key functions.
Pure data models, enums, and re-exports do NOT need logging.
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases.

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
{**/*.py,cli/**/*.go}

📄 CodeRabbit inference engine (CLAUDE.md)

Line length is 88 characters (enforced by ruff).

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
  • tests/unit/core/test_company_reporting.py
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/organization.md
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow.
Test timeout is 30 seconds per test globally in pyproject.toml—do not add per-file pytest.mark.timeout(30) markers; only non-default overrides like timeout(60) are allowed.
Prefer @pytest.mark.parametrize for testing similar cases.
In tests, use test-provider, test-small-001, etc. instead of vendor-specific names.
Use Hypothesis for property-based testing in Python with @given and @settings decorators. Use profiles: ci (50 examples, default) and dev (1000 examples).
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 slow providers), use asyncio.Event().wait() instead of asyncio.sleep(large_number)—it is cancellation-safe and carries no timing assumptions.

Files:

  • tests/unit/core/test_company_reporting.py
🧠 Learnings (5)
📓 Common learnings
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)
📚 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:

  • src/synthorg/templates/builtins/agency.yaml
  • docs/design/organization.md
  • src/synthorg/templates/builtins/product_team.yaml
📚 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/templates/**/*.py : Templates: pre-built company templates, personality presets, and builder.

Applied to files:

  • docs/design/organization.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Test markers: pytest.mark.unit, pytest.mark.integration, pytest.mark.e2e, pytest.mark.slow. Coverage: 80% minimum (enforced in CI).

Applied to files:

  • tests/unit/core/test_company_reporting.py
📚 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:

  • src/synthorg/templates/builtins/product_team.yaml
🧬 Code graph analysis (1)
tests/unit/core/test_company_reporting.py (1)
src/synthorg/core/company.py (2)
  • Department (316-409)
  • ReportingLine (40-102)
🔇 Additional comments (14)
src/synthorg/templates/renderer.py (1)

786-793: LGTM!

The conditional injection of head_id only when head_merge_id is truthy correctly prevents passing empty strings that would fail NotBlankStr validation on Department.head_id. The logic integrates cleanly with the schema and model changes.

src/synthorg/templates/schema.py (1)

185-188: LGTM!

The head_merge_id field correctly follows the same pattern as merge_id on TemplateAgentConfig (line 132-135), using str with empty string default to indicate "no disambiguation needed". The renderer's truthiness check filters this appropriately.

src/synthorg/templates/builtins/agency.yaml (2)

31-65: LGTM!

The reporting lines are well-structured with proper ID disambiguation for the Full-Stack Developer roles. The head_merge_id: "fullstack-senior" correctly identifies which of the two Full-Stack Developers leads the engineering department, and the reporting lines appropriately use subordinate_id/supervisor_id to establish the fullstack-mid → fullstack-senior relationship.


146-196: LGTM!

The workflow handoffs form a coherent agency delivery loop (operations → design → engineering → QA → operations) with a parallel marketing track. Escalation paths appropriately route blockers from technical departments to operations where the Project Manager can coordinate client communication.

src/synthorg/core/company.py (3)

25-38: LGTM!

The _identity_key helper cleanly separates the namespacing concern. Using distinct namespaces ("id" vs "name") prevents false collisions when an ID value happens to match a name string, which is an important edge case for robust validation.


60-102: LGTM!

The self-report validation logic correctly handles all cases:

  • Same namespace + same key → rejected as self-report
  • Different namespaces (asymmetric ID presence) → accepted as distinct agents
  • Error messages include ID context when IDs are involved

The case-insensitive comparison via casefold() and whitespace trimming via strip() ensure robust matching.


347-350: LGTM!

The head_id field follows the same pattern as the other optional ID fields. The updated _validate_unique_subordinates correctly uses namespace-tagged identity keys, and the error message format (namespace:key) provides clear debugging information.

Also applies to: 388-409

docs/design/organization.md (3)

82-92: LGTM!

The example clearly demonstrates the ID disambiguation pattern with multiple Backend Developers, showing both subordinate_id and supervisor_id usage for establishing reporting relationships.


117-120: LGTM!

Good documentation updates clarifying the head_id disambiguation mechanism, the scope of budget_percent (task-execution costs vs. subscriptions), and the optional ID fields on reporting lines.


211-223: LGTM!

The workflow_handoffs and escalation_paths examples demonstrate the cross-department governance model effectively with appropriate triggers, artifacts, conditions, and priority boosts.

tests/unit/core/test_company_reporting.py (2)

1-141: LGTM!

Comprehensive test coverage for ReportingLine validation including self-report detection (by name and by ID), case-insensitivity, asymmetric ID handling, and blank ID rejection. The parametrized test for blank IDs efficiently covers multiple edge cases.


146-340: LGTM!

Thorough coverage of Department reporting-line validation including duplicate detection by name, by ID, case/whitespace insensitivity, namespace separation (name vs ID), and head_id behavior. The tests correctly verify the namespace-tagged identity key approach.

src/synthorg/templates/builtins/product_team.yaml (2)

34-45: LGTM!

The addition of head_merge_id: "backend-senior" properly disambiguates which Backend Developer serves as the engineering department head, addressing the concern from the previous review. The reporting lines correctly establish the hierarchy with the senior developer supervising the mid-level developer, frontend developer, and full-stack developer.


129-173: LGTM!

The workflow handoffs create a coherent product development cycle (product → design → engineering → QA → product) with a data analytics feedback loop. The escalation paths appropriately route technical constraints to product and critical defects through the engineering chain.

@Aureliolo Aureliolo force-pushed the feat/template-reporting-escalation branch from 5393e09 to 30ed259 Compare March 22, 2026 19:38
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 22, 2026 19:39 — with GitHub Actions Inactive
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: 7

🤖 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/organization.md`:
- Around line 117-120: Update the "head" paragraph to explicitly state that
while runtime objects use the field head_id for disambiguation, template YAML
must use head_merge_id (which maps to the agent's merge_id) to avoid schema
errors; mention this parallels the reporting_lines subordinate_id/supervisor_id
pattern and clarify that head_merge_id in templates becomes head_id at runtime.

In `@src/synthorg/core/company.py`:
- Around line 347-350: The Department model currently allows head_id to be set
without a corresponding head object; add a validation rule on the Department
class (e.g., a `@root_validator` or `@validator`) that if head_id is not None then
head must be provided and its identifier must match head_id (or at minimum head
is non-null); update the validator to raise a ValidationError with a clear
message referencing head_id/head when the check fails so Department(name=...,
head_id=...) cannot validate without providing head.

In `@src/synthorg/templates/builtins/full_company.yaml`:
- Around line 47-61: Add a reporting line that connects the engineering lead to
the department head: inside the reporting_lines block in
src/synthorg/templates/builtins/full_company.yaml, create an entry with
subordinate: "Software Architect" and supervisor: "CTO" (optionally include a
subordinate_id if you use ids elsewhere) so the Software Architect is linked up
to the CTO and the engineering subtree is attached to the department head.

In `@src/synthorg/templates/builtins/research_lab.yaml`:
- Around line 34-37: The template entry with name "data_analytics" uses
head_role: "Data Analyst" which is ambiguous because there are two agents
("analyst-primary" and "analyst-secondary"); update the "data_analytics" block
to add a head_merge_id pointing to the intended agent's merge id (e.g.,
"analyst-primary" or "analyst-secondary") so the head identity is explicit and
matches the ID-aware reporting_lines structure; ensure head_role remains "Data
Analyst" if you want the title preserved but the head_merge_id disambiguates
which agent instance is the head.

In `@src/synthorg/templates/renderer.py`:
- Around line 786-793: The code currently adds dept_dict["head_id"] when
head_merge_id is present even if head_role was missing and fell back to
dept_name; change the guard so head_id is only set when head_merge_id is present
AND a real head role was provided (i.e., head_role is non-empty and not the
fallback dept_name). Update the conditional around head_merge_id to check both
head_merge_id and head_role != dept_name (and head_role truthy) before assigning
dept_dict["head_id"] so you don't manufacture bogus department heads.

In `@src/synthorg/templates/schema.py`:
- Around line 185-188: Replace the plain string Field for head_merge_id with a
validated NotBlankStr type so whitespace-only values are rejected at schema
parse time; specifically change the Field declaration for head_merge_id in the
Template schema to use core.types.NotBlankStr (importing it where needed) and
keep the same default/description semantics so identifier validation occurs at
the schema boundary instead of later in render/root-config validation.

In `@tests/unit/core/test_company_workflows.py`:
- Around line 203-221: Tests test_priority_boost_above_3_rejected and
test_priority_boost_negative_rejected duplicate the same pattern; replace them
with a single parametrized test using pytest.mark.parametrize to iterate invalid
priority_boost values (e.g., 4 and -1) and assert EscalationPath(...) raises
ValidationError, referencing the test function name (e.g.,
test_priority_boost_invalid_rejected) and the EscalationPath and ValidationError
symbols so the test remains clear and DRY.
🪄 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: f9572175-9140-4d3c-b5a4-d45125155363

📥 Commits

Reviewing files that changed from the base of the PR and between 5393e09 and 30ed259.

📒 Files selected for processing (13)
  • docs/design/organization.md
  • src/synthorg/core/company.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • tests/unit/core/test_company.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company_workflows.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). (4)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • 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 native lazy annotations.
Use PEP 758 except syntax: 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).
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections, use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries.
Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models for runtime state that evolves. Never mix static config fields with mutable runtime fields.
Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use @computed_field for derived values. Use NotBlankStr for all identifier/name fields instead of manual whitespace validators.
Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code. Prefer structured concurrency over bare create_task.
Line length: 88 characters (ruff).
Functions: < 50 lines, files < 800 lines.
Errors: handle explicitly, never silently swallow.
Validate: at system boundaries (user input, external APIs, config files).
Variable name: always logger (not _logger, not log).

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
  • tests/unit/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company.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__)
Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). 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 creation, internal flow, entry/exit of key functions.
Vendor-agnostic everywhere: 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.
Pure data models, enums, and re-exports do NOT need logging.
Use NotBlankStr from core.types for all identifier/name fields -- including optional (NotBlankStr | None) and tuple variants -- instead of manual whitespace validators.

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
src/synthorg/!(observability)/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Never use import logging / logging.getLogger() / print() in application code (exception: observability/setup.py and observability/sinks.py may use stdlib logging and print for bootstrap).

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Parametrize: Prefer @pytest.mark.parametrize for testing similar cases.
Tests must use test-provider, test-small-001, etc. for vendor-agnostic test code.
Property-based testing: Python uses Hypothesis (@given + @settings). Hypothesis profiles: ci (50 examples, default) and dev (1000 examples), controlled via HYPOTHESIS_PROFILE env var.
Flaky tests: NEVER skip, dismiss, or ignore -- always fix them fully and fundamentally. 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/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company.py
🧠 Learnings (13)
📓 Common learnings
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)
📚 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:

  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • docs/design/organization.md
  • src/synthorg/templates/builtins/research_lab.yaml
📚 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:

  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
📚 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/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
📚 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:

  • tests/unit/core/test_company.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:

  • tests/unit/core/test_company.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:

  • tests/unit/core/test_company.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Test markers: pytest.mark.unit, pytest.mark.integration, pytest.mark.e2e, pytest.mark.slow. Coverage: 80% minimum (enforced in CI).

Applied to files:

  • tests/unit/core/test_company.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 tests/**/*.py : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.

Applied to files:

  • tests/unit/core/test_company.py
📚 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 docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • docs/design/organization.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:

  • docs/design/organization.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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue — DESIGN_SPEC.md is a pointer file linking to 7 design pages (Agents, Organization, Communication, Engine, Memory, Operations)

Applied to files:

  • docs/design/organization.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/templates/**/*.py : Templates: pre-built company templates, personality presets, and builder.

Applied to files:

  • docs/design/organization.md
🧬 Code graph analysis (4)
src/synthorg/templates/renderer.py (1)
src/synthorg/memory/org/store.py (2)
  • get (82-95)
  • get (349-379)
tests/unit/core/test_company_workflows.py (1)
src/synthorg/core/company.py (5)
  • ApprovalChain (131-170)
  • DepartmentPolicies (173-201)
  • EscalationPath (249-279)
  • ReviewRequirements (105-128)
  • WorkflowHandoff (218-246)
tests/unit/core/test_company_reporting.py (1)
src/synthorg/core/company.py (2)
  • Department (316-409)
  • ReportingLine (40-102)
tests/unit/core/test_company.py (1)
src/synthorg/core/company.py (4)
  • Department (316-409)
  • DepartmentPolicies (173-201)
  • ReviewRequirements (105-128)
  • Company (491-574)
🪛 YAMLlint (1.38.0)
src/synthorg/templates/builtins/full_company.yaml

[error] 52-52: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (5)
tests/unit/core/test_company_workflows.py (1)

17-200: Good workflow-model validation coverage.

This suite exercises defaults, bounds, duplicate detection, same-department rejection, and frozen-model behavior with clear assertions.

Also applies to: 223-239

src/synthorg/templates/builtins/startup.yaml (1)

31-43: Startup template hierarchy and cross-department flow look solid.

Reporting lines and new handoff/escalation paths are consistent with the defined departments/agents and correctly disambiguate duplicate Full-Stack Developer roles via IDs.

Also applies to: 85-101

tests/unit/core/test_company_reporting.py (1)

14-340: Excellent ID-aware reporting-line test coverage.

The cases for symmetric/asymmetric ID presence, case-insensitive collisions, namespace separation, and duplicate subordinate detection are comprehensive and directly validate the intended model behavior.

src/synthorg/templates/builtins/research_lab.yaml (1)

101-129: Workflow handoffs and escalations are well-structured.

The added cross-department handoffs and escalation paths are coherent and reference valid departments with bounded priority_boost values.

src/synthorg/templates/builtins/product_team.yaml (1)

34-57: Nice update—this template now has clear hierarchy and realistic workflow routing.

head_merge_id resolves the duplicated Backend Developer head ambiguity, and the added reporting lines/handoffs/escalations are internally consistent.

Also applies to: 129-173

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/templates/renderer.py`:
- Around line 786-793: The code currently drops dept["head_merge_id"] when
dept.get("head_role") is falsy without notifying the user; update the logic
around head_merge_id (using the variables head_merge_id and
dept.get("head_role") and the dept_dict population) to log a warning when
head_merge_id is present but head_role is missing/falsey — mirror the existing
missing head_role warning behavior at the earlier check (same logging
style/location) so that custom template authors are alerted whenever
head_merge_id is discarded instead of silently ignored.

In `@tests/unit/core/test_company_reporting.py`:
- Around line 337-345: Add a new unit test to validate that whitespace-only
head_id values are rejected: create a test function similar to
test_blank_head_id_rejected (e.g., test_whitespace_head_id_rejected) that
constructs Department(name="eng", head="cto", head_id="  ") and asserts it
raises a ValidationError matching a "whitespace-only" or equivalent message;
reference the Department model and existing tests test_blank_head_id_rejected
and test_head_id_without_head_rejected to place and name the new test
consistently.

In `@tests/unit/core/test_company_workflows.py`:
- Around line 150-167: The two tests test_same_department_rejected and
test_same_department_case_insensitive duplicate the same assertion pattern
against the WorkflowHandoff model; replace them with a single parametrized test
using `@pytest.mark.parametrize` to iterate cases like ("eng","eng") and
("Eng","eng") and assert a ValidationError with "different departments" when
constructing WorkflowHandoff(from_department=..., to_department=...,
trigger="test"); keep the same match string and test name (e.g.,
test_same_department_rejected_parametrized) so intent remains clear.
- Around line 79-93: Combine the two duplicate-approver tests into a single
parametrized test using pytest.mark.parametrize to cover both ("lead","lead")
and ("Lead","lead") cases; replace the separate
test_duplicate_approvers_rejected and test_duplicate_approvers_case_insensitive
with one function (e.g., test_duplicate_approvers_rejected_param) that takes an
approvers tuple parameter, constructs ApprovalChain(action_type="deploy",
approvers=approvers), and asserts a ValidationError with match "Duplicate
approvers".
- Around line 214-230: Combine the two duplicate tests into a single
parametrized test using pytest.mark.parametrize: replace
test_same_department_rejected and test_same_department_case_insensitive with one
test (e.g., test_same_department_rejected_param) that parametrizes input pairs
for from_department/to_department (like ("eng","eng") and ("Eng","eng")) and
asserts EscalationPath(...) raises ValidationError with "different departments";
locate the assertions around EscalationPath construction and reuse the same
match string to keep behavior identical.
🪄 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: b78f1bc7-dead-4356-a2de-a6309003fd94

📥 Commits

Reviewing files that changed from the base of the PR and between 30ed259 and 6c42a8f.

📒 Files selected for processing (9)
  • docs/design/organization.md
  • src/synthorg/core/company.py
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • tests/unit/core/conftest.py
  • tests/unit/core/test_company_reporting.py
  • tests/unit/core/test_company_workflows.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: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Test (Python 3.14)
  • 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 native lazy annotations.
Use PEP 758 except syntax: 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).
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections, use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries.
Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models for runtime state that evolves. Never mix static config fields with mutable runtime fields.
Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use @computed_field for derived values. Use NotBlankStr for all identifier/name fields instead of manual whitespace validators.
Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code. Prefer structured concurrency over bare create_task.
Line length: 88 characters (ruff).
Functions: < 50 lines, files < 800 lines.
Errors: handle explicitly, never silently swallow.
Validate: at system boundaries (user input, external APIs, config files).
Variable name: always logger (not _logger, not log).

Files:

  • tests/unit/core/conftest.py
  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • tests/unit/core/test_company_workflows.py
  • src/synthorg/core/company.py
  • tests/unit/core/test_company_reporting.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Parametrize: Prefer @pytest.mark.parametrize for testing similar cases.
Tests must use test-provider, test-small-001, etc. for vendor-agnostic test code.
Property-based testing: Python uses Hypothesis (@given + @settings). Hypothesis profiles: ci (50 examples, default) and dev (1000 examples), controlled via HYPOTHESIS_PROFILE env var.
Flaky tests: NEVER skip, dismiss, or ignore -- always fix them fully and fundamentally. 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/core/conftest.py
  • tests/unit/core/test_company_workflows.py
  • tests/unit/core/test_company_reporting.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__)
Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). 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 creation, internal flow, entry/exit of key functions.
Vendor-agnostic everywhere: 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.
Pure data models, enums, and re-exports do NOT need logging.
Use NotBlankStr from core.types for all identifier/name fields -- including optional (NotBlankStr | None) and tuple variants -- instead of manual whitespace validators.

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
src/synthorg/!(observability)/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Never use import logging / logging.getLogger() / print() in application code (exception: observability/setup.py and observability/sinks.py may use stdlib logging and print for bootstrap).

Files:

  • src/synthorg/templates/renderer.py
  • src/synthorg/templates/schema.py
  • src/synthorg/core/company.py
🧠 Learnings (17)
📚 Learning: 2026-03-22T19:37:56.694Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-22T19:37:56.694Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields -- including optional (`NotBlankStr | None`) and tuple variants -- instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.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/templates/schema.py
  • src/synthorg/core/company.py
📚 Learning: 2026-03-17T11:41:02.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T11:41:02.964Z
Learning: Applies to src/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values instead of storing + validating redundant fields. Use `NotBlankStr` for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.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 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.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 **/*.py : Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use computed_field for derived values 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/templates/schema.py
📚 Learning: 2026-03-22T19:37:56.694Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-22T19:37:56.694Z
Learning: Applies to **/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values. Use `NotBlankStr` for all identifier/name fields instead of manual whitespace validators.

Applied to files:

  • src/synthorg/templates/schema.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 tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases.

Applied to files:

  • tests/unit/core/test_company_workflows.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer pytest.mark.parametrize for testing similar cases.

Applied to files:

  • tests/unit/core/test_company_workflows.py
📚 Learning: 2026-03-22T19:37:56.694Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-22T19:37:56.694Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer `pytest.mark.parametrize` for testing similar cases.

Applied to files:

  • tests/unit/core/test_company_workflows.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/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:

  • docs/design/organization.md
  • src/synthorg/templates/builtins/full_company.yaml
  • tests/unit/core/test_company_reporting.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 docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations

Applied to files:

  • docs/design/organization.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 docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • docs/design/organization.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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue — DESIGN_SPEC.md is a pointer file linking to 7 design pages (Agents, Organization, Communication, Engine, Memory, Operations)

Applied to files:

  • docs/design/organization.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:

  • docs/design/organization.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/templates/**/*.py : Templates: pre-built company templates, personality presets, and builder.

Applied to files:

  • docs/design/organization.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:

  • src/synthorg/templates/builtins/full_company.yaml
📚 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/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
🧬 Code graph analysis (1)
tests/unit/core/test_company_reporting.py (1)
src/synthorg/core/company.py (2)
  • Department (316-421)
  • ReportingLine (40-102)
🪛 YAMLlint (1.38.0)
src/synthorg/templates/builtins/full_company.yaml

[error] 54-54: syntax error: found character '%' that cannot start any token

(syntax)

🔇 Additional comments (20)
src/synthorg/core/company.py (5)

374-384: LGTM! Addresses the requirement that head_id requires head.

This validator correctly rejects configurations where head_id is set without a corresponding head, preventing impossible states for downstream hierarchy consumers.


25-37: LGTM! Well-designed identity key helper.

The namespace-tagging approach (("id", ...) vs ("name", ...)) correctly prevents false collisions when an ID value happens to match a name from a different entry. The docstring clearly explains the rationale.


60-67: LGTM! Proper use of NotBlankStr | None for optional ID fields.

The optional ID fields correctly use NotBlankStr | None per coding guidelines, ensuring whitespace-only values are rejected at the model boundary.


69-102: LGTM! Robust self-report validation with namespace awareness.

The validator correctly:

  1. Computes identity keys for both sides
  2. Short-circuits when namespaces differ (legitimate asymmetric ID configurations)
  3. Provides context-aware error messages including IDs when present

400-421: LGTM! Duplicate subordinate detection using identity keys.

The updated validator correctly uses _identity_key for namespace-tagged duplicate detection, and the error message format (ns:key) provides clear diagnostic output.

src/synthorg/templates/schema.py (1)

165-188: LGTM! Proper validation for head_merge_id at schema boundary.

The field correctly uses NotBlankStr | None with default=None, ensuring whitespace-only values are rejected during the first parsing pass rather than deferring validation to render time.

docs/design/organization.md (2)

117-121: LGTM! Clear documentation of head_merge_idhead_id mapping.

The documentation now explicitly explains that template YAML uses head_merge_id while runtime objects use head_id, paralleling the subordinate_id/supervisor_id pattern in reporting_lines.


82-91: LGTM! Documentation example now complete.

The example correctly demonstrates:

  1. Reporting lines with optional ID disambiguation (subordinate_id/supervisor_id)
  2. A CTO agent definition that matches the department references

Also applies to: 168-172

src/synthorg/templates/builtins/research_lab.yaml (2)

34-48: LGTM! Proper disambiguation with head_merge_id and ID-aware reporting lines.

The data_analytics department now correctly:

  1. Uses head_merge_id: "analyst-primary" to disambiguate the department head
  2. Uses supervisor_id: "analyst-primary" in reporting lines to reference the correct Data Analyst instance

102-130: LGTM! Well-structured cross-department workflows.

The workflow_handoffs and escalation_paths define realistic bidirectional flows between data_analytics, engineering, and product with appropriate triggers, artifacts, and priority boosts.

src/synthorg/templates/builtins/full_company.yaml (2)

47-63: LGTM! Engineering hierarchy now complete with Jinja2-generated reporting lines.

The reporting lines correctly:

  1. Connect Software Architect → CTO (addressing prior feedback)
  2. Use Jinja2 loops to generate dynamic subordinate_id values matching agent merge_id patterns (backend-{{ i }}, frontend-{{ i }})

The YAMLlint error about % on line 54 is a false positive — the {% for %} syntax is valid Jinja2 that will be processed before YAML parsing.


295-368: LGTM! Comprehensive enterprise workflow definitions.

The workflow_handoffs model a realistic product pipeline (product → design → engineering → QA → operations) plus security review loops, while escalation_paths provide appropriate priority boosts (1-3) for different severity levels.

tests/unit/core/conftest.py (1)

128-134: LGTM! Factory updated to match model changes.

Adding head_id = None keeps the DepartmentFactory aligned with the new optional Department.head_id field.

tests/unit/core/test_company_reporting.py (2)

14-141: LGTM! Comprehensive ReportingLine test coverage.

The tests thoroughly cover:

  • Valid cases and self-report rejection (case-insensitive)
  • ID handling: defaults, same role with different IDs, same ID rejection
  • Asymmetric ID configurations (one side has ID, other doesn't)
  • Namespace collision prevention (name matching other side's ID)
  • Blank/whitespace ID rejection via parametrized tests

146-322: LGTM! Thorough Department reporting-line validation tests.

The tests comprehensively cover:

  • Basic reporting lines and backward compatibility (empty tuple default)
  • Duplicate subordinate detection (by name, case-insensitive, whitespace-insensitive)
  • ID-based duplicate detection and case insensitivity
  • Mixed ID/no-ID entries
  • Namespace isolation (name matching another entry's ID)
  • head_id acceptance, defaults, and validation
tests/unit/core/test_company_workflows.py (5)

1-37: LGTM! Clean test structure and comprehensive coverage.

The module is well-organized with proper imports, type hints, docstrings, and test markers. The TestReviewRequirements class appropriately tests both default values and custom configurations.


39-78: LGTM! Thorough validation testing.

The tests for ApprovalChain properly verify valid construction and enforce validation rules (empty approvers, min_approvals constraints, and special handling of zero).


95-129: LGTM! Proper immutability and policy validation testing.

The frozen test correctly verifies immutability, and TestDepartmentPolicies appropriately tests defaults and duplicate action_type validation.


139-148: LGTM! Proper valid handoff test.

The test appropriately verifies that a valid WorkflowHandoff can be constructed and checks the expected field values.


176-212: LGTM! Excellent priority_boost test coverage.

The EscalationPath tests properly verify valid construction, boundary values (0 and 3), and parametrized rejection of invalid values. The parametrization at line 203 correctly addresses the past review comment.

Aureliolo and others added 6 commits March 22, 2026 21:02
… templates

Add subordinate_id/supervisor_id optional fields to ReportingLine for
disambiguating agents that share the same role name (e.g. multiple
"Backend Developer" agents with different merge_ids). Update self-report
and unique-subordinate validators to use ids when present.

Populate reporting_lines, workflow_handoffs, and escalation_paths across
6 builtin templates (startup, dev_shop, research_lab, product_team,
agency, full_company). Solo founder skipped (only 2 agents).

Key additions per template:
- startup: 3 reporting lines, 1 handoff, 2 escalations
- dev_shop: 5 reporting lines, 2 handoffs, 2 escalations
- research_lab: 4 reporting lines, 3 handoffs, 2 escalations
- product_team: 5 reporting lines, 5 handoffs, 3 escalations
- agency: 7 reporting lines, 6 handoffs, 3 escalations
- full_company: 15 reporting lines (with Jinja2 loops for dynamic
  backend/frontend devs), 8 handoffs, 5 escalations

Closes #719

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Use explicit branching instead of `or` fallback when comparing
subordinate/supervisor identity keys. The `or` pattern mixed ID and name
namespaces, which could produce false self-report rejections when only
one side carries an ID. Now: both IDs present -> compare IDs; neither
present -> compare names; asymmetric -> always different agents.

Also: replace redundant test with asymmetric-ID and blank-ID edge cases,
add case-insensitive duplicate subordinate_id test, document
reporting_lines in docs/design/organization.md, fix docstring spacing.

Pre-reviewed by 7 agents, 10 findings addressed.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
… Gemini

- Fix cross-namespace collision in _validate_unique_subordinates by
  using namespace-tagged (ns, key) tuples instead of flat strings
- Extract _identity_key() helper to eliminate duplicated identity
  resolution logic between validators
- Conditionally format self-report error message (omit id=None noise)
- Reword asymmetric ID comment to describe design decision, not
  impossibility assertion
- Split test_company.py (1045 lines) into three files under 800 lines:
  test_company.py (578), test_company_reporting.py (321),
  test_company_workflows.py (239)
- Add cross-namespace collision test, reverse asymmetric ID test
- Parametrize blank-ID rejection tests with match= assertions
- Document budget_percent as task-execution budget in organization.md
- Fix Template Structure example: add departments section, correct
  department names, add artifacts and priority_boost fields
- Add supervisor_id to Department Configuration YAML example
- Update merge_id docstring reference to be layer-neutral

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Add head_id to Department model and head_merge_id to template schema
to resolve ambiguity when multiple agents share the head_role name.
The renderer passes head_merge_id through as head_id on Department.

Applied to product_team (backend-senior) and agency (fullstack-senior)
templates where head_role matches multiple agents.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The example referenced "CTO" in departments (head_role, reporting
lines) but did not include a CTO agent in the agents list.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Add validator: head_id requires head to be non-null on Department
- Add Software Architect -> CTO reporting line in full_company.yaml
  (connects engineering subtree to department head)
- Add head_merge_id to research_lab.yaml data_analytics department
  (disambiguates which Data Analyst is head)
- Guard renderer: only set head_id when head_role was explicitly
  provided (not the dept_name fallback)
- Change head_merge_id schema type from str to NotBlankStr | None
  (rejects whitespace-only values at parse time)
- Document head_merge_id -> head_id mapping in organization.md
- Parametrize priority_boost invalid-value tests

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Log warning when head_merge_id is present but head_role is missing
  (renderer silently discarded it before)
- Add whitespace-only head_id rejection test
- Parametrize duplicate-approver tests (exact + case-insensitive)
- Parametrize same-department tests for WorkflowHandoff and
  EscalationPath (exact + case-insensitive)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@Aureliolo Aureliolo force-pushed the feat/template-reporting-escalation branch from 6c42a8f to 6f07dea Compare March 22, 2026 20:02
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 22, 2026 20:03 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit c374cc9 into main Mar 22, 2026
30 checks passed
@Aureliolo Aureliolo deleted the feat/template-reporting-escalation branch March 22, 2026 20:08
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 22, 2026 20:08 — with GitHub Actions Inactive
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 22, 2026

Codecov Report

❌ Patch coverage is 93.33333% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.28%. Comparing base (8f871a4) to head (6f07dea).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/templates/renderer.py 60.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #745   +/-   ##
=======================================
  Coverage   92.28%   92.28%           
=======================================
  Files         573      573           
  Lines       29664    29690   +26     
  Branches     2871     2877    +6     
=======================================
+ Hits        27374    27398   +24     
- Misses       1810     1811    +1     
- Partials      480      481    +1     

☔ 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.

Aureliolo added a commit that referenced this pull request Mar 22, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.4.8](v0.4.7...v0.4.8)
(2026-03-22)


### Features

* add auto_cleanup config and improve update UX
([#741](#741))
([289638f](289638f))
* add reporting lines, escalation paths, and workflow handoffs to
templates ([#745](#745))
([c374cc9](c374cc9))
* differentiate template operational configs
([#742](#742))
([9b48345](9b48345))
* diversify personality preset assignments across templates
([#743](#743))
([15487a5](15487a5))
* improve template metadata -- skill taxonomy, descriptions, tags, and
display names ([#752](#752))
([f333f24](f333f24))


### Bug Fixes

* resolve log analysis findings (Ollama prefix, logging, init)
([#748](#748))
([8f871a4](8f871a4))
* use git tag for dev release container image tags
([#749](#749))
([f30d071](f30d071))
* use subordinate_id/supervisor_id in HierarchyResolver
([#751](#751))
([118235b](118235b))


### Performance

* add long-lived cache headers for content-hashed static assets
([#747](#747))
([4d350b5](4d350b5))
* use worksteal distribution for pytest-xdist
([#750](#750))
([b7dd7de](b7dd7de))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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: add reporting lines, escalation paths, and workflow handoffs to templates

1 participant