feat: ceremony template defaults + strategy migration UX#1031
feat: ceremony template defaults + strategy migration UX#1031
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (20)
📜 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)
🧰 Additional context used📓 Path-based instructions (4)**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
tests/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
⚙️ CodeRabbit configuration file
Files:
src/synthorg/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/**/*.py⚙️ CodeRabbit configuration file
Files:
🧠 Learnings (34)📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-16T06:24:56.341ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-19T07:13:44.964ZApplied to files:
📚 Learning: 2026-03-17T06:30:14.180ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-20T21:44:04.528ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-04-02T07:18:02.381ZApplied to files:
📚 Learning: 2026-03-20T11:18:48.128ZApplied to files:
📚 Learning: 2026-03-15T19:14:27.144ZApplied to files:
📚 Learning: 2026-03-18T21:23:23.586ZApplied to files:
📚 Learning: 2026-03-20T21:44:04.528ZApplied to files:
📚 Learning: 2026-03-31T16:09:24.320ZApplied to files:
📚 Learning: 2026-03-15T18:28:13.207ZApplied to files:
📚 Learning: 2026-03-15T18:38:44.202ZApplied to files:
📚 Learning: 2026-04-01T09:09:43.948ZApplied to files:
📚 Learning: 2026-03-20T21:44:04.528ZApplied to files:
📚 Learning: 2026-03-20T21:44:04.528ZApplied to files:
📚 Learning: 2026-03-31T21:07:37.470ZApplied to files:
📚 Learning: 2026-03-20T21:44:04.528ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-04-03T10:53:55.087ZApplied to files:
📚 Learning: 2026-03-15T16:55:07.730ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-04-03T10:53:55.087ZApplied to files:
🪛 LanguageTooldocs/design/ceremony-scheduling.md[grammar] ~748-~748: Ensure spelling is correct (QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1) 🔇 Additional comments (39)
WalkthroughAdds strategy-migration support to ceremony scheduling: new module Suggested labelsautorelease: tagged 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure 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 FilesNone |
Codecov Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
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_CALCand updateresolve_ceremony_policy()to defaultvelocity_calculatorbased on the resolved strategy. - Introduce
strategy_migration.py(model + detection + formatters + best-effort notifications) and returnStrategyMigrationInfo | NonefromCeremonyScheduler.activate_sprint(). - Enrich all 9 builtin templates with explicit
ceremony_policy.velocity_calculatorand 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.
| try: | ||
| await messenger.broadcast( | ||
| content=format_migration_warning(info), | ||
| message_type=MessageType.ANNOUNCEMENT, | ||
| priority=MessagePriority.HIGH, | ||
| ) |
There was a problem hiding this comment.
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.
| ### 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 |
There was a problem hiding this comment.
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.
| sprint.id, | ||
| len(velocity_history), | ||
| ) | ||
| if migration is not None: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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
📒 Files selected for processing (20)
CLAUDE.mddocs/design/ceremony-scheduling.mdsrc/synthorg/engine/workflow/__init__.pysrc/synthorg/engine/workflow/ceremony_policy.pysrc/synthorg/engine/workflow/ceremony_scheduler.pysrc/synthorg/engine/workflow/strategy_migration.pysrc/synthorg/observability/events/workflow.pysrc/synthorg/templates/builtins/agency.yamlsrc/synthorg/templates/builtins/consultancy.yamlsrc/synthorg/templates/builtins/data_team.yamlsrc/synthorg/templates/builtins/dev_shop.yamlsrc/synthorg/templates/builtins/full_company.yamlsrc/synthorg/templates/builtins/product_team.yamlsrc/synthorg/templates/builtins/research_lab.yamlsrc/synthorg/templates/builtins/solo_founder.yamlsrc/synthorg/templates/builtins/startup.yamltests/unit/engine/workflow/test_ceremony_policy.pytests/unit/engine/workflow/test_ceremony_scheduler.pytests/unit/engine/workflow/test_strategy_migration.pytests/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: Nofrom __future__ import annotationsin 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), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention andcopy.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 (usingmodel_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 useallow_inf_nan=FalseinConfigDictdeclarations to rejectNaN/Infin numeric fields at validation time.
Use@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens)
UseNotBlankStr(fromcore.types) for all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_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.pysrc/synthorg/engine/workflow/ceremony_scheduler.pytests/unit/engine/workflow/test_ceremony_scheduler.pytests/unit/engine/workflow/test_ceremony_policy.pysrc/synthorg/engine/workflow/ceremony_policy.pytests/unit/engine/workflow/test_strategy_migration.pysrc/synthorg/engine/workflow/__init__.pytests/unit/templates/test_workflow_config_integration.pysrc/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_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application code (exceptions:observability/setup.py,observability/sinks.py,observability/syslog_handler.py, andobservability/http_handler.pymay use stdlibloggingandprint(..., file=sys.stderr))
Logger variable name must always belogger(not_logger, notlog)
Always use event constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Always use structured kwargs for logging:logger.info(EVENT, key=value)-- neverlogger.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, useallow_inf_nan=Falsein allConfigDictdeclarations
Pure data models, enums, and re-exports do NOT need logging
Files:
src/synthorg/observability/events/workflow.pysrc/synthorg/engine/workflow/ceremony_scheduler.pysrc/synthorg/engine/workflow/ceremony_policy.pysrc/synthorg/engine/workflow/__init__.pysrc/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: useasyncio_mode = "auto"-- no manual@pytest.mark.asyncioneeded
Test timeout: 30 seconds per test (global inpyproject.toml-- do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed)
Prefer@pytest.mark.parametrizefor testing similar cases
For property-based testing in Python, use Hypothesis (@given+@settings). Profiles:ci(50 examples, default) anddev(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, mocktime.monotonic()andasyncio.sleep(). For tasks that must block indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/engine/workflow/test_ceremony_scheduler.pytests/unit/engine/workflow/test_ceremony_policy.pytests/unit/engine/workflow/test_strategy_migration.pytests/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.mdsrc/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 explicitmulti_dimensionalcalculator 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_CALCand 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 toStrategyMigrationInfo | None- The "Shipped in
#978" section comprehensively documents all new entities and their integration pointsAlso 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
ClassVarfor 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
StrategyMigrationInfomodel is well-designed:
- Frozen with
allow_inf_nan=Falseas required- Uses
NotBlankStrfor the identifier field- The
model_validatorcorrectly enforces thatprevious_strategyandnew_strategymust differ- Clear, informative docstrings following Google style
77-108: LGTM!The
detect_strategy_migration()function correctly handles all cases:
- Returns
Nonefor first activation (no previous strategy)- Returns
Nonewhen strategy type is unchanged- Returns populated
StrategyMigrationInfowhen strategies differClean, 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 Exceptioncatch aligns with the documented best-effort contract, ensuring notification failures don't disrupt sprint activation. The PEP 758except MemoryError, RecursionError:syntax is correct for Python 3.14.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/synthorg/engine/workflow/ceremony_scheduler.py (1)
167-169:⚠️ Potential issue | 🟠 MajorUse the outgoing velocity window when building
StrategyMigrationInfo.Line 228 is counting the incoming
velocity_historytuple for the new activation, butvelocity_history_sizeis defined as the discarded history from the previous strategy. If the caller resets the new window on migration, the log and notification will incorrectly say0even 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
📒 Files selected for processing (8)
docs/design/ceremony-scheduling.mdsrc/synthorg/engine/workflow/ceremony_policy.pysrc/synthorg/engine/workflow/ceremony_scheduler.pysrc/synthorg/engine/workflow/strategy_migration.pysrc/synthorg/observability/events/workflow.pytests/unit/engine/workflow/test_ceremony_scheduler.pytests/unit/engine/workflow/test_strategy_migration.pytests/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: Nofrom __future__ import annotationsin 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), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention andcopy.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 (usingmodel_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 useallow_inf_nan=FalseinConfigDictdeclarations to rejectNaN/Infin numeric fields at validation time.
Use@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens)
UseNotBlankStr(fromcore.types) for all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_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.pysrc/synthorg/engine/workflow/ceremony_policy.pytests/unit/engine/workflow/test_ceremony_scheduler.pytests/unit/engine/workflow/test_strategy_migration.pysrc/synthorg/engine/workflow/ceremony_scheduler.pysrc/synthorg/observability/events/workflow.pysrc/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: useasyncio_mode = "auto"-- no manual@pytest.mark.asyncioneeded
Test timeout: 30 seconds per test (global inpyproject.toml-- do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed)
Prefer@pytest.mark.parametrizefor testing similar cases
For property-based testing in Python, use Hypothesis (@given+@settings). Profiles:ci(50 examples, default) anddev(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, mocktime.monotonic()andasyncio.sleep(). For tasks that must block indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/templates/test_workflow_config_integration.pytests/unit/engine/workflow/test_ceremony_scheduler.pytests/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_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application code (exceptions:observability/setup.py,observability/sinks.py,observability/syslog_handler.py, andobservability/http_handler.pymay use stdlibloggingandprint(..., file=sys.stderr))
Logger variable name must always belogger(not_logger, notlog)
Always use event constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Always use structured kwargs for logging:logger.info(EVENT, key=value)-- neverlogger.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, useallow_inf_nan=Falsein allConfigDictdeclarations
Pure data models, enums, and re-exports do NOT need logging
Files:
src/synthorg/engine/workflow/ceremony_policy.pysrc/synthorg/engine/workflow/ceremony_scheduler.pysrc/synthorg/observability/events/workflow.pysrc/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)
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@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
📒 Files selected for processing (3)
src/synthorg/engine/workflow/ceremony_scheduler.pysrc/synthorg/engine/workflow/strategy_migration.pytests/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: Nofrom __future__ import annotationsin 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), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention andcopy.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 (usingmodel_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 useallow_inf_nan=FalseinConfigDictdeclarations to rejectNaN/Infin numeric fields at validation time.
Use@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens)
UseNotBlankStr(fromcore.types) for all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_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.pysrc/synthorg/engine/workflow/strategy_migration.pysrc/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: useasyncio_mode = "auto"-- no manual@pytest.mark.asyncioneeded
Test timeout: 30 seconds per test (global inpyproject.toml-- do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed)
Prefer@pytest.mark.parametrizefor testing similar cases
For property-based testing in Python, use Hypothesis (@given+@settings). Profiles:ci(50 examples, default) anddev(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, mocktime.monotonic()andasyncio.sleep(). For tasks that must block indefinitely, useasyncio.Event().wait()instead ofasyncio.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_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application code (exceptions:observability/setup.py,observability/sinks.py,observability/syslog_handler.py, andobservability/http_handler.pymay use stdlibloggingandprint(..., file=sys.stderr))
Logger variable name must always belogger(not_logger, notlog)
Always use event constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Always use structured kwargs for logging:logger.info(EVENT, key=value)-- neverlogger.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, useallow_inf_nan=Falsein allConfigDictdeclarations
Pure data models, enums, and re-exports do NOT need logging
Files:
src/synthorg/engine/workflow/strategy_migration.pysrc/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.pysrc/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
| The caller is responsible for invoking | ||
| ``notify_strategy_migration()`` with the returned info | ||
| and an ``AgentMessenger`` when migration is detected. |
There was a problem hiding this comment.
🧩 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 -20Repository: 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.pyRepository: 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.pyRepository: 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.pyRepository: 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 -15Repository: 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 -10Repository: 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.pyRepository: 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.pyRepository: 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 -20Repository: 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-matchesRepository: 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:
- Calls
activate_sprint()when a sprint transitions toACTIVE - Passes the returned
StrategyMigrationInfo(if notNone) tonotify_strategy_migration()with the appropriateAgentMessenger
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.
| previous_strategy_type = ( | ||
| self._active_strategy.strategy_type if self._active_strategy else None | ||
| ) | ||
| previous_velocity_history_size = len(self._velocity_history) |
There was a problem hiding this comment.
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
7b0fc89 to
d0222b0
Compare
🤖 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).
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 hardcodedTASK_DRIVENfallback forvelocity_calculatorregardless of the active strategy. 5 of 9 templates resolved to the wrong calculator (e.g., consultancy withcalendarstrategy gotTASK_DRIVENinstead ofCALENDAR).Fix:
STRATEGY_DEFAULT_VELOCITY_CALCmapping toceremony_policy.py-- each strategy maps to its natural default calculatorresolve_ceremony_policy()now derives the velocity calculator default from the resolved strategyvelocity_calculatoralongsidestrategy(belt and suspenders)#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:StrategyMigrationInfofrozen model withmodel_validatorenforcingprevious_strategy != new_strategydetect_strategy_migration()pure function for change detectionformat_migration_warning()andformat_reorder_prompt()pure text formattersnotify_strategy_migration()async best-effort notification viaAgentMessenger(broadcast ANNOUNCEMENT + channel TASK_UPDATE)CeremonyScheduler.activate_sprint()changes:_deactivate_sprint_unlocked()cleared_active_strategy)StrategyMigrationInfo | None(wasNone)SPRINT_CEREMONY_STRATEGY_CHANGEDevent on detectionTest plan
strategy_migration.py(model validation, detection, formatters, notification happy/error paths, MemoryError/RecursionError propagation from both broadcast and send_message)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