Skip to content

feat: implement Kanban board and Agile sprints workflow types#960

Merged
Aureliolo merged 4 commits intomainfrom
feat/workflow-types
Apr 1, 2026
Merged

feat: implement Kanban board and Agile sprints workflow types#960
Aureliolo merged 4 commits intomainfrom
feat/workflow-types

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Add Kanban board workflow type (feat: implement Kanban board workflow type #239): 5-column state machine (Backlog/Ready/In Progress/Review/Done), bidirectional TaskStatus bridge with module-level consistency guards, column transition validation with status path resolution, per-column WIP limits with strict/advisory enforcement
  • Add Agile sprints workflow type (feat: implement Agile sprints workflow type #240): strictly-linear SprintStatus lifecycle (Planning/Active/In Review/Retrospective/Completed), Sprint model with date/task/story-point tracking, immutable sprint backlog management, velocity tracking with rolling averages, ceremony config integrating MeetingProtocolType/MeetingFrequency
  • Add shared infrastructure: WorkflowType enum (4 members matching design spec), WorkflowConfig umbrella model in RootConfig, CompanyTemplate.workflow field typed as WorkflowType enum, observability event constants for both workflow types
  • 156 unit tests with Hypothesis property tests, comprehensive edge case coverage

Review coverage

Pre-reviewed by 7 agents (code-reviewer, type-design-analyzer, conventions-enforcer, logging-audit, test-analyzer, issue-resolution-verifier, docs-consistency). 13 findings addressed: error-path logging, NotBlankStr parameter types, MappingProxyType wrapping, function length extraction, completeness guards, CLAUDE.md and design spec updates.

Test plan

  • uv run ruff check src/ tests/ -- all checks passed
  • uv run ruff format src/ tests/ -- all formatted
  • uv run mypy src/ tests/ -- no issues found
  • uv run python -m pytest tests/unit/engine/workflow/ -m unit -n auto -- 156 passed
  • Pre-push hooks (mypy + pytest unit suite) -- passed
  • CI pipeline (lint + type-check + test + coverage)

Closes #239
Closes #240

🤖 Generated with Claude Code

Aureliolo and others added 3 commits April 1, 2026 10:56
Add workflow type infrastructure layered on top of the existing task
lifecycle state machine:

Kanban Board (#239):
- KanbanColumn enum (Backlog/Ready/In Progress/Review/Done)
- Column-to-TaskStatus bridge mappings with module-level guards
- Column transition validation and task status path resolution
- WIP limits with hard/advisory enforcement modes
- KanbanConfig with per-column WIP limit configuration

Agile Sprints (#240):
- SprintStatus enum with strictly linear lifecycle
  (Planning -> Active -> In Review -> Retrospective -> Completed)
- Sprint model with date, task, and story point tracking
- Sprint backlog management (add/remove/complete tasks)
- Velocity tracking with rolling average calculation
- SprintConfig with ceremony definitions integrating MeetingProtocolType

Shared infrastructure:
- WorkflowType enum (sequential_pipeline, parallel_execution, kanban,
  agile_kanban) matching the Engine design spec
- WorkflowConfig umbrella model added to RootConfig
- CompanyTemplate.workflow field typed as WorkflowType enum
- Observability events for both workflow types
- 156 unit tests with Hypothesis property tests

Closes #239
Closes #240

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The workflow field changed from NotBlankStr to WorkflowType, requiring
an additional type: ignore[assignment] comment in the frozen-field test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 7 agents, 13 findings addressed:

- Add error-path logging (WARNING) before all ValueError raises
  across kanban_columns, sprint_lifecycle, sprint_backlog,
  sprint_velocity (CLAUDE.md logging rule 6)
- Add SPRINT_TASK_COMPLETED event and success logging to
  complete_task_in_sprint
- Change task_id parameter type from str to NotBlankStr in
  sprint_backlog functions (prevents blank ID bypass via model_copy)
- Document that remove_task_from_sprint does not adjust story
  point totals (per-task point data not stored on Sprint model)
- Wrap VALID_COLUMN_TRANSITIONS and VALID_SPRINT_TRANSITIONS in
  MappingProxyType for immutability
- Add module-load completeness guard for _COLUMN_MOVE_STATUS_PATH
- Document backward column moves (READY/IN_PROGRESS->BACKLOG)
  ending in BLOCKED (off-board) as known limitation
- Extract _find_column_limit helper to bring check_wip_limit under
  50-line limit; extract _validate_completion_preconditions helper
- Log WIP limit exceeded on strict rejection path (was silent)
- Remove unused logger from sprint_config.py (pure data model)
- Update CLAUDE.md Package Structure with workflow/ subpackage
- Add implementation detail prose to docs/design/engine.md for
  Kanban Board and Agile Sprints sections
- Add new event constants: KANBAN_COLUMN_TRANSITION_INVALID,
  KANBAN_STATUS_PATH_MISSING, SPRINT_LIFECYCLE_TRANSITION_INVALID,
  SPRINT_TASK_COMPLETED, SPRINT_BACKLOG_INVALID,
  SPRINT_VELOCITY_INVALID

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 1, 2026 09:31
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

Walkthrough

Added a new workflow package under src/synthorg/engine/workflow and related artifacts. Introduced WorkflowType enum and WorkflowConfig plus KanbanConfig and SprintConfig models; implemented Kanban column enums/mappings, WIP-limit checking, and column-to-task-status transition resolution; implemented a linear sprint lifecycle (SprintStatus) with a Sprint model, pure sprint-backlog operations, and sprint velocity recording/averaging. Exposed workflow config in root defaults/schema and updated templates. Added observability event constants and comprehensive unit tests for the new workflow functionality.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.37% which is insufficient. The required threshold is 40.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: implementing both Kanban board and Agile sprints workflow types as described in the changeset.
Description check ✅ Passed The description comprehensively covers the changeset scope, detailing Kanban implementation, Agile sprints, shared infrastructure, test coverage, and validation status.
Linked Issues check ✅ Passed All code changes fully meet the requirements from issues #239 and #240: Kanban workflow with 5-column state machine, WIP limits, and TaskStatus integration [#239]; Agile sprints with linear lifecycle, sprint model, backlog operations, and velocity tracking [#240].
Out of Scope Changes check ✅ Passed All changes are in-scope: Kanban columns/transitions/WIP limits, sprint lifecycle/backlog/velocity, WorkflowType enum, workflow configuration, and comprehensive test coverage directly support the linked issue objectives.

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


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

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 1, 2026 09:31 — with GitHub Actions Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Dependency Review

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

Snapshot Warnings

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

Scanned Files

None

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive workflow management system to the engine, supporting both Kanban and Agile Sprint methodologies. It includes models for Kanban boards with configurable WIP limits, a strictly linear sprint lifecycle state machine, backlog management functions, and velocity tracking with rolling average calculations. Feedback focuses on a logical flaw in Kanban column transitions where moving tasks back to the backlog results in an off-board status, as well as data integrity concerns when removing tasks from a sprint without adjusting point totals. Additionally, the strict enforcement of committed story points during task completion is noted as potentially too restrictive for standard Agile practices.

Comment on lines +160 to +164
(KanbanColumn.READY, KanbanColumn.BACKLOG): (
TaskStatus.BLOCKED,
TaskStatus.ASSIGNED,
TaskStatus.BLOCKED,
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The status path for moving from READY to BACKLOG is logically flawed. It ends in TaskStatus.BLOCKED, which maps to None (off-board) in STATUS_TO_COLUMN. This means a task moved to the BACKLOG column effectively disappears from the board. Furthermore, since the TaskStatus state machine does not allow transitions back to CREATED (the only status mapping to BACKLOG), this transition is technically impossible to complete correctly. If backward moves to BACKLOG are not supported by the underlying task engine, they should be removed from VALID_COLUMN_TRANSITIONS.

Comment on lines +131 to +138
new_task_ids = tuple(t for t in sprint.task_ids if t != task_id)
new_completed = tuple(t for t in sprint.completed_task_ids if t != task_id)
result = sprint.model_copy(
update={
"task_ids": new_task_ids,
"completed_task_ids": new_completed,
},
)
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

Removing a task from a sprint does not adjust the story_points_committed or story_points_completed totals. While the docstring notes this limitation, it creates a significant data integrity issue for velocity tracking and sprint planning. Since the Sprint model does not store per-task point data, the remove_task_from_sprint function should ideally accept the story points to be subtracted to maintain accurate aggregate totals.

Comment on lines +236 to +249
if new_completed_points > sprint.story_points_committed:
msg = (
f"Completing task {task_id!r} with {story_points} "
f"points would exceed committed points "
f"({new_completed_points} > "
f"{sprint.story_points_committed})"
)
_log_and_raise(
SPRINT_BACKLOG_INVALID,
msg,
sprint_id=sprint.id,
task_id=task_id,
reason="exceeds_committed",
)
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 check preventing task completion if it exceeds the total story_points_committed is too restrictive for real-world Agile workflows. Estimates are often inaccurate, and hard-blocking the completion of a task because it pushes the sprint total over the initial commitment prevents teams from accurately recording delivered work. This validation should be removed or downgraded to a warning.

    new_completed_points = sprint.story_points_completed + story_points
    # Removed strict commitment check to allow for estimate variance

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements the workflow-type layer described in the engine design spec by adding first-class Kanban board and Agile sprints primitives (state machines + configs), wiring them into the root config/template schema, and introducing dedicated observability event constants plus unit coverage.

Changes:

  • Added Kanban board column model, column-transition validation, TaskStatus bridging, and per-column WIP limits with enforcement behavior.
  • Added Agile sprint lifecycle model, sprint ceremony/config models, immutable sprint backlog operations, and velocity recording/rolling averages.
  • Integrated workflow configuration into RootConfig and CompanyTemplate, and added workflow observability event constants + corresponding tests/docs updates.

Reviewed changes

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

Show a summary per file
File Description
tests/unit/templates/test_schema.py Updates type-ignore to reflect workflow now being an enum-typed frozen field.
tests/unit/observability/test_events.py Adds workflow to the expected per-domain observability events modules.
tests/unit/engine/workflow/test_workflow_config.py New tests covering WorkflowConfig, WorkflowType, and schema integration points.
tests/unit/engine/workflow/test_sprint_velocity.py New tests for sprint velocity record creation and rolling average calculation.
tests/unit/engine/workflow/test_sprint_lifecycle.py New tests for SprintStatus lifecycle transitions and Sprint validation/immutability helpers.
tests/unit/engine/workflow/test_sprint_config.py New tests for sprint ceremony/config validation (protocol/frequency integration, bounds, uniqueness).
tests/unit/engine/workflow/test_sprint_backlog.py New tests for immutable sprint backlog operations and status-dependent guards.
tests/unit/engine/workflow/test_kanban_columns.py New tests for Kanban columns, TaskStatus bridge consistency, transitions, status paths, and WIP enforcement.
tests/unit/engine/workflow/conftest.py Adds shared Kanban config fixtures for strict/advisory WIP enforcement tests.
tests/unit/config/conftest.py Extends RootConfigFactory to include the new workflow section.
src/synthorg/templates/schema.py Changes CompanyTemplate.workflow from string to WorkflowType with enum default.
src/synthorg/observability/events/workflow.py Introduces workflow-specific observability event name constants.
src/synthorg/engine/workflow/sprint_velocity.py Implements VelocityRecord, velocity recording, and rolling average calculation.
src/synthorg/engine/workflow/sprint_lifecycle.py Adds SprintStatus, strict transition map, and the frozen Sprint domain model with validators.
src/synthorg/engine/workflow/sprint_config.py Adds sprint ceremony + sprint configuration models and validation.
src/synthorg/engine/workflow/sprint_backlog.py Adds immutable sprint backlog manipulation helpers with structured logging.
src/synthorg/engine/workflow/kanban_columns.py Adds Kanban columns, transition map, column↔TaskStatus bridge, and status-path resolution.
src/synthorg/engine/workflow/kanban_board.py Adds Kanban WIP limit models/config and enforcement helper.
src/synthorg/engine/workflow/config.py Adds umbrella WorkflowConfig for root configuration integration.
src/synthorg/engine/workflow/init.py Exposes workflow APIs/models via package exports.
src/synthorg/core/enums.py Adds WorkflowType enum to core enums.
src/synthorg/core/init.py Re-exports WorkflowType from the core package.
src/synthorg/config/schema.py Adds workflow: WorkflowConfig to RootConfig.
src/synthorg/config/defaults.py Adds the new workflow section to the default config dict.
docs/design/engine.md Updates design doc to describe the new Kanban/Sprint workflow models and integration points.
CLAUDE.md Updates repository structure documentation to include the new engine/workflow/ module.

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

Comment on lines +160 to +171
(KanbanColumn.READY, KanbanColumn.BACKLOG): (
TaskStatus.BLOCKED,
TaskStatus.ASSIGNED,
TaskStatus.BLOCKED,
),
# IN_PROGRESS -> ...
(KanbanColumn.IN_PROGRESS, KanbanColumn.REVIEW): (TaskStatus.IN_REVIEW,),
(KanbanColumn.IN_PROGRESS, KanbanColumn.BACKLOG): (
TaskStatus.BLOCKED,
TaskStatus.ASSIGNED,
TaskStatus.BLOCKED,
),
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

_COLUMN_MOVE_STATUS_PATH entries for moves to KanbanColumn.BACKLOG do not result in a TaskStatus that maps to the BACKLOG column. For example, READY -> BACKLOG ends in TaskStatus.BLOCKED (which maps to None via STATUS_TO_COLUMN), so applying the returned status path cannot actually place the task in the target column as described by the module docstring/design docs. Either remove READY -> BACKLOG from VALID_COLUMN_TRANSITIONS, or change the TaskStatus mapping/transition model so the final status maps to BACKLOG (e.g., ends in CREATED) and is reachable via valid task transitions.

Suggested change
(KanbanColumn.READY, KanbanColumn.BACKLOG): (
TaskStatus.BLOCKED,
TaskStatus.ASSIGNED,
TaskStatus.BLOCKED,
),
# IN_PROGRESS -> ...
(KanbanColumn.IN_PROGRESS, KanbanColumn.REVIEW): (TaskStatus.IN_REVIEW,),
(KanbanColumn.IN_PROGRESS, KanbanColumn.BACKLOG): (
TaskStatus.BLOCKED,
TaskStatus.ASSIGNED,
TaskStatus.BLOCKED,
),
# IN_PROGRESS -> ...
(KanbanColumn.IN_PROGRESS, KanbanColumn.REVIEW): (TaskStatus.IN_REVIEW,),

Copilot uses AI. Check for mistakes.
Comment on lines +165 to +175
# IN_PROGRESS -> ...
(KanbanColumn.IN_PROGRESS, KanbanColumn.REVIEW): (TaskStatus.IN_REVIEW,),
(KanbanColumn.IN_PROGRESS, KanbanColumn.BACKLOG): (
TaskStatus.BLOCKED,
TaskStatus.ASSIGNED,
TaskStatus.BLOCKED,
),
(KanbanColumn.IN_PROGRESS, KanbanColumn.READY): (
TaskStatus.BLOCKED,
TaskStatus.ASSIGNED,
),
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

IN_PROGRESS -> BACKLOG is currently declared as a valid column transition, but its _COLUMN_MOVE_STATUS_PATH ends with TaskStatus.BLOCKED (off-board) rather than a status that maps to KanbanColumn.BACKLOG (TaskStatus.CREATED). This means resolve_task_transitions() cannot produce a sequence that actually lands in the requested target column. Consider removing this transition or updating the underlying task transition model / column<->status mapping so the last status in the path maps to the target column.

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@src/synthorg/engine/workflow/kanban_board.py`:
- Around line 95-116: The two model validators _validate_no_duplicate_columns
and _validate_no_done_limit currently raise ValueError without logging; update
each to log the validation failure (with context and the same message) at
WARNING or ERROR before raising, using the module-level logger (e.g.,
logger.warning or logger.error), and ensure a module-level logger is defined if
missing; keep the existing error messages (msg) and raise the ValueError
afterwards so the logs record the failure details for observability.

In `@tests/unit/engine/workflow/test_kanban_columns.py`:
- Around line 379-394: The tests test_status_to_column_round_trips and
test_every_status_is_mapped currently hardcode Hypothesis settings with
`@settings`(max_examples=50); remove these `@settings`(...) decorators so the tests
rely on the centralized Hypothesis profile (configured via HYPOTHESIS_PROFILE in
tests/conftest.py) and keep the existing `@given`(...) and `@pytest.mark.unit`
decorators and the functions (COLUMN_TO_STATUSES, STATUS_TO_COLUMN,
KanbanColumn, TaskStatus) unchanged.
🪄 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: 7b4144be-a46f-41d2-b2a6-f942f87a22e5

📥 Commits

Reviewing files that changed from the base of the PR and between 29cc419 and 040de07.

📒 Files selected for processing (27)
  • CLAUDE.md
  • docs/design/engine.md
  • src/synthorg/config/defaults.py
  • src/synthorg/config/schema.py
  • src/synthorg/core/__init__.py
  • src/synthorg/core/enums.py
  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/engine/workflow/config.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_backlog.py
  • src/synthorg/engine/workflow/sprint_config.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/templates/schema.py
  • tests/unit/config/conftest.py
  • tests/unit/engine/workflow/__init__.py
  • tests/unit/engine/workflow/conftest.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • tests/unit/engine/workflow/test_sprint_backlog.py
  • tests/unit/engine/workflow/test_sprint_config.py
  • tests/unit/engine/workflow/test_sprint_lifecycle.py
  • tests/unit/engine/workflow/test_sprint_velocity.py
  • tests/unit/engine/workflow/test_workflow_config.py
  • tests/unit/observability/test_events.py
  • tests/unit/templates/test_schema.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). (7)
  • GitHub Check: Agent
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.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) for multiple exception types in except clauses — PEP 758 style, enforced by ruff on Python 3.14
Use line length of 88 characters, enforced by ruff

Files:

  • src/synthorg/config/defaults.py
  • tests/unit/templates/test_schema.py
  • tests/unit/config/conftest.py
  • src/synthorg/core/__init__.py
  • src/synthorg/config/schema.py
  • tests/unit/observability/test_events.py
  • src/synthorg/core/enums.py
  • src/synthorg/templates/schema.py
  • src/synthorg/engine/workflow/config.py
  • tests/unit/engine/workflow/conftest.py
  • src/synthorg/engine/workflow/__init__.py
  • tests/unit/engine/workflow/test_sprint_velocity.py
  • tests/unit/engine/workflow/test_sprint_config.py
  • tests/unit/engine/workflow/test_sprint_backlog.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • tests/unit/engine/workflow/test_sprint_lifecycle.py
  • src/synthorg/engine/workflow/sprint_config.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • tests/unit/engine/workflow/test_workflow_config.py
  • src/synthorg/engine/workflow/sprint_backlog.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: All public functions and classes in Python must have type hints; mypy strict mode is enforced
Use Google style docstrings on all public classes and functions; enforced by ruff D rules
Create new objects instead of mutating existing ones for immutability. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence)
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use Pydantic v2 with conventions: allow_inf_nan=False in all ConfigDict declarations, @computed_field for derived values instead of storing redundant fields, NotBlankStr from core.types for all identifier/name fields including optional and tuple variants
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Maintain 80% minimum test coverage; enforced in CI
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, or large/medium/small aliases
Apply mypy type-checking in pre-push hooks for Python code

Files:

  • src/synthorg/config/defaults.py
  • src/synthorg/core/__init__.py
  • src/synthorg/config/schema.py
  • src/synthorg/core/enums.py
  • src/synthorg/templates/schema.py
  • src/synthorg/engine/workflow/config.py
  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • src/synthorg/engine/workflow/sprint_config.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/sprint_backlog.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic must have: from synthorg.observability import get_logger then logger = get_logger(__name__)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py and observability/sinks.py may use stdlib logging and print(..., file=sys.stderr) for bootstrap and handler-cleanup code
Always use logger as the variable name for loggers (not _logger, not log)
Always use event name 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
Always use structured kwargs for logging: logger.info(EVENT, key=value) — never logger.info("msg %s", val)
All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO
DEBUG logging is appropriate for object creation, internal flow, and entry/exit of key functions
Pure data models, enums, and re-exports do not need logging

Files:

  • src/synthorg/config/defaults.py
  • src/synthorg/core/__init__.py
  • src/synthorg/config/schema.py
  • src/synthorg/core/enums.py
  • src/synthorg/templates/schema.py
  • src/synthorg/engine/workflow/config.py
  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • src/synthorg/engine/workflow/sprint_config.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/sprint_backlog.py
{src,tests}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Apply ruff check+format in pre-commit hooks to ensure consistent Python code style

Files:

  • src/synthorg/config/defaults.py
  • tests/unit/templates/test_schema.py
  • tests/unit/config/conftest.py
  • src/synthorg/core/__init__.py
  • src/synthorg/config/schema.py
  • tests/unit/observability/test_events.py
  • src/synthorg/core/enums.py
  • src/synthorg/templates/schema.py
  • src/synthorg/engine/workflow/config.py
  • tests/unit/engine/workflow/conftest.py
  • src/synthorg/engine/workflow/__init__.py
  • tests/unit/engine/workflow/test_sprint_velocity.py
  • tests/unit/engine/workflow/test_sprint_config.py
  • tests/unit/engine/workflow/test_sprint_backlog.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • tests/unit/engine/workflow/test_sprint_lifecycle.py
  • src/synthorg/engine/workflow/sprint_config.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • tests/unit/engine/workflow/test_workflow_config.py
  • src/synthorg/engine/workflow/sprint_backlog.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow for test categorization
Global test timeout is 30 seconds per test (in pyproject.toml) — do not add per-file markers unless overriding with longer timeouts like timeout(60)
Prefer @pytest.mark.parametrize for testing similar cases
Use test-provider, test-small-001, etc. in tests instead of real vendor names
Use Hypothesis for property-based testing with @given + @settings. Use HYPOTHESIS_PROFILE env var to select profiles: ci (50 examples, default) or dev (1000 examples)
Never skip, dismiss, or ignore flaky tests. Always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic. For indefinite blocking tasks, use asyncio.Event().wait() instead of asyncio.sleep(large_number)

Files:

  • tests/unit/templates/test_schema.py
  • tests/unit/config/conftest.py
  • tests/unit/observability/test_events.py
  • tests/unit/engine/workflow/conftest.py
  • tests/unit/engine/workflow/test_sprint_velocity.py
  • tests/unit/engine/workflow/test_sprint_config.py
  • tests/unit/engine/workflow/test_sprint_backlog.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • tests/unit/engine/workflow/test_sprint_lifecycle.py
  • tests/unit/engine/workflow/test_workflow_config.py
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Update the relevant docs/design/ page to reflect the approved deviation from the design spec

Files:

  • docs/design/engine.md
🧠 Learnings (43)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Commits: <type>: <description> — types: feat, fix, refactor, docs, test, chore, perf, ci. Enforced by commitizen (commit-msg hook). Signed commits: required on main via branch protection — all commits must be GPG/SSH signed.
📚 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:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to src/**/*.py : For `dict`/`list` fields in frozen Pydantic models, rely on `frozen=True` for field reassignment prevention and `copy.deepcopy()` at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence)

Applied to files:

  • tests/unit/templates/test_schema.py
📚 Learning: 2026-03-16T23:05:29.577Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T23:05:29.577Z
Learning: Applies to **/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).

Applied to files:

  • tests/unit/templates/test_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 : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).

Applied to files:

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

Applied to files:

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

Applied to files:

  • tests/unit/config/conftest.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/core/**/*.py : Core module must contain shared domain models, base classes, resilience config (RetryConfig, RateLimiterConfig)

Applied to files:

  • tests/unit/config/conftest.py
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to src/synthorg/providers/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`

Applied to files:

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

Applied to files:

  • tests/unit/config/conftest.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.

Applied to files:

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

Applied to files:

  • src/synthorg/config/schema.py
  • src/synthorg/engine/workflow/config.py
  • src/synthorg/engine/workflow/sprint_config.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 frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.

Applied to files:

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

Applied to files:

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

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.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/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly and use in structured logging

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.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 event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/templates/**/*.py : Templates: pre-built company templates, personality presets, and builder.

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 BaseModel, model_validator, computed_field, ConfigDict.

Applied to files:

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

Applied to files:

  • src/synthorg/templates/schema.py
  • src/synthorg/engine/workflow/config.py
  • src/synthorg/engine/workflow/sprint_config.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/engine/workflow/config.py
  • src/synthorg/engine/workflow/sprint_config.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`

Applied to files:

  • src/synthorg/templates/schema.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.

Applied to files:

  • src/synthorg/templates/schema.py
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to src/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/config.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.

Applied to files:

  • src/synthorg/engine/workflow/config.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 src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to src/synthorg/**/*.py : Always use event name 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`

Applied to files:

  • src/synthorg/observability/events/workflow.py
🔇 Additional comments (43)
src/synthorg/observability/events/workflow.py (1)

1-58: Well-structured event constants module.

The event naming follows the established domain.subject.qualifier pattern consistently, documentation is clear, and the module correctly omits logging infrastructure as per guidelines for pure data modules.

src/synthorg/engine/workflow/kanban_columns.py (7)

1-19: LGTM: Module setup and imports.

Correct use of get_logger(__name__) with logger variable name, and event constants imported directly from the domain-specific module as per guidelines.


22-52: LGTM: Enum and column-to-status mapping.

KanbanColumn enum is well-documented, and COLUMN_TO_STATUSES uses MappingProxyType with frozenset for proper immutability. As per coding guidelines, MappingProxyType wrapping is the correct pattern for read-only enforcement on internal collections.


54-99: LGTM: Bidirectional mapping with comprehensive guards.

STATUS_TO_COLUMN correctly maps all TaskStatus members (including off-board statuses to None), and the module-level guards at lines 72-98 ensure mapping completeness and consistency at import time—catching configuration drift early.


103-131: LGTM: Column transition rules with completeness guard.

VALID_COLUMN_TRANSITIONS correctly defines the state machine with DONE as terminal, and the guard at lines 125-131 ensures every column has a transition entry.


146-191: LGTM: Status path mapping with completeness guard.

The _COLUMN_MOVE_STATUS_PATH mapping is well-documented regarding backward moves through BLOCKED, and the guard at lines 183-190 ensures every valid column transition has a corresponding status path.


193-237: LGTM: Transition validation with proper observability.

validate_column_transition correctly logs at WARNING before raising on invalid transitions (lines 210-216, 219-231) and logs at INFO on successful state transitions (lines 232-236), complying with the logging guidelines.


239-272: LGTM: Status path resolution with proper error logging.

resolve_task_transitions logs at WARNING with context before raising when no path exists (lines 262-271), complying with the error-path logging guideline.

src/synthorg/engine/workflow/sprint_lifecycle.py (6)

1-24: LGTM: Module setup and imports.

Correct use of get_logger(__name__) with logger variable name, and event constants imported directly from the workflow events module.


26-69: LGTM: Sprint status enum and transition map.

SprintStatus enum is well-documented with the linear lifecycle, and VALID_SPRINT_TRANSITIONS uses MappingProxyType with a completeness guard to catch configuration drift at import time.


71-115: LGTM: Transition validation with proper observability.

validate_sprint_transition logs at WARNING before raising on invalid transitions (lines 88-94, 97-109) and logs at INFO on successful state transitions (lines 110-114), complying with logging guidelines.


120-183: LGTM: Sprint model with proper Pydantic conventions.

The model correctly uses:

  • frozen=True and allow_inf_nan=False in ConfigDict
  • NotBlankStr for identifier fields (id, name, task_ids, completed_task_ids)
  • str for goal (correctly allows empty since it has a default)
  • Appropriate field constraints (ge=1 for sprint_number, ge=0.0 for story points)

184-266: LGTM: Comprehensive model validators.

The validators properly enforce:

  • ISO 8601 date format validation
  • Date ordering constraint (end_date >= start_date)
  • Task ID uniqueness and subset constraint (completed_task_ids ⊆ task_ids)
  • Story points bound (completed <= committed)
  • Status-dependent date requirements (start_date for ACTIVE+, end_date for COMPLETED)

267-288: LGTM: Immutable transition method.

with_transition correctly:

  • Forbids status in overrides to prevent ambiguity
  • Validates the transition before applying
  • Uses model_dump() + model_validate() which re-runs all validators on the new instance
src/synthorg/engine/workflow/sprint_velocity.py (4)

1-23: LGTM: Module setup and imports.

Correct use of get_logger(__name__) with logger variable name, proper TYPE_CHECKING guard for Sequence, and event constants imported directly from the workflow events module.


25-67: LGTM: VelocityRecord model with computed field.

The model correctly uses:

  • frozen=True and allow_inf_nan=False in ConfigDict
  • NotBlankStr for sprint_id
  • @computed_field for completion_ratio (as per guidelines, instead of storing redundant fields)
  • Proper division-by-zero handling in the computed property

69-109: LGTM: Velocity recording with proper observability.

record_velocity logs at WARNING before raising on non-completed sprints (lines 87-93) and logs at INFO on successful velocity recording (lines 101-108), complying with logging guidelines.


112-139: LGTM: Average velocity calculation.

The function correctly:

  • Validates window >= 1 and logs before raising (line 133)
  • Returns 0.0 for empty input
  • Uses the last window records for rolling average
src/synthorg/engine/workflow/sprint_backlog.py (4)

1-24: LGTM: Module setup with logging helper.

The _log_and_raise helper ensures consistent "log before raise" behavior across all error paths, and the module correctly imports event constants from the workflow events module.


26-89: LGTM: Add task operation with proper validation.

add_task_to_sprint correctly validates all preconditions (PLANNING status, no duplicates, non-negative points) before creating the new sprint via model_copy, and logs at INFO on success.


92-144: LGTM: Remove task operation maintains invariants.

remove_task_from_sprint correctly filters both task_ids and completed_task_ids (lines 131-132), maintaining the subset invariant by construction even though model_copy skips validators.


147-249: LGTM: Complete task operation with comprehensive pre-validation.

complete_task_in_sprint correctly pre-validates all preconditions in _validate_completion_preconditions, including the critical story points bound check (lines 235-248) that prevents violating the story_points_completed <= story_points_committed invariant—essential since model_copy skips model validators.

src/synthorg/config/defaults.py (1)

45-45: LGTM: Workflow config default added.

The empty dict "workflow": {} follows the existing pattern for other config sections and will be properly deep-merged with user config before Pydantic constructs the WorkflowConfig via its default_factory.

tests/unit/observability/test_events.py (1)

228-228: LGTM: Workflow domain added to expected modules.

The addition of "workflow" to the expected set ensures the new src/synthorg/observability/events/workflow.py module is properly discovered by the test's pkgutil.iter_modules scan.

CLAUDE.md (1)

96-96: LGTM: Documentation updated for workflow subpackage.

The engine/ description accurately reflects the new workflow/ subpackage with its Kanban board, Agile sprints, WIP limits, sprint lifecycle, and velocity tracking functionality.

src/synthorg/core/__init__.py (1)

58-58: Re-export of WorkflowType is correctly wired.

Import + __all__ updates are aligned, so WorkflowType is now consistently available from synthorg.core.

Also applies to: 142-142

tests/unit/templates/test_schema.py (1)

540-540: Type-ignore scope update is appropriate for this frozen-field test.

The updated suppression matches the intentional negative assignment pattern without changing runtime assertions.

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

27-27: RootConfigFactory is correctly extended with workflow config defaults.

This keeps test factories in sync with the new RootConfig.workflow surface.

Also applies to: 106-106

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

284-295: WorkflowType enum addition is well-scoped and consistent.

The four string members are explicit and suitable for schema coercion/serialization paths.

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

27-27: RootConfig.workflow integration is correct and follows existing schema patterns.

Typed field, default factory, and documentation updates are coherent with the rest of the root config model.

Also applies to: 534-535, 658-661

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

15-15: CompanyTemplate.workflow is correctly constrained to WorkflowType.

This is a solid type-safety improvement for template validation and downstream workflow handling.

Also applies to: 338-341

tests/unit/engine/workflow/conftest.py (1)

1-36: Workflow fixture set is well-structured and test-friendly.

The three KanbanConfig fixtures cleanly cover default/strict/advisory WIP scenarios.

docs/design/engine.md (2)

140-147: Kanban workflow design updates are clear and sufficiently specific.

The status mapping, off-board behavior, transition handling, and strict/advisory WIP semantics are documented well.


154-163: Sprint workflow section is complete and implementation-aligned.

Lifecycle linearity, backlog gates, ceremony linkage, and velocity model details are documented clearly.

src/synthorg/engine/workflow/config.py (1)

14-38: WorkflowConfig is well-structured and immutable.

Typed fields, frozen model config, and nested default_factory usage look correct for a top-level config model.

src/synthorg/engine/workflow/__init__.py (1)

7-67: Public workflow exports are explicit and coherent.

The aggregation module cleanly defines the workflow package surface via __all__.

tests/unit/engine/workflow/test_sprint_velocity.py (1)

34-154: Sprint velocity tests are comprehensive and targeted.

Good coverage of computed values, invalid-state rejection, and rolling window boundaries.

tests/unit/engine/workflow/test_sprint_config.py (1)

15-155: Sprint configuration tests provide solid validation coverage.

Defaults, bounds, duplicates, and custom overrides are all exercised clearly.

tests/unit/engine/workflow/test_workflow_config.py (1)

14-142: Workflow config integration tests are strong.

The suite validates defaults, immutability, enum values, and schema integration end-to-end.

tests/unit/engine/workflow/test_sprint_backlog.py (1)

41-223: Backlog operation tests are well-designed for lifecycle constraints.

Great coverage of allowed states, rejection paths, and immutability expectations.

src/synthorg/engine/workflow/sprint_config.py (1)

17-121: Sprint config models align with the project’s Pydantic config conventions.

Immutability, field constraints, NotBlankStr usage, and duplicate-name validation are all implemented cleanly.

tests/unit/engine/workflow/test_kanban_columns.py (1)

26-370: Kanban workflow test coverage is excellent across mappings, transitions, and WIP behavior.

The suite thoroughly exercises both deterministic and boundary/error paths.

tests/unit/engine/workflow/test_sprint_lifecycle.py (1)

18-278: Comprehensive lifecycle/model coverage looks solid.

These tests cover enum contracts, valid/invalid transitions, model invariants, and immutable transition behavior in a clear and maintainable way.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 91.84652% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.99%. Comparing base (29cc419) to head (5e1b27c).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/engine/workflow/kanban_columns.py 62.00% 13 Missing and 6 partials ⚠️
src/synthorg/engine/workflow/sprint_lifecycle.py 90.26% 8 Missing and 3 partials ⚠️
src/synthorg/engine/workflow/config.py 81.81% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #960      +/-   ##
==========================================
- Coverage   92.00%   91.99%   -0.01%     
==========================================
  Files         612      621       +9     
  Lines       33021    33436     +415     
  Branches     3170     3223      +53     
==========================================
+ Hits        30380    30761     +381     
- Misses       2097     2120      +23     
- Partials      544      555      +11     

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

Fixes from 13 local review agents + Gemini, Copilot, and CodeRabbit:

- Design spec: rename "Agile Sprints" to "Agile Kanban", update sprint
  diagram to use actual status names, add WorkflowConfig docs
- Sprint.with_transition: use model_copy instead of model_dump round-trip,
  validate override field names (only start_date/end_date allowed), log
  before raising on status override rejection
- KanbanConfig validators: log at WARNING before raising ValueError
- _log_and_raise: annotate -> NoReturn instead of -> None
- _validate_completion_preconditions: use NotBlankStr for task_id parameter
- Module-level guard variables: del after use in kanban_columns.py and
  sprint_lifecycle.py to avoid namespace pollution
- Backward move docs: clarify BACKLOG moves end at BLOCKED (off-board),
  no path from BLOCKED to CREATED exists
- Sprint upper bounds: add le=100_000 on sprint_number and story points
- add_task_to_sprint: document max_tasks_per_sprint as caller responsibility
- remove_task_from_sprint: log warning when removing completed task (stale
  story points), expand docstring about consequences
- VelocityRecord.completion_ratio: document that values > 1.0 are valid
- WorkflowConfig: add advisory warning validator for unused sub-configs
- Hypothesis tests: remove hardcoded @settings, use profile system
- Remove unused default_kanban_config fixture
- Add new event constants: WORKFLOW_CONFIG_UNUSED_SUBCONFIG,
  KANBAN_CONFIG_VALIDATION_FAILED

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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/engine/workflow/sprint_lifecycle.py`:
- Around line 189-270: Add structured logging to each Sprint model validator
before raising ValueError: in _validate_date_formats, _validate_date_ordering,
_validate_task_collections, _validate_story_points, and
_validate_status_date_requirements, call the module/logger (same pattern used by
KanbanConfig) to emit a SPRINT_VALIDATION_FAILED-like event with context
(validator name, offending field names/values, and the error message) and
include exception info when catching (e.g., in the datetime parse except block),
then raise the existing ValueError as before; keep messages consistent with
existing KANBAN_CONFIG_VALIDATION_FAILED usage so operational logs show
validator, payload, and reason prior to the raise.
🪄 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: 04e3cbee-2271-4e2d-9b2b-4910febef9a2

📥 Commits

Reviewing files that changed from the base of the PR and between 040de07 and 5e1b27c.

📒 Files selected for processing (11)
  • docs/design/engine.md
  • src/synthorg/engine/workflow/config.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_backlog.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • src/synthorg/observability/events/workflow.py
  • tests/unit/engine/workflow/conftest.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • tests/unit/engine/workflow/test_sprint_lifecycle.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: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.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) for multiple exception types in except clauses — PEP 758 style, enforced by ruff on Python 3.14
Use line length of 88 characters, enforced by ruff

Files:

  • src/synthorg/engine/workflow/config.py
  • src/synthorg/observability/events/workflow.py
  • tests/unit/engine/workflow/test_sprint_lifecycle.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/sprint_backlog.py
  • tests/unit/engine/workflow/conftest.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: All public functions and classes in Python must have type hints; mypy strict mode is enforced
Use Google style docstrings on all public classes and functions; enforced by ruff D rules
Create new objects instead of mutating existing ones for immutability. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence)
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use Pydantic v2 with conventions: allow_inf_nan=False in all ConfigDict declarations, @computed_field for derived values instead of storing redundant fields, NotBlankStr from core.types for all identifier/name fields including optional and tuple variants
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Maintain 80% minimum test coverage; enforced in CI
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, or large/medium/small aliases
Apply mypy type-checking in pre-push hooks for Python code

Files:

  • src/synthorg/engine/workflow/config.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/sprint_backlog.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic must have: from synthorg.observability import get_logger then logger = get_logger(__name__)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py and observability/sinks.py may use stdlib logging and print(..., file=sys.stderr) for bootstrap and handler-cleanup code
Always use logger as the variable name for loggers (not _logger, not log)
Always use event name 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
Always use structured kwargs for logging: logger.info(EVENT, key=value) — never logger.info("msg %s", val)
All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO
DEBUG logging is appropriate for object creation, internal flow, and entry/exit of key functions
Pure data models, enums, and re-exports do not need logging

Files:

  • src/synthorg/engine/workflow/config.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/sprint_backlog.py
{src,tests}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Apply ruff check+format in pre-commit hooks to ensure consistent Python code style

Files:

  • src/synthorg/engine/workflow/config.py
  • src/synthorg/observability/events/workflow.py
  • tests/unit/engine/workflow/test_sprint_lifecycle.py
  • src/synthorg/engine/workflow/sprint_velocity.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • src/synthorg/engine/workflow/kanban_columns.py
  • src/synthorg/engine/workflow/sprint_lifecycle.py
  • src/synthorg/engine/workflow/kanban_board.py
  • src/synthorg/engine/workflow/sprint_backlog.py
  • tests/unit/engine/workflow/conftest.py
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Update the relevant docs/design/ page to reflect the approved deviation from the design spec

Files:

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

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow for test categorization
Global test timeout is 30 seconds per test (in pyproject.toml) — do not add per-file markers unless overriding with longer timeouts like timeout(60)
Prefer @pytest.mark.parametrize for testing similar cases
Use test-provider, test-small-001, etc. in tests instead of real vendor names
Use Hypothesis for property-based testing with @given + @settings. Use HYPOTHESIS_PROFILE env var to select profiles: ci (50 examples, default) or dev (1000 examples)
Never skip, dismiss, or ignore flaky tests. Always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic. For indefinite blocking tasks, use asyncio.Event().wait() instead of asyncio.sleep(large_number)

Files:

  • tests/unit/engine/workflow/test_sprint_lifecycle.py
  • tests/unit/engine/workflow/test_kanban_columns.py
  • tests/unit/engine/workflow/conftest.py
🧠 Learnings (41)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/config.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 frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/config.py
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to src/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/config.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/config.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to **/*.py : Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.

Applied to files:

  • src/synthorg/observability/events/workflow.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 event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly and use in structured logging

Applied to files:

  • src/synthorg/observability/events/workflow.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 src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to src/synthorg/**/*.py : Always use event name 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`

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`

Applied to files:

  • src/synthorg/observability/events/workflow.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/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly

Applied to files:

  • src/synthorg/observability/events/workflow.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to tests/**/*.py : Use Hypothesis for property-based testing: `given` + `settings`; dev profile has 1000 examples (via `HYPOTHESIS_PROFILE=dev env var`), ci profile (default) has 50

Applied to files:

  • tests/unit/engine/workflow/test_kanban_columns.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 tests/**/*.py : Use Hypothesis for property-based testing with `given` + `settings` decorators; control profiles via `HYPOTHESIS_PROFILE` env var (`ci` for 200 examples, `dev` for 1000 examples)

Applied to files:

  • tests/unit/engine/workflow/test_kanban_columns.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to tests/**/*.py : Use Hypothesis for property-based testing in Python with `given` + `settings`. Control via `HYPOTHESIS_PROFILE` env var (dev: 1000 examples, ci: 200 examples).

Applied to files:

  • tests/unit/engine/workflow/test_kanban_columns.py
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to tests/**/*.py : Use Hypothesis for property-based testing with `given` + `settings`. Use `HYPOTHESIS_PROFILE` env var to select profiles: `ci` (50 examples, default) or `dev` (1000 examples)

Applied to files:

  • tests/unit/engine/workflow/test_kanban_columns.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: Property-based testing: Python uses Hypothesis (given + settings). Hypothesis profiles: ci (200 examples, default) and dev (1000 examples), controlled via HYPOTHESIS_PROFILE env var. Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties.

Applied to files:

  • tests/unit/engine/workflow/test_kanban_columns.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run property tests with `HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties`

Applied to files:

  • tests/unit/engine/workflow/test_kanban_columns.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to src/synthorg/**/*.py : Maintain 80% minimum test coverage (enforced in CI)

Applied to files:

  • tests/unit/engine/workflow/test_kanban_columns.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 : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.

Applied to files:

  • src/synthorg/engine/workflow/kanban_board.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit

Applied to files:

  • src/synthorg/engine/workflow/kanban_board.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.

Applied to files:

  • src/synthorg/engine/workflow/kanban_board.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.

Applied to files:

  • src/synthorg/engine/workflow/kanban_board.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/kanban_board.py
📚 Learning: 2026-03-31T22:07:03.782Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T22:07:03.782Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/sprint_backlog.py
🔇 Additional comments (24)
tests/unit/engine/workflow/conftest.py (1)

1-30: LGTM!

Clean, well-documented fixtures providing strict and advisory Kanban configurations for WIP limit testing. The fixtures are properly typed and follow project conventions.

src/synthorg/engine/workflow/config.py (1)

1-74: LGTM!

Well-structured umbrella configuration model with appropriate immutability (frozen=True), observability integration for detecting unused sub-configs, and correct workflow-type categorization. The validator logic correctly warns when users customize configs that won't be consulted at runtime.

docs/design/engine.md (1)

140-171: LGTM!

Documentation accurately reflects the new workflow implementations: Kanban column mappings, WIP limit enforcement modes, Agile Kanban combination workflow, linear SprintStatus lifecycle, and VelocityRecord metrics. The ASCII diagram correctly illustrates the sprint flow.

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

1-64: LGTM!

Well-organized event constants module with clear domain separation (Kanban vs Sprint), consistent naming convention (workflow.kanban.*, workflow.sprint.*), and descriptive docstrings for each event.

src/synthorg/engine/workflow/sprint_velocity.py (1)

1-144: LGTM!

Clean velocity tracking implementation with proper immutable model design, correct division-by-zero handling in completion_ratio, consistent log-before-raise patterns, and a straightforward rolling average calculation. The TYPE_CHECKING usage for Sequence is appropriate.

src/synthorg/engine/workflow/kanban_columns.py (1)

1-281: LGTM!

Excellent implementation with comprehensive module-level guards ensuring mapping consistency at import time. The bidirectional column-status bridge is well-documented, backward move semantics through BLOCKED are clearly explained, and the MappingProxyType wrapping enforces immutability. Log-before-raise patterns are correctly applied throughout.

tests/unit/engine/workflow/test_kanban_columns.py (1)

1-394: LGTM!

Comprehensive test coverage for Kanban columns, transitions, WIP limits, and configuration validation. Good use of @pytest.mark.parametrize for similar cases, fixtures for shared configs, and Hypothesis property-based tests (correctly using profile-based configuration without hardcoded @settings).

tests/unit/engine/workflow/test_sprint_lifecycle.py (1)

1-288: LGTM!

Thorough test coverage for the sprint lifecycle state machine: enum values, valid/invalid transitions, self-transitions, full lifecycle path, Sprint model validation (dates, task constraints, story points), and with_transition behavior including disallowed override rejection. The _make_sprint helper pattern keeps tests DRY.

src/synthorg/engine/workflow/kanban_board.py (6)

1-25: Module setup and imports are correct.

Logger is properly initialized with get_logger(__name__) and event constants are imported from the correct domain module. The TYPE_CHECKING guard for Mapping is appropriate for avoiding runtime import overhead.


26-42: Well-structured WIP limit model.

The KanbanWipLimit model correctly uses frozen config with allow_inf_nan=False, and the limit bounds (1-100) are sensible for practical Kanban boards.


44-66: LGTM!

The WipCheckResult model correctly captures check outcomes with appropriate field constraints.


68-128: LGTM!

The KanbanConfig model is well-designed with sensible defaults and proper validation. The validators correctly log KANBAN_CONFIG_VALIDATION_FAILED before raising, addressing the observability requirement.


131-139: LGTM!

Simple helper with clear semantics. Linear search is appropriate given the small, bounded number of Kanban columns.


142-203: LGTM!

The check_wip_limit function correctly implements WIP enforcement with proper logging at appropriate levels (INFO for reaching limit, WARNING for exceeding). The distinction between strict and advisory enforcement is handled cleanly.

src/synthorg/engine/workflow/sprint_backlog.py (4)

1-26: Module setup is correct.

The _log_and_raise helper is a clean pattern for consistent error logging. The NoReturn type hint correctly indicates the function always raises.


28-93: LGTM!

The add_task_to_sprint function correctly validates preconditions and returns a new immutable Sprint instance. The docstring appropriately documents the caller's responsibility for max_tasks_per_sprint enforcement.


96-161: LGTM!

The function correctly handles task removal with appropriate warnings when removing completed tasks. The docstring clearly documents that story point adjustments are the caller's responsibility since per-task point data isn't stored on the Sprint model.


164-266: LGTM!

The complete_task_in_sprint function and its helper properly validate all preconditions. The check preventing story_points_completed from exceeding story_points_committed ensures sprint integrity.

src/synthorg/engine/workflow/sprint_lifecycle.py (6)

1-24: Module setup is correct.

Logger and event imports are properly configured. Using MappingProxyType for the transition map provides read-only enforcement per coding guidelines.


26-46: LGTM!

The SprintStatus enum is well-documented with clear lifecycle semantics using StrEnum for serialization compatibility.


48-70: LGTM!

The MappingProxyType wrapper ensures immutability per coding guidelines. The completeness guard is good defensive programming for future maintenance.


73-117: LGTM!

The transition validator properly logs before raising on error paths and logs successful transitions at INFO level per coding guidelines.


122-188: LGTM!

The Sprint model is well-structured with appropriate field constraints. Using NotBlankStr for identifiers (id, name) and regular str for the optional goal field is correct.


272-312: LGTM!

The with_transition method correctly restricts overrides to start_date and end_date, logs before raising on error paths, and uses model_copy for immutability. The explicit rejection of status in overrides prevents misuse.

Comment on lines +189 to +270
@model_validator(mode="after")
def _validate_date_formats(self) -> Self:
"""Validate ISO 8601 date format when present."""
for field_name in ("start_date", "end_date"):
value = getattr(self, field_name)
if value is not None:
if not value.strip():
msg = f"{field_name} must not be whitespace-only"
raise ValueError(msg)
try:
datetime.fromisoformat(value)
except ValueError as exc:
msg = f"{field_name} must be a valid ISO 8601 string, got {value!r}"
raise ValueError(msg) from exc
return self

@model_validator(mode="after")
def _validate_date_ordering(self) -> Self:
"""Ensure end_date >= start_date when both are present."""
if self.start_date is not None and self.end_date is not None:
start = datetime.fromisoformat(self.start_date)
end = datetime.fromisoformat(self.end_date)
if end < start:
msg = (
f"end_date ({self.end_date}) must be >= "
f"start_date ({self.start_date})"
)
raise ValueError(msg)
return self

@model_validator(mode="after")
def _validate_task_collections(self) -> Self:
"""Validate task ID uniqueness and subset constraint."""
if len(self.task_ids) != len(set(self.task_ids)):
dupes = sorted(t for t, c in Counter(self.task_ids).items() if c > 1)
msg = f"Duplicate entries in task_ids: {dupes}"
raise ValueError(msg)
if len(self.completed_task_ids) != len(set(self.completed_task_ids)):
dupes = sorted(
t for t, c in Counter(self.completed_task_ids).items() if c > 1
)
msg = f"Duplicate entries in completed_task_ids: {dupes}"
raise ValueError(msg)
task_set = set(self.task_ids)
extra = set(self.completed_task_ids) - task_set
if extra:
msg = f"completed_task_ids contains IDs not in task_ids: {sorted(extra)}"
raise ValueError(msg)
return self

@model_validator(mode="after")
def _validate_story_points(self) -> Self:
"""story_points_completed must not exceed committed."""
if self.story_points_completed > self.story_points_committed:
msg = (
f"story_points_completed ({self.story_points_completed}) "
f"exceeds story_points_committed "
f"({self.story_points_committed})"
)
raise ValueError(msg)
return self

@model_validator(mode="after")
def _validate_status_date_requirements(self) -> Self:
"""Enforce date requirements based on status.

- ACTIVE and later require ``start_date``.
- COMPLETED requires ``end_date``.
"""
requires_start = {
SprintStatus.ACTIVE,
SprintStatus.IN_REVIEW,
SprintStatus.RETROSPECTIVE,
SprintStatus.COMPLETED,
}
if self.status in requires_start and self.start_date is None:
msg = f"start_date is required when status is {self.status.value!r}"
raise ValueError(msg)
if self.status is SprintStatus.COMPLETED and self.end_date is None:
msg = "end_date is required when status is 'completed'"
raise ValueError(msg)
return self
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding validation failure logging to Sprint validators for consistency.

The Sprint model validators raise ValueError without logging, unlike the KanbanConfig validators in kanban_board.py which log KANBAN_CONFIG_VALIDATION_FAILED before raising. While the coding guidelines note that "pure data models do not need logging," the precedent set by KanbanConfig suggests logging validation failures aids operational diagnosis.

This is a minor inconsistency. If sprint validation failures are expected to occur primarily during development/testing, the current approach is acceptable. If invalid Sprint constructions could occur in production (e.g., from persisted data or external input), consider adding structured logging for consistency.

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

In `@src/synthorg/engine/workflow/sprint_lifecycle.py` around lines 189 - 270, Add
structured logging to each Sprint model validator before raising ValueError: in
_validate_date_formats, _validate_date_ordering, _validate_task_collections,
_validate_story_points, and _validate_status_date_requirements, call the
module/logger (same pattern used by KanbanConfig) to emit a
SPRINT_VALIDATION_FAILED-like event with context (validator name, offending
field names/values, and the error message) and include exception info when
catching (e.g., in the datetime parse except block), then raise the existing
ValueError as before; keep messages consistent with existing
KANBAN_CONFIG_VALIDATION_FAILED usage so operational logs show validator,
payload, and reason prior to the raise.

@Aureliolo Aureliolo merged commit f511e1d into main Apr 1, 2026
31 checks passed
@Aureliolo Aureliolo deleted the feat/workflow-types branch April 1, 2026 10:40
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 1, 2026 10:40 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Apr 1, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.5.5](v0.5.4...v0.5.5)
(2026-04-01)


### Features

* add workflow configs to builtin templates
([#963](#963))
([b7fe6e3](b7fe6e3))
* implement Kanban board and Agile sprints workflow types
([#960](#960))
([f511e1d](f511e1d))
* personality preset support in template YAML schema
([#959](#959))
([97ca81e](97ca81e))


### Documentation

* LMEB embedding evaluation + CSP accepted risk
([#695](#695),
[#925](#925))
([#962](#962))
([43dfab3](43dfab3))


### CI/CD

* bump wrangler from 4.78.0 to 4.79.0 in /.github in the all group
across 1 directory
([#955](#955))
([18b4cb1](18b4cb1))


### Maintenance

* bump mypy from 1.19.1 to 1.20.0 in the all group across 1 directory
([#956](#956))
([29cc419](29cc419))

---
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: implement Agile sprints workflow type feat: implement Kanban board workflow type

2 participants