Skip to content

feat: ceremony template defaults + strategy migration UX#1031

Merged
Aureliolo merged 6 commits intomainfrom
feat/ceremony-template-defaults
Apr 3, 2026
Merged

feat: ceremony template defaults + strategy migration UX#1031
Aureliolo merged 6 commits intomainfrom
feat/ceremony-template-defaults

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

Implements two ceremony scheduling features: strategy-aware velocity calculator defaults (#976) and strategy migration UX (#978).

#976: Strategy-aware velocity calculator default + template enrichment

Problem: resolve_ceremony_policy() used a hardcoded TASK_DRIVEN fallback for velocity_calculator regardless of the active strategy. 5 of 9 templates resolved to the wrong calculator (e.g., consultancy with calendar strategy got TASK_DRIVEN instead of CALENDAR).

Fix:

  • Add STRATEGY_DEFAULT_VELOCITY_CALC mapping to ceremony_policy.py -- each strategy maps to its natural default calculator
  • resolve_ceremony_policy() now derives the velocity calculator default from the resolved strategy
  • All 9 builtin template YAMLs explicitly declare velocity_calculator alongside strategy (belt and suspenders)
  • Parametrized tests validate the mapping covers all 8 strategies and all 9 templates render correctly

#978: Strategy migration UX -- detection, notifications, velocity reset

Problem: When a ceremony scheduling strategy changes between sprints, the system had no mechanism to detect the change, notify stakeholders, or communicate the velocity window reset.

Solution:

New module strategy_migration.py:

  • StrategyMigrationInfo frozen model with model_validator enforcing previous_strategy != new_strategy
  • detect_strategy_migration() pure function for change detection
  • format_migration_warning() and format_reorder_prompt() pure text formatters
  • notify_strategy_migration() async best-effort notification via AgentMessenger (broadcast ANNOUNCEMENT + channel TASK_UPDATE)

CeremonyScheduler.activate_sprint() changes:

  • Captures previous strategy type before deactivation (previously lost when _deactivate_sprint_unlocked() cleared _active_strategy)
  • Returns StrategyMigrationInfo | None (was None)
  • Logs SPRINT_CEREMONY_STRATEGY_CHANGED event on detection

Test plan

  • 29 new tests for strategy_migration.py (model validation, detection, formatters, notification happy/error paths, MemoryError/RecursionError propagation from both broadcast and send_message)
  • 5 new tests for scheduler integration (first activation, same strategy, strategy change, velocity history, activation failure)
  • 8 new tests for strategy-aware velocity defaults (parametrized over all 8 strategies + override + multi-level resolution)
  • 10 new tests for template velocity calculators (all 9 templates + coverage matrix)
  • All 12,811 existing tests pass with no regressions

Review coverage

Pre-reviewed by 6 agents (code-reviewer, silent-failure-hunter, type-design-analyzer, pr-test-analyzer, docs-consistency, issue-resolution-verifier). 11 findings addressed.

Closes #976
Closes #978

Copilot AI review requested due to automatic review settings April 3, 2026 09:48
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5764f3af-4ea1-4dfd-b85d-303fd5b0b5d0

📥 Commits

Reviewing files that changed from the base of the PR and between 7b0fc89 and d0222b0.

📒 Files selected for processing (20)
  • CLAUDE.md
  • docs/design/ceremony-scheduling.md
  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/engine/workflow/ceremony_policy.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/engine/workflow/strategy_migration.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/consultancy.yaml
  • src/synthorg/templates/builtins/data_team.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/solo_founder.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • tests/unit/engine/workflow/test_ceremony_policy.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_strategy_migration.py
  • tests/unit/templates/test_workflow_config_integration.py
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations -- Python 3.14 has PEP 649 native lazy annotations.
Use PEP 758 except syntax: use except A, B: (no parentheses) -- ruff enforces this on Python 3.14.
Require type hints on all public functions; use mypy strict mode.
Use Google-style docstrings; required on public classes and functions (enforced by ruff D rules).
Create new objects instead of mutating existing ones. Use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement in non-Pydantic internal collections.
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (with model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields.
Use Pydantic v2 with allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time.
Use @computed_field for derived values instead of storing and validating redundant fields (e.g., TokenUsage.total_tokens).
Use NotBlankStr from core.types for all identifier/name fields (including optional NotBlankStr | None and tuple variants) instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Maintain line length of 88 characters (ruff enforced).
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly; never silently swallow exceptions.
Validate at system boundaries (user input, external APIs, config files).

Files:

  • tests/unit/templates/test_workflow_config_integration.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/engine/workflow/ceremony_policy.py
  • tests/unit/engine/workflow/test_ceremony_policy.py
  • tests/unit/engine/workflow/test_strategy_migration.py
  • src/synthorg/engine/workflow/strategy_migration.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest markers @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, and @pytest.mark.slow to categorize tests.
Maintain 80% minimum test coverage (enforced in CI).
Use asyncio_mode = "auto" for async tests; no manual @pytest.mark.asyncio needed.
Set global test timeout to 30 seconds per test (defined in pyproject.toml); do not add per-file pytest.mark.timeout(30) markers unless overriding with non-default values.
Use @pytest.mark.parametrize for testing similar cases across all test files.
Use Hypothesis for property-based testing with @given and @settings decorators. Profiles: ci (50 examples, default) and dev (1000 examples), controlled via HYPOTHESIS_PROFILE env var.
Never skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. Mock time.monotonic() and asyncio.sleep() for timing-sensitive tests. Use asyncio.Event().wait() for indefinite blocking instead of asyncio.sleep(large_number).

Files:

  • tests/unit/templates/test_workflow_config_integration.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_ceremony_policy.py
  • tests/unit/engine/workflow/test_strategy_migration.py

⚙️ CodeRabbit configuration file

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

Files:

  • tests/unit/templates/test_workflow_config_integration.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_ceremony_policy.py
  • tests/unit/engine/workflow/test_strategy_migration.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic MUST import and use get_logger from synthorg.observability, then assign logger = get_logger(__name__).
Never use import logging, logging.getLogger(), or print() in application code. Exceptions: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr) for handler construction.
Always use variable name logger (not _logger, not log) for the logger instance.
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.
Use structured kwargs for logging: always logger.info(EVENT, key=value) -- never logger.info("msg %s", val).
All error paths must log at WARNING or ERROR with context before raising.
All state transitions must log at INFO.
Use DEBUG level for object creation, internal flow, entry/exit of key functions.
Set RetryConfig and RateLimiterConfig per-provider in ProviderConfig.
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 size aliases. Vendor names only in: (1) Operations design page, (2) .claude/ files, (3) third-party import paths, (4) provider presets. Tests use test-provider, test-small-001, etc.

Files:

  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/engine/workflow/ceremony_policy.py
  • src/synthorg/engine/workflow/strategy_migration.py
src/**/*.py

⚙️ CodeRabbit configuration file

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

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • CLAUDE.md
📚 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 tests/**/*.py : Fix flaky tests completely and fundamentally; for timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/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-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-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-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-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
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-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-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/ceremony_policy.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 **/*.py : Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.

Applied to files:

  • src/synthorg/engine/workflow/ceremony_policy.py
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax with `except A, B:` (no parentheses) for multiple exceptions—ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.py
📚 Learning: 2026-04-03T10:53:55.087Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T10:53:55.087Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) -- ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly; never silently swallow exceptions

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly, never silently swallow exceptions

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly—never silently swallow exceptions.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.py
📚 Learning: 2026-04-03T10:53:55.087Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T10:53:55.087Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.py
🪛 LanguageTool
docs/design/ceremony-scheduling.md

[grammar] ~748-~748: Ensure spelling is correct
Context: ...Explicit velocity_calculator in all 9 builtin template ceremony policies ### Follow-...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (39)
CLAUDE.md (1)

98-98: Documentation update is aligned with engine scope.

Line 98 accurately reflects the new workflow strategy migration capability in the engine package description.

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

378-378: Hybrid strategy + explicit velocity calculator looks correct.

Line 378 cleanly makes the template explicit and deterministic for ceremony policy resolution.

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

128-128: Good explicit ceremony-policy defaulting.

Line 128 removes ambiguity by declaring the velocity calculator directly in the template config.

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

77-77: Consistent task-driven policy declaration.

Line 77 matches the selected strategy and keeps template behavior explicit.

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

192-192: Event-driven template config is now explicit and clear.

Line 192 is a solid addition for predictable ceremony policy resolution.

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

119-119: Startup sprint policy now has explicit velocity behavior.

Line 119 is consistent with the task-driven strategy and improves config transparency.

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

182-182: Hybrid product template mapping is well-defined.

Line 182 makes the sprint policy explicit and avoids hidden defaults.

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

108-108: Data-team ceremony policy is consistently explicit.

Line 108 correctly mirrors the strategy and improves template determinism.

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

111-113: LGTM!

The explicit velocity_calculator: "calendar" declaration correctly aligns with the strategy: "calendar" setting, matching the strategy-to-velocity-calculator mapping defined in STRATEGY_DEFAULT_VELOCITY_CALC.

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

173-175: LGTM!

The explicit velocity_calculator: "multi_dimensional" correctly aligns with the strategy: "hybrid" setting per the STRATEGY_DEFAULT_VELOCITY_CALC mapping.

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

609-645: LGTM!

The new velocity calculator test coverage is comprehensive:

  • Matrix completeness check ensures all builtins are covered
  • Raw template assertion (lines 639-641) enforces that YAMLs explicitly declare velocity_calculator rather than relying on derived defaults
  • Rendered config assertion confirms the value flows through correctly

This addresses the previous review feedback about testing the template source, not just the rendered output.

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

241-311: LGTM!

Excellent test coverage for the strategy-aware velocity calculator defaults:

  • Exhaustive mapping verification ensures no strategy is missed
  • Parametrized tests cover all 8 strategies
  • Override scenarios test the resolution precedence (explicit > strategy default, department > project, ceremony > department)
src/synthorg/engine/workflow/__init__.py (1)

69-75: LGTM!

The new public API surface is correctly exposed:

  • STRATEGY_DEFAULT_VELOCITY_CALC for policy resolution
  • Strategy migration utilities (StrategyMigrationInfo, detect_strategy_migration, format_migration_warning, format_reorder_prompt, notify_strategy_migration)

The __all__ list maintains alphabetical ordering.

Also applies to: 87-140

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

381-532: LGTM!

Comprehensive strategy migration test coverage:

  • First activation (no migration expected)
  • Same strategy re-activation (no migration expected)
  • Strategy change detection with correct field values
  • Velocity history size tracking
  • Exception propagation on activation failure
  • State cleanup verification after failed activation

The test structure properly isolates failure scenarios using locally-defined strategy subclasses.

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

84-118: LGTM!

New observability event constants are well-structured:

  • SPRINT_CEREMONY_DEACTIVATION_HOOK_FAILED: Teardown hook errors
  • SPRINT_CEREMONY_POLICY_CONFIG_CONFLICT: Config validation conflicts
  • SPRINT_CEREMONY_NOTIFICATION_FAILED: Best-effort notification failures
  • SPRINT_CEREMONY_STRATEGY_HOOK_FAILED: Strategy lifecycle hook failures

Each constant has a descriptive docstring explaining when it's emitted.

src/synthorg/engine/workflow/ceremony_policy.py (3)

65-84: LGTM!

The STRATEGY_DEFAULT_VELOCITY_CALC mapping is well-implemented:

  • Uses MappingProxyType for read-only enforcement per coding guidelines
  • Covers all 8 CeremonyStrategyType members exhaustively
  • Clear docstring explains the purpose and usage

163-172: LGTM!

Correct event constant usage: SPRINT_CEREMONY_POLICY_CONFIG_CONFLICT is semantically appropriate for the validation warning about conflicting configuration (threshold set but auto-transition disabled), distinguishing it from the normal SPRINT_CEREMONY_POLICY_RESOLVED event.


201-208: LGTM!

The direct lookup STRATEGY_DEFAULT_VELOCITY_CALC[strategy] (line 203) correctly fails fast on unmapped strategies instead of silently falling back, as the tests assert exhaustive coverage. This addresses the previous review feedback.

docs/design/ceremony-scheduling.md (3)

554-554: LGTM!

The return type update accurately reflects the new activate_sprint() signature that returns migration info when a strategy change is detected.


616-621: LGTM!

The updated documentation correctly describes the new behavior: logging SPRINT_CEREMONY_STRATEGY_CHANGED at INFO level instead of a warning, and clarifying that raw records are preserved while computed metrics don't carry over.


739-749: LGTM!

The roadmap section comprehensively documents all components shipped in #978: the migration model, detection/formatting helpers, notification function, scheduler integration, event logging, and velocity calculator defaults.

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

23-29: LGTM!

New imports are correctly organized: CeremonyStrategyType from ceremony_policy, migration utilities from strategy_migration, and new event constants for deactivation/strategy hook failures and strategy change logging.

Also applies to: 38-39


133-165: LGTM!

Return type and docstring updates clearly document:

  • The new StrategyMigrationInfo | None return type
  • Caller responsibility for invoking notify_strategy_migration()
  • Exception handling behavior with MemoryError/RecursionError propagation

167-170: LGTM!

Correctly captures both previous_strategy_type and previous_velocity_history_size from the current state before any modifications occur, addressing the prior review concern about using the incoming history size instead of the outgoing one.


217-239: LGTM!

Clean helper method that encapsulates migration detection and logging. The structured log includes all relevant context: sprint_id, previous/new strategy values, and velocity history size.


264-270: LGTM!

Using the dedicated SPRINT_CEREMONY_DEACTIVATION_HOOK_FAILED event constant improves observability by distinguishing deactivation hook failures from normal scheduler stops.


331-337: LGTM!

The early return after SPRINT_CEREMONY_STRATEGY_HOOK_FAILED prevents ceremony evaluation and auto-transition from proceeding with potentially inconsistent state. Including sprint_id in the log improves traceability.


189-215: Design intent is confirmed: migration is intentionally not returned on activation failure.

The test test_activation_failure_does_not_return_migration explicitly validates that activation failures do not return migration info. The scheduler's behavior is documented (in the activate_sprint docstring) and tested. No action needed.

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

1-28: LGTM!

Module setup follows all conventions: clear docstring, proper TYPE_CHECKING pattern for type-only imports, and logger initialization via get_logger(__name__).


31-77: LGTM!

The StrategyMigrationInfo model is well-designed:

  • Frozen for immutability
  • allow_inf_nan=False prevents invalid numeric values
  • NotBlankStr for sprint_id ensures non-empty identifiers
  • ge=0 constraint on velocity_history_size
  • model_validator enforces the invariant that strategies must differ

80-111: LGTM!

Pure detection function with clear semantics: returns None for first activation or unchanged strategy, returns populated info when strategies differ. The early-return pattern keeps the logic readable.


114-162: LGTM!

Both formatters are pure functions that produce clear, actionable messages. The updated format_reorder_prompt now includes the sprint_id and correctly states that velocity records are "preserved but velocity-based ordering from the previous strategy is no longer valid."


165-187: LGTM!

The _send_best_effort helper correctly implements best-effort semantics:

  • Re-raises MemoryError and RecursionError (system-critical)
  • Logs other exceptions at WARNING with exc_info=True for debugging
  • Accepts extra context kwargs for richer log messages

190-239: LGTM!

The notification function correctly:

  • Broadcasts an ANNOUNCEMENT with migration warning (high priority)
  • Sends a TASK_UPDATE to the responsible role (high priority)
  • Uses _send_best_effort so failures don't cascade
  • Supports customizable responsible_role and channel parameters

The calls match the AgentMessenger.broadcast() and send_message() signatures from the context snippets.

tests/unit/engine/workflow/test_strategy_migration.py (5)

1-17: LGTM!

Clean imports covering all necessary test dependencies: pytest fixtures, Pydantic ValidationError, message enums, and all functions under test.


22-82: LGTM!

Thorough model validation tests covering:

  • Basic construction and field assignment
  • Immutability (frozen) enforcement
  • Same-strategy rejection via model validator
  • Blank sprint_id rejection (NotBlankStr)
  • Negative velocity_history_size rejection (ge=0)

87-150: LGTM!

Detection tests comprehensively cover all branches:

  • None previous strategy returns None
  • Same strategy returns None
  • Different strategies return populated info
  • Parametrized test validates all strategy transitions from TASK_DRIVEN

156-216: LGTM!

Format helper tests verify that output strings contain all expected components: strategy names, sprint_id, velocity history size, and key phrases like "preserved".


222-325: LGTM!

Notification tests are comprehensive:

  • Verifies broadcast and send_message are called with correct message types and priorities
  • Tests failure isolation (broadcast failure doesn't prevent send_message)
  • Tests exception swallowing for RuntimeError
  • Tests MemoryError and RecursionError propagation from both calls
  • Verifies content matches formatter output
  • Verifies custom channel and role are forwarded correctly

Walkthrough

Adds strategy-migration support to ceremony scheduling: new module src/synthorg/engine/workflow/strategy_migration.py (defines StrategyMigrationInfo, detection, formatting, and async best-effort notification helpers). CeremonyScheduler.activate_sprint() return type changed to StrategyMigrationInfo | None; it snapshots prior strategy and velocity-history size, invokes migration detection, and logs SPRINT_CEREMONY_STRATEGY_CHANGED when a migration is detected. Introduces STRATEGY_DEFAULT_VELOCITY_CALC used by policy resolution, new observability event constants, per-template velocity_calculator fields, and unit tests for policy resolution, scheduler migration, and migration utilities.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: adding strategy-aware velocity calculator defaults to ceremony templates and implementing strategy migration UX.
Description check ✅ Passed The description comprehensively explains the problem, solution, and test coverage for both features (#976 and #978) addressed in the PR.
Linked Issues check ✅ Passed The PR successfully implements all requirements from linked issues #976 and #978: strategy-aware velocity defaults, explicit template configuration, strategy change detection, and migration notifications.
Out of Scope Changes check ✅ Passed All changes directly support the two linked issues: template YAML updates, velocity calculator defaults, strategy migration detection/notification, and supporting tests with no extraneous modifications.
Docstring Coverage ✅ Passed Docstring coverage is 53.23% which is sufficient. The required threshold is 40.00%.

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


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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Dependency Review

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

Snapshot Warnings

⚠️: No snapshots were found for the head SHA d0222b0.
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

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 3, 2026

Codecov Report

❌ Patch coverage is 98.38710% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 91.79%. Comparing base (ef5d3c1) to head (d0222b0).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/engine/workflow/ceremony_scheduler.py 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1031      +/-   ##
==========================================
+ Coverage   91.78%   91.79%   +0.01%     
==========================================
  Files         677      678       +1     
  Lines       37569    37628      +59     
  Branches     3740     3744       +4     
==========================================
+ Hits        34481    34539      +58     
- Misses       2445     2446       +1     
  Partials      643      643              

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

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

Copy link
Copy Markdown

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

This PR improves ceremony scheduling by (1) making velocity calculator defaults strategy-aware and explicit in builtin templates, and (2) adding strategy migration detection/UX primitives (migration info, formatting, and notifications) surfaced via CeremonyScheduler.activate_sprint().

Changes:

  • Add STRATEGY_DEFAULT_VELOCITY_CALC and update resolve_ceremony_policy() to default velocity_calculator based on the resolved strategy.
  • Introduce strategy_migration.py (model + detection + formatters + best-effort notifications) and return StrategyMigrationInfo | None from CeremonyScheduler.activate_sprint().
  • Enrich all 9 builtin templates with explicit ceremony_policy.velocity_calculator and add/extend tests to validate defaults and template rendering.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/unit/templates/test_workflow_config_integration.py Adds builtin template matrix tests for velocity_calculator.
tests/unit/engine/workflow/test_strategy_migration.py New unit tests for migration detection, formatting, and notification behavior.
tests/unit/engine/workflow/test_ceremony_scheduler.py Adds integration tests for migration detection via activate_sprint().
tests/unit/engine/workflow/test_ceremony_policy.py Adds tests ensuring velocity calculator defaults derive from strategy and mapping coverage.
src/synthorg/templates/builtins/startup.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/solo_founder.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/research_lab.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/product_team.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/full_company.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/dev_shop.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/data_team.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/consultancy.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/templates/builtins/agency.yaml Explicitly sets ceremony_policy.velocity_calculator.
src/synthorg/observability/events/workflow.py Updates the event docstring for SPRINT_CEREMONY_STRATEGY_CHANGED.
src/synthorg/engine/workflow/strategy_migration.py Adds migration model, detection, formatting helpers, and notification sender.
src/synthorg/engine/workflow/ceremony_scheduler.py Captures previous strategy type before deactivation; returns/logs migration info.
src/synthorg/engine/workflow/ceremony_policy.py Adds strategy→velocity-calc mapping and uses it during policy resolution.
src/synthorg/engine/workflow/init.py Re-exports new migration and mapping symbols.
docs/design/ceremony-scheduling.md Updates documented scheduler signature and adds “Shipped in #978” section.
CLAUDE.md Updates repo structure description to mention “strategy migration”.

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

Comment on lines +182 to +187
try:
await messenger.broadcast(
content=format_migration_warning(info),
message_type=MessageType.ANNOUNCEMENT,
priority=MessagePriority.HIGH,
)
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

notify_strategy_migration() calls messenger.broadcast() without a channel= argument, so it will use AgentMessenger.broadcast's default channel (currently #all-hands). Given this function already takes a channel parameter for the migration UX, it seems likely the announcement should go to the same sprint/planning channel (or a dedicated broadcast channel) rather than company-wide all-hands. Consider adding a broadcast_channel parameter (defaulting to channel) or passing channel=channel into broadcast() so the warning goes to the intended audience.

Copilot uses AI. Check for mistakes.
Comment on lines +736 to +745
### Shipped in #978 (Strategy Migration UX)

- `StrategyMigrationInfo` frozen model (sprint_id, previous/new strategy, velocity_window_reset, history size)
- `detect_strategy_migration()` pure function for change detection
- `format_migration_warning()` and `format_reorder_prompt()` pure text formatters
- `notify_strategy_migration()` async best-effort notification via `AgentMessenger`
- `CeremonyScheduler.activate_sprint()` captures previous strategy type before deactivation, returns `StrategyMigrationInfo | None`
- `SPRINT_CEREMONY_STRATEGY_CHANGED` event logged on detection
- Strategy-aware velocity calculator default in `resolve_ceremony_policy()` (`STRATEGY_DEFAULT_VELOCITY_CALC` mapping)
- Explicit `velocity_calculator` in all 9 builtin template ceremony policies
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

The design doc says StrategyMigrationInfo includes a velocity_window_reset field, but the actual model only exposes velocity_history_size (and the text in the module refers to a rolling-average reset, not a boolean field). Please update this section to reflect the real API surface (or add the documented field) so the doc doesn’t drift from the shipped implementation.

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +200
sprint.id,
len(velocity_history),
)
if migration is not None:
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

activate_sprint() reports that the velocity rolling-average window has been reset on strategy migration, but the scheduler still stores and forwards the provided velocity_history into CeremonyEvalContext (self._velocity_history = velocity_history) even when a migration is detected. This means downstream strategy evaluations can still “see” pre-migration velocity history, which contradicts the reset semantics and could lead to incorrect behavior once any strategy starts using context.velocity_history. Consider clearing self._velocity_history (or overriding the context to an empty history) when migration is not None, while still recording len(velocity_history) in the returned StrategyMigrationInfo for messaging/UI.

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

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

Choose a reason for hiding this comment

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

Code Review

This pull request implements strategy migration UX for ceremony scheduling, introducing the StrategyMigrationInfo model and logic to detect strategy changes between sprints. It adds strategy-aware default velocity calculators, notification mechanisms for migration warnings and action items, and updates builtin templates with explicit velocity calculator settings. Feedback identifies critical syntax errors in strategy_migration.py where multiple exceptions are caught without the required tuple syntax, which would lead to runtime failures.

message_type=MessageType.ANNOUNCEMENT,
priority=MessagePriority.HIGH,
)
except MemoryError, RecursionError:
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.

critical

In Python 3, catching multiple exceptions requires them to be enclosed in a tuple. The current syntax except MemoryError, RecursionError: is invalid and will result in a SyntaxError. In Python 2, this syntax was used for exception aliasing (e.g., except Exception, e:), but even then, it would catch MemoryError and assign it to the name RecursionError, which is likely not what was intended here.

    except (MemoryError, RecursionError):

message_type=MessageType.TASK_UPDATE,
priority=MessagePriority.HIGH,
)
except MemoryError, RecursionError:
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.

critical

This line contains the same syntax error as line 188. Multiple exceptions must be caught using a tuple: except (MemoryError, RecursionError):.

    except (MemoryError, RecursionError):

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@src/synthorg/engine/workflow/ceremony_policy.py`:
- Around line 201-204: The code currently uses
STRATEGY_DEFAULT_VELOCITY_CALC.get(strategy, _DEFAULT_VELOCITY_CALC) which
silently falls back to _DEFAULT_VELOCITY_CALC for unmapped strategies; change
this to a direct lookup (e.g., default_vel_calc =
STRATEGY_DEFAULT_VELOCITY_CALC[strategy]) or explicitly raise a clear error if
the key is missing so additions to strategy must update the mapping; reference
symbols: default_vel_calc, STRATEGY_DEFAULT_VELOCITY_CALC,
_DEFAULT_VELOCITY_CALC, and strategy.

In `@src/synthorg/engine/workflow/ceremony_scheduler.py`:
- Around line 152-154: Capture the outgoing strategy's velocity history length
before you overwrite it: compute outgoing_size =
len(self._active_strategy.velocity_history) (guarded by self._active_strategy is
not None and attribute existence) and assign that to
StrategyMigrationInfo.velocity_history_size instead of using
len(velocity_history) (which is the new incoming history). Update the same logic
in the second migration branch (the similar block around lines 194-199) so both
places record the old strategy's history size before replacing/clearing it, and
keep previous_strategy_type assignment as-is.

In `@tests/unit/templates/test_workflow_config_integration.py`:
- Around line 631-640: The test test_builtin_ceremony_policy_velocity_calculator
currently only asserts the rendered config; update it to also assert the raw
template declares the field by checking the loaded template before rendering:
after loaded = load_template(name) assert that the template source contains a
ceremony_policy with a velocity_calculator equal to expected_calc (e.g., inspect
loaded["workflow"]["sprint"]["ceremony_policy"]["velocity_calculator"] or the
equivalent attribute on loaded) and then continue to render_template(loaded) and
assert the rendered policy equals VelocityCalcType(expected_calc); this ensures
the YAML declares the field rather than relying on derived values.
🪄 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: b0734434-3ed4-4c84-95e7-eb1335ab023f

📥 Commits

Reviewing files that changed from the base of the PR and between 7639021 and 7f27b13.

📒 Files selected for processing (20)
  • CLAUDE.md
  • docs/design/ceremony-scheduling.md
  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/engine/workflow/ceremony_policy.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/engine/workflow/strategy_migration.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/consultancy.yaml
  • src/synthorg/templates/builtins/data_team.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/solo_founder.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • tests/unit/engine/workflow/test_ceremony_policy.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_strategy_migration.py
  • tests/unit/templates/test_workflow_config_integration.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 Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations in Python code -- Python 3.14 has PEP 649
Use PEP 758 except syntax: except A, B: (no parentheses) -- ruff enforces this on Python 3.14
All public functions must have type hints and pass mypy strict mode
Docstrings must use Google style and are required on all public classes/functions (enforced by ruff D rules)
Create new objects instead of mutating existing ones. 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 (BaseModel, model_validator, computed_field, ConfigDict). Always use allow_inf_nan=False in ConfigDict declarations to reject NaN/Inf in numeric fields at validation time.
Use @computed_field for derived values instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens)
Use NotBlankStr (from core.types) for all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Maximum line length: 88 characters (enforced by ruff)
Functions must be less than 50 lines, files must be less than 800 lines

Files:

  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_ceremony_policy.py
  • src/synthorg/engine/workflow/ceremony_policy.py
  • tests/unit/engine/workflow/test_strategy_migration.py
  • src/synthorg/engine/workflow/__init__.py
  • tests/unit/templates/test_workflow_config_integration.py
  • src/synthorg/engine/workflow/strategy_migration.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 (exceptions: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr))
Logger variable name must always be logger (not _logger, not log)
Always use event 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 level
Use DEBUG level for object creation, internal flow, entry/exit of key functions
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small.
In Pydantic models, use allow_inf_nan=False in all ConfigDict declarations
Pure data models, enums, and re-exports do NOT need logging

Files:

  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/engine/workflow/ceremony_policy.py
  • src/synthorg/engine/workflow/__init__.py
  • src/synthorg/engine/workflow/strategy_migration.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: All test files must use markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Async tests: use asyncio_mode = "auto" -- no manual @pytest.mark.asyncio needed
Test timeout: 30 seconds per test (global in pyproject.toml -- do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed)
Prefer @pytest.mark.parametrize for testing similar cases
For property-based testing in Python, use Hypothesis (@given + @settings). Profiles: ci (50 examples, default) and dev (1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n 8 -k properties
Never skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For tasks that must block indefinitely, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_ceremony_policy.py
  • tests/unit/engine/workflow/test_strategy_migration.py
  • tests/unit/templates/test_workflow_config_integration.py
🧠 Learnings (14)
📚 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
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: CLI (Go binary): see `cli/CLAUDE.md` for commands, flags, and reference; use `go -C cli` (never `cd cli`)

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/core/**/*.py : Core module must contain shared domain models, base classes, resilience config (RetryConfig, RateLimiterConfig)

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/ceremony_policy.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 **/*.py : Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/ceremony_policy.py
🔇 Additional comments (15)
src/synthorg/templates/builtins/research_lab.yaml (1)

128-128: Explicit velocity calculator default is correctly added.
This aligns the template with the strategy-aware defaulting rollout and keeps builtin config explicit.

CLAUDE.md (1)

98-98: Engine package description update is accurate.
The added “strategy migration” mention keeps architecture docs in sync with the new workflow capability.

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

77-77: Good explicit pairing of strategy and velocity calculator.
No concerns with this template update.

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

378-378: Template ceremony policy update looks correct.
The explicit multi_dimensional calculator for the hybrid strategy is consistent with the migration/defaulting work.

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

182-182: Looks good — explicit velocity calculator is correctly set.
This keeps the builtin policy deterministic and testable.

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

119-119: Good change — ceremony policy is now explicit.
No issues found with this update.

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

113-113: Calendar template policy update is correct.
The explicit calculator selection is consistent with the PR’s builtin-template policy standardization.

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

91-91: Docstring cleanup is good and non-breaking.
The event constant remains stable while the description is now current.

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

69-75: LGTM!

The new re-exports for STRATEGY_DEFAULT_VELOCITY_CALC and the strategy-migration API (StrategyMigrationInfo, detect_strategy_migration, format_migration_warning, format_reorder_prompt, notify_strategy_migration) are correctly imported and added to __all__ in alphabetical order. The module maintains its role as a clean public API surface for the workflow package.

Also applies to: 90-90, 114-114, 130-133

docs/design/ceremony-scheduling.md (1)

554-554: LGTM!

The design documentation accurately reflects the implementation:

  • The activate_sprint() return type is updated to StrategyMigrationInfo | None
  • The "Shipped in #978" section comprehensively documents all new entities and their integration points

Also applies to: 736-746

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

1-288: LGTM!

Comprehensive test coverage for the strategy migration module:

  • Model validation tests (construction, frozen immutability, same-strategy rejection, blank ID rejection, negative history rejection)
  • Detection function tests (None previous, same strategy, different strategy, parametrized all-strategy coverage)
  • Format helper tests (content assertions)
  • Notification tests (broadcast/send_message calls, error handling, fatal exception propagation, custom channel/role forwarding)

The use of ClassVar for the parametrize list and the thorough coverage of both happy paths and error conditions is well done.

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

29-74: LGTM!

The StrategyMigrationInfo model is well-designed:

  • Frozen with allow_inf_nan=False as required
  • Uses NotBlankStr for the identifier field
  • The model_validator correctly enforces that previous_strategy and new_strategy must differ
  • Clear, informative docstrings following Google style

77-108: LGTM!

The detect_strategy_migration() function correctly handles all cases:

  • Returns None for first activation (no previous strategy)
  • Returns None when strategy type is unchanged
  • Returns populated StrategyMigrationInfo when strategies differ

Clean, simple logic with comprehensive docstring.


111-155: LGTM!

Pure formatting functions that produce clear, actionable text. The docstrings correctly note these are pure functions with no I/O, making them safe to call from any context.


158-214: LGTM!

The notify_strategy_migration() function implements best-effort semantics correctly:

  • Fatal errors (MemoryError, RecursionError) propagate as required
  • Non-fatal errors are logged at WARNING with context (note= differentiates broadcast vs send_message failures)
  • Both notifications are attempted independently (broadcast failure doesn't prevent reorder prompt)

The broad except Exception catch aligns with the documented best-effort contract, ensuring notification failures don't disrupt sprint activation. The PEP 758 except MemoryError, RecursionError: syntax is correct for Python 3.14.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

167-169: ⚠️ Potential issue | 🟠 Major

Use the outgoing velocity window when building StrategyMigrationInfo.

Line 228 is counting the incoming velocity_history tuple for the new activation, but velocity_history_size is defined as the discarded history from the previous strategy. If the caller resets the new window on migration, the log and notification will incorrectly say 0 even though the old strategy had accumulated history.

🐛 Proposed fix
         async with self._lock:
             previous_strategy_type = (
                 self._active_strategy.strategy_type if self._active_strategy else None
             )
+            previous_velocity_history_size = (
+                len(self._velocity_history) if self._active_strategy else 0
+            )
 
             if self._running:
                 await self._deactivate_sprint_unlocked()
@@
             return self._detect_migration(
                 previous_strategy_type,
                 strategy,
                 sprint,
-                velocity_history,
+                previous_velocity_history_size,
             )
 
     def _detect_migration(
         self,
         previous_strategy_type: CeremonyStrategyType | None,
         strategy: CeremonySchedulingStrategy,
         sprint: Sprint,
-        velocity_history: tuple[VelocityRecord, ...],
+        previous_velocity_history_size: int,
     ) -> StrategyMigrationInfo | None:
         """Detect and log a strategy migration (if any)."""
         migration = detect_strategy_migration(
             previous_strategy_type,
             strategy.strategy_type,
             sprint.id,
-            len(velocity_history),
+            previous_velocity_history_size,
         )

Also applies to: 209-228

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

In `@src/synthorg/engine/workflow/ceremony_scheduler.py` around lines 167 - 169,
When constructing StrategyMigrationInfo during strategy migration, use the
outgoing strategy's discarded/history window (the previous strategy's velocity
history) rather than the incoming activation's new velocity_history; update the
code that computes velocity_history_size and velocity_history (near where
previous_strategy_type and self._active_strategy are referenced) to read the
outgoing window from the prior strategy instance (e.g.,
self._active_strategy.velocity_history or its configured outgoing window) and
pass that into StrategyMigrationInfo so logs/notifications reflect the discarded
history size instead of the new activation's reset history.
🤖 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/strategy_migration.py`:
- Around line 152-160: The notification string returned in the function that
builds the message using info.new_strategy, info.previous_strategy, and
info.velocity_history_size should be made actionable and accurate: include a
clear identifier of which backlog/sprint needs reordering by referencing an
existing field on info (e.g., info.backlog_id or info.sprint_name — use
whichever identifier exists in the info object), and change the wording about
velocity data to state that raw velocity records are preserved but the previous
strategy's velocity-based ordering is now invalid/has been cleared for ranking
(include info.velocity_history_size and info.previous_strategy.value in the
text). Update the returned message to explicitly name the target backlog/sprint
and to replace “reset” with precise language about preserved raw records but
invalidated ordering so the responsible role knows exactly what to update.

---

Duplicate comments:
In `@src/synthorg/engine/workflow/ceremony_scheduler.py`:
- Around line 167-169: When constructing StrategyMigrationInfo during strategy
migration, use the outgoing strategy's discarded/history window (the previous
strategy's velocity history) rather than the incoming activation's new
velocity_history; update the code that computes velocity_history_size and
velocity_history (near where previous_strategy_type and self._active_strategy
are referenced) to read the outgoing window from the prior strategy instance
(e.g., self._active_strategy.velocity_history or its configured outgoing window)
and pass that into StrategyMigrationInfo so logs/notifications reflect the
discarded history size instead of the new activation's reset history.
🪄 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: 9640d717-d8b4-452b-8818-0a9331eb708f

📥 Commits

Reviewing files that changed from the base of the PR and between 7f27b13 and b4a82c5.

📒 Files selected for processing (8)
  • docs/design/ceremony-scheduling.md
  • src/synthorg/engine/workflow/ceremony_policy.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/engine/workflow/strategy_migration.py
  • src/synthorg/observability/events/workflow.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_strategy_migration.py
  • tests/unit/templates/test_workflow_config_integration.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations in Python code -- Python 3.14 has PEP 649
Use PEP 758 except syntax: except A, B: (no parentheses) -- ruff enforces this on Python 3.14
All public functions must have type hints and pass mypy strict mode
Docstrings must use Google style and are required on all public classes/functions (enforced by ruff D rules)
Create new objects instead of mutating existing ones. 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 (BaseModel, model_validator, computed_field, ConfigDict). Always use allow_inf_nan=False in ConfigDict declarations to reject NaN/Inf in numeric fields at validation time.
Use @computed_field for derived values instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens)
Use NotBlankStr (from core.types) for all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Maximum line length: 88 characters (enforced by ruff)
Functions must be less than 50 lines, files must be less than 800 lines

Files:

  • tests/unit/templates/test_workflow_config_integration.py
  • src/synthorg/engine/workflow/ceremony_policy.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_strategy_migration.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/observability/events/workflow.py
  • src/synthorg/engine/workflow/strategy_migration.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: All test files must use markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Async tests: use asyncio_mode = "auto" -- no manual @pytest.mark.asyncio needed
Test timeout: 30 seconds per test (global in pyproject.toml -- do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed)
Prefer @pytest.mark.parametrize for testing similar cases
For property-based testing in Python, use Hypothesis (@given + @settings). Profiles: ci (50 examples, default) and dev (1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n 8 -k properties
Never skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For tasks that must block indefinitely, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/templates/test_workflow_config_integration.py
  • tests/unit/engine/workflow/test_ceremony_scheduler.py
  • tests/unit/engine/workflow/test_strategy_migration.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 (exceptions: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr))
Logger variable name must always be logger (not _logger, not log)
Always use event 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 level
Use DEBUG level for object creation, internal flow, entry/exit of key functions
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small.
In Pydantic models, use allow_inf_nan=False in all ConfigDict declarations
Pure data models, enums, and re-exports do NOT need logging

Files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/ceremony_policy.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 **/*.py : Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/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-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-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
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-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-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax with `except A, B:` (no parentheses) for multiple exceptions—ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly; never silently swallow exceptions

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly, never silently swallow exceptions

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.py
📚 Learning: 2026-04-03T07:34:51.027Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T07:34:51.027Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) -- ruff enforces this on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly—never silently swallow exceptions.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.py
🪛 LanguageTool
docs/design/ceremony-scheduling.md

[grammar] ~748-~748: Ensure spelling is correct
Context: ...Explicit velocity_calculator in all 9 builtin template ceremony policies ### Follow-...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

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/ceremony_scheduler.py`:
- Around line 167-170: The code currently derives previous_strategy_type and
previous_velocity_history_size from in-memory _active_strategy and
_velocity_history right before activation and then clears _active_strategy on
recoverable exceptions, causing migration context to be lost across retries; fix
this by capturing and persisting the outgoing migration context to
instance-level fields (e.g., set self._pending_migration_strategy_type =
previous_strategy_type and self._pending_velocity_history_size =
previous_velocity_history_size) before calling
on_sprint_activated()/_fire_sprint_start_ceremonies(), use these pending fields
in _detect_migration() and for strategy-change logging/notifications, and only
clear the pending fields upon the first successful activation (or on a
non-recoverable final abort); avoid clearing self._active_strategy or the
pending fields in the exception/retry path so retries see the original migration
context.
- Around line 144-146: CeremonyScheduler's activate_sprint() is never invoked so
StrategyMigrationInfo is never produced; add orchestration where sprint state
transitions to ACTIVE (inside the ceremony/sprint transition handler that wires
CeremonyScheduler into AppState) to call CeremonyScheduler.activate_sprint(),
check its return (if not None) and immediately call
CeremonyScheduler.notify_strategy_migration() with that StrategyMigrationInfo
and the appropriate AgentMessenger instance (obtain the same AgentMessenger used
elsewhere in AppState orchestration), ensuring this path is triggered whenever a
sprint becomes ACTIVE so SPRINT_CEREMONY_STRATEGY_CHANGED results in user-facing
notifications.
🪄 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: 49223b2f-65d3-48e7-996c-a7392c00ddc1

📥 Commits

Reviewing files that changed from the base of the PR and between b4a82c5 and 7b0fc89.

📒 Files selected for processing (3)
  • src/synthorg/engine/workflow/ceremony_scheduler.py
  • src/synthorg/engine/workflow/strategy_migration.py
  • tests/unit/engine/workflow/test_strategy_migration.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations in Python code -- Python 3.14 has PEP 649
Use PEP 758 except syntax: except A, B: (no parentheses) -- ruff enforces this on Python 3.14
All public functions must have type hints and pass mypy strict mode
Docstrings must use Google style and are required on all public classes/functions (enforced by ruff D rules)
Create new objects instead of mutating existing ones. 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 (BaseModel, model_validator, computed_field, ConfigDict). Always use allow_inf_nan=False in ConfigDict declarations to reject NaN/Inf in numeric fields at validation time.
Use @computed_field for derived values instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens)
Use NotBlankStr (from core.types) for all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Maximum line length: 88 characters (enforced by ruff)
Functions must be less than 50 lines, files must be less than 800 lines

Files:

  • tests/unit/engine/workflow/test_strategy_migration.py
  • src/synthorg/engine/workflow/strategy_migration.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: All test files must use markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Async tests: use asyncio_mode = "auto" -- no manual @pytest.mark.asyncio needed
Test timeout: 30 seconds per test (global in pyproject.toml -- do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed)
Prefer @pytest.mark.parametrize for testing similar cases
For property-based testing in Python, use Hypothesis (@given + @settings). Profiles: ci (50 examples, default) and dev (1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n 8 -k properties
Never skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For tasks that must block indefinitely, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/engine/workflow/test_strategy_migration.py

⚙️ CodeRabbit configuration file

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

Files:

  • tests/unit/engine/workflow/test_strategy_migration.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 (exceptions: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr))
Logger variable name must always be logger (not _logger, not log)
Always use event 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 level
Use DEBUG level for object creation, internal flow, entry/exit of key functions
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small.
In Pydantic models, use allow_inf_nan=False in all ConfigDict declarations
Pure data models, enums, and re-exports do NOT need logging

Files:

  • src/synthorg/engine/workflow/strategy_migration.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
src/**/*.py

⚙️ CodeRabbit configuration file

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

Files:

  • src/synthorg/engine/workflow/strategy_migration.py
  • src/synthorg/engine/workflow/ceremony_scheduler.py
🧠 Learnings (10)
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax with `except A, B:` (no parentheses) for multiple exceptions—ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly; never silently swallow exceptions

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly, never silently swallow exceptions

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.py
📚 Learning: 2026-04-03T07:34:51.027Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T07:34:51.027Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) -- ruff enforces this on Python 3.14

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.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 **/*.py : Handle errors explicitly—never silently swallow exceptions.

Applied to files:

  • src/synthorg/engine/workflow/strategy_migration.py

Comment on lines +144 to +146
The caller is responsible for invoking
``notify_strategy_migration()`` with the returned info
and an ``AgentMessenger`` when migration is detected.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Production call sites that activate a sprint:"
rg -nP --type=py -g '!tests/**' -C3 '\.activate_sprint\s*\('

echo
echo "Production usages of notify_strategy_migration:"
rg -nP --type=py -g '!tests/**' -C3 '\bnotify_strategy_migration\b'

Repository: Aureliolo/synthorg

Length of output: 107


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if the file exists
echo "=== Checking if ceremony_scheduler.py exists ==="
find . -name ceremony_scheduler.py -type f

echo
echo "=== Examining lines 144-146 and 210-215 from ceremony_scheduler.py ==="
if [ -f "src/synthorg/engine/workflow/ceremony_scheduler.py" ]; then
  echo "Lines 140-150:"
  sed -n '140,150p' src/synthorg/engine/workflow/ceremony_scheduler.py
  echo
  echo "Lines 205-220:"
  sed -n '205,220p' src/synthorg/engine/workflow/ceremony_scheduler.py
fi

echo
echo "=== Search for activate_sprint definition (any pattern) ==="
rg -nP --type=py 'def activate_sprint' -A 2

echo
echo "=== Search for notify_strategy_migration definition ==="
rg -nP --type=py 'def notify_strategy_migration' -A 2

echo
echo "=== Broader search for activate_sprint calls (including property access) ==="
rg -nP --type=py 'activate_sprint' | head -20

Repository: Aureliolo/synthorg

Length of output: 4279


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for StrategyMigrationInfo usage in production ==="
rg -nP --type=py -g '!tests/**' 'StrategyMigrationInfo'

echo
echo "=== Search for imports of strategy_migration in production ==="
rg -nP --type=py -g '!tests/**' 'from.*strategy_migration\|import.*strategy_migration'

echo
echo "=== Search for all orchestration/workflow files in production ==="
fd -e py 'src/synthorg/engine/orchestrat|src/synthorg/engine/workflow' --type f -g '!tests' | head -20

echo
echo "=== Check what calls ceremony_scheduler in production ==="
rg -nP --type=py -g '!tests/**' 'ceremony_scheduler|CeremonyScheduler'

Repository: Aureliolo/synthorg

Length of output: 1420


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for ANY production code that instantiates CeremonyScheduler ==="
rg -nP --type=py -g '!tests/**' 'CeremonyScheduler\(' -B 2 -A 2

echo
echo "=== List all production files in engine/workflow/ ==="
find src/synthorg/engine/workflow -name '*.py' -type f | sort

echo
echo "=== Check the full return type annotation of activate_sprint ==="
sed -n '126,135p' src/synthorg/engine/workflow/ceremony_scheduler.py

echo
echo "=== Check if _detect_migration is called anywhere else ==="
rg -nP --type=py '_detect_migration'

echo
echo "=== Check notify_strategy_migration signature and where it should be called ==="
sed -n '190,210p' src/synthorg/engine/workflow/strategy_migration.py

Repository: Aureliolo/synthorg

Length of output: 3554


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find where CeremonyScheduler.activate_sprint is actually called in production ==="
rg -nP --type=py -g '!tests/**' '\.activate_sprint' -B 5 -A 5

echo
echo "=== Check if notify_strategy_migration is called ANYWHERE in production ==="
rg -nP --type=py -g '!tests/**' 'notify_strategy_migration\(' 

echo
echo "=== Check auto_wire.py to see how ceremony_scheduler is used ==="
sed -n '285,320p' src/synthorg/api/auto_wire.py

Repository: Aureliolo/synthorg

Length of output: 147


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for ceremony_scheduler variable usage (broader) ==="
rg -nP --type=py -g '!tests/**' 'ceremony_scheduler' -C 3

echo
echo "=== Check the full auto_wire.py file around where ceremony_scheduler is created ==="
sed -n '280,350p' src/synthorg/api/auto_wire.py

echo
echo "=== Check if notify_strategy_migration is imported/used anywhere ==="
rg -nP --type=py -g '!tests/**' 'notify_strategy_migration'

echo
echo "=== Check sprint_lifecycle.py which likely orchestrates sprint activation ==="
head -50 src/synthorg/engine/workflow/sprint_lifecycle.py

Repository: Aureliolo/synthorg

Length of output: 12602


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for where sprint transitions happen or where lifecycle is used ==="
rg -nP --type=py -g '!tests/**' 'SprintStatus\|sprint.*transition\|activate.*sprint' -i

echo
echo "=== Check if AppState.ceremony_scheduler is accessed anywhere ==="
rg -nP --type=py -g '!tests/**' '\.ceremony_scheduler' -C 2

echo
echo "=== Check strategy_migration.py to see detect_strategy_migration function ==="
sed -n '75,115p' src/synthorg/engine/workflow/strategy_migration.py

echo
echo "=== Look for any agent or task that might orchestrate sprints ==="
rg -nP --type=py -g '!tests/**' 'sprint.*agent\|ActivateSprint\|activate.*sprint' -i | head -15

Repository: Aureliolo/synthorg

Length of output: 139


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check what imports ceremony_scheduler or uses it ==="
rg -nP --type=py -g '!tests/**' 'ceremony_scheduler' --type-list

echo
echo "=== Look for Agent implementations that might handle sprints ==="
find src/synthorg -name '*agent*.py' -type f | xargs grep -l 'sprint\|ceremony' 2>/dev/null || echo "No agent files match"

echo
echo "=== Check the actual test to understand how activate_sprint should be used ==="
sed -n '382,420p' tests/unit/engine/workflow/test_ceremony_scheduler.py

echo
echo "=== Look for any actual orchestration or workflow handler ==="
find src/synthorg -name '*orchestrat*.py' -o -name '*handler*.py' -o -name '*dispatch*.py' | head -10

Repository: Aureliolo/synthorg

Length of output: 525


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check the actual test to understand how activate_sprint should be used ==="
sed -n '382,420p' tests/unit/engine/workflow/test_ceremony_scheduler.py

echo
echo "=== Look for any orchestration or handler files ==="
find src/synthorg -type f -name '*.py' | grep -E 'orchestrat|handler|dispatch|coordinator' 

echo
echo "=== Search for files that import ceremony_scheduler (excluding tests) ==="
rg -nP --type=py -g '!tests/**' 'ceremony_scheduler' --files-with-matches

echo
echo "=== Check what the actual AppState property returns and how it's used ==="
grep -n 'def ceremony_scheduler\|_ceremony_scheduler' src/synthorg/api/state.py

Repository: Aureliolo/synthorg

Length of output: 2754


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check if MeetingOrchestrator or any coordinator uses ceremony_scheduler ==="
sed -n '1,50p' src/synthorg/communication/meeting/orchestrator.py

echo
echo "=== Check the MultiAgentCoordinator ==="
find src/synthorg/engine/coordination -name '*.py' -type f

echo
echo "=== Search for any method that takes CeremonyScheduler as parameter ==="
rg -nP --type=py -g '!tests/**' 'CeremonyScheduler' -B 2 -A 2

echo
echo "=== Look in coordination/service.py to see if it uses ceremony_scheduler ==="
rg -nP --type=py 'ceremony_scheduler\|activate_sprint' src/synthorg/engine/coordination/service.py

Repository: Aureliolo/synthorg

Length of output: 9831


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Examine MultiAgentCoordinator service.py ==="
head -100 src/synthorg/engine/coordination/service.py

echo
echo "=== Search for any sprint activation in coordination ==="
rg -nP --type=py 'sprint\|ceremony' src/synthorg/engine/coordination/service.py -i

echo
echo "=== Look for any agent that might handle sprint lifecycle ==="
find src/synthorg -path '*/agents/*.py' -o -path '*/agent/*.py' | head -10

echo
echo "=== Search for SprintStatus transitions or ACTIVE usage ==="
rg -nP --type=py -g '!tests/**' 'SprintStatus\.ACTIVE\|\.ACTIVE' | head -20

Repository: Aureliolo/synthorg

Length of output: 3590


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Final check: is activate_sprint called ANYWHERE outside of tests? ==="
rg -nP --type=py 'activate_sprint' | grep -v tests/

echo
echo "=== Final check: is notify_strategy_migration called ANYWHERE in production? ==="
rg -nP --type=py -g '!tests/**' 'notify_strategy_migration\(' 

echo
echo "=== Summary: grep all non-test python files for both together ==="
echo "Files with activate_sprint (non-test):"
rg -nP --type=py -g '!tests/**' 'activate_sprint' --files-with-matches

echo
echo "Files with notify_strategy_migration (non-test):"
rg -nP --type=py -g '!tests/**' 'notify_strategy_migration' --files-with-matches

Repository: Aureliolo/synthorg

Length of output: 1947


Integrate activate_sprint() return value into orchestration flow before production release.

activate_sprint() returns StrategyMigrationInfo | None, but no production code currently calls activate_sprint() at all—despite CeremonyScheduler being wired into AppState. This means StrategyMigrationInfo is never generated, and notify_strategy_migration() is never invoked, leaving the warning/reorder UX from #978 completely disconnected.

An orchestration path must be added that:

  1. Calls activate_sprint() when a sprint transitions to ACTIVE
  2. Passes the returned StrategyMigrationInfo (if not None) to notify_strategy_migration() with the appropriate AgentMessenger

Until then, strategy change detection occurs internally (logged via SPRINT_CEREMONY_STRATEGY_CHANGED) but produces no user-facing output.

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

In `@src/synthorg/engine/workflow/ceremony_scheduler.py` around lines 144 - 146,
CeremonyScheduler's activate_sprint() is never invoked so StrategyMigrationInfo
is never produced; add orchestration where sprint state transitions to ACTIVE
(inside the ceremony/sprint transition handler that wires CeremonyScheduler into
AppState) to call CeremonyScheduler.activate_sprint(), check its return (if not
None) and immediately call CeremonyScheduler.notify_strategy_migration() with
that StrategyMigrationInfo and the appropriate AgentMessenger instance (obtain
the same AgentMessenger used elsewhere in AppState orchestration), ensuring this
path is triggered whenever a sprint becomes ACTIVE so
SPRINT_CEREMONY_STRATEGY_CHANGED results in user-facing notifications.

Comment on lines +167 to +170
previous_strategy_type = (
self._active_strategy.strategy_type if self._active_strategy else None
)
previous_velocity_history_size = len(self._velocity_history)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve migration context across activation retries.

If on_sprint_activated() or _fire_sprint_start_ceremonies() raises a recoverable Exception, Lines 194-201 clear _active_strategy before the caller can retry. The next activate_sprint() then re-enters with previous_strategy_type is None, so _detect_migration() returns None and the strategy-change log/notifications are lost even though the sprint really did migrate. Persist the outgoing strategy/history size until the first successful activation instead of deriving it only from current in-memory state.

Also applies to: 194-215

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

In `@src/synthorg/engine/workflow/ceremony_scheduler.py` around lines 167 - 170,
The code currently derives previous_strategy_type and
previous_velocity_history_size from in-memory _active_strategy and
_velocity_history right before activation and then clears _active_strategy on
recoverable exceptions, causing migration context to be lost across retries; fix
this by capturing and persisting the outgoing migration context to
instance-level fields (e.g., set self._pending_migration_strategy_type =
previous_strategy_type and self._pending_velocity_history_size =
previous_velocity_history_size) before calling
on_sprint_activated()/_fire_sprint_start_ceremonies(), use these pending fields
in _detect_migration() and for strategy-change logging/notifications, and only
clear the pending fields upon the first successful activation (or on a
non-recoverable final abort); avoid clearing self._active_strategy or the
pending fields in the exception/retry path so retries see the original migration
context.

…solution

Add STRATEGY_DEFAULT_VELOCITY_CALC mapping to ceremony_policy.py so
resolve_ceremony_policy() derives the velocity calculator default from
the resolved strategy type instead of always falling back to TASK_DRIVEN.

Previously, templates that set only ceremony_policy.strategy (e.g.
consultancy with calendar, dev_shop with hybrid) resolved to the wrong
velocity calculator. Now the resolution uses the strategy's natural
default (calendar -> CALENDAR, hybrid -> MULTI_DIMENSIONAL, etc.).

Also add explicit velocity_calculator to all 9 builtin template YAMLs
for self-documentation alongside the strategy field.

Closes #976
…ocity reset

Implement strategy change detection at sprint boundaries and migration
notifications (#978).

New module strategy_migration.py with:
- StrategyMigrationInfo frozen model (sprint_id, previous/new strategy,
  velocity_window_reset flag, velocity_history_size)
- detect_strategy_migration() pure function for change detection
- format_migration_warning() and format_reorder_prompt() pure formatters
- notify_strategy_migration() async best-effort notification via
  AgentMessenger (broadcast ANNOUNCEMENT + channel TASK_UPDATE)

CeremonyScheduler.activate_sprint() now:
- Captures previous strategy type before deactivation
- Returns StrategyMigrationInfo | None (was None)
- Logs SPRINT_CEREMONY_STRATEGY_CHANGED event on detection

Closes #978
Pre-reviewed by 6 agents, 11 findings addressed:

- Add exc_info=True to both catch blocks in notify_strategy_migration
  (silent-failure-hunter)
- Remove velocity_window_reset dead weight field from StrategyMigrationInfo
  (type-design-analyzer -- always True, carried no information)
- Add model_validator enforcing previous_strategy != new_strategy
  (type-design-analyzer)
- Add STRATEGY_DEFAULT_VELOCITY_CALC, format_migration_warning,
  format_reorder_prompt to __init__.py re-exports (docs-consistency)
- Add strategy_migration to CLAUDE.md Package Structure (docs-consistency)
- Add tests: send_message failure path, MemoryError/RecursionError from
  send_message, blank sprint_id rejected, negative velocity_history_size
  rejected, model_validator same-strategy rejected (pr-test-analyzer)
- Strengthen test_broadcast_failure_still_sends_reorder to assert
  send_message called (pr-test-analyzer)
…pilot

- Add dedicated event constants: SPRINT_CEREMONY_NOTIFICATION_FAILED,
  SPRINT_CEREMONY_DEACTIVATION_HOOK_FAILED, SPRINT_CEREMONY_POLICY_CONFIG_CONFLICT,
  SPRINT_CEREMONY_STRATEGY_HOOK_FAILED
- Fix notify_strategy_migration: use correct event constant, NotBlankStr params,
  extract _send_best_effort helper, add Raises section, add diagnostic context
- Fix activate_sprint: extract _detect_migration helper (50-line rule),
  add Raises section, document caller contract for notify_strategy_migration
- Fix on_task_completed: skip ceremony eval after strategy hook failure (return early)
- Fix deactivation hook: use dedicated event constant instead of SCHEDULER_STOPPED
- Fix ceremony_policy: direct STRATEGY_DEFAULT_VELOCITY_CALC[strategy] lookup,
  dedicated config conflict constant, fix transition_threshold docstring range
- Fix format_migration_warning: align with spec data-preservation guarantee
- Fix design doc: velocity_window_reset -> velocity_history_size, update log
  level/message description
- Fix tests: ValidationError in test_frozen, add formatter content assertions,
  add notify content verification, add post-failure activation test,
  validate raw template YAML
- Capture previous_velocity_history_size from self._velocity_history
  before deactivation clears it, ensuring StrategyMigrationInfo
  reflects the outgoing strategy's accumulated records
- format_reorder_prompt now references sprint_id and uses precise
  language: raw records preserved, velocity-based ordering invalidated
@Aureliolo Aureliolo force-pushed the feat/ceremony-template-defaults branch from 7b0fc89 to d0222b0 Compare April 3, 2026 11:02
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 3, 2026 11:03 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit da4a8e1 into main Apr 3, 2026
34 checks passed
@Aureliolo Aureliolo deleted the feat/ceremony-template-defaults branch April 3, 2026 11:14
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 3, 2026 11:14 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Apr 3, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.5.9](v0.5.8...v0.5.9)
(2026-04-03)


### Features

* ceremony template defaults + strategy migration UX
([#1031](#1031))
([da4a8e1](da4a8e1)),
closes [#976](#976)
[#978](#978)
* hybrid search (dense + BM25 sparse) for memory retrieval pipeline
([#1016](#1016))
([fccac4a](fccac4a)),
closes [#694](#694)
* implement network hosting and multi-user access
([#1032](#1032))
([398c378](398c378)),
closes [#244](#244)
* implement visual workflow editor
([#247](#247))
([#1018](#1018))
([ef5d3c1](ef5d3c1))

---
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: ceremony strategy migration UX (warnings, notifications) feat: template default ceremony strategy assignments

2 participants