Skip to content

feat: config fixes and deferred improvements from PR #1058 review#1067

Merged
Aureliolo merged 20 commits intomainfrom
chore/config-fix-deferred-improvements
Apr 4, 2026
Merged

feat: config fixes and deferred improvements from PR #1058 review#1067
Aureliolo merged 20 commits intomainfrom
chore/config-fix-deferred-improvements

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

@Aureliolo Aureliolo commented Apr 4, 2026

Summary

Implements #1061 (PerformanceConfig in RootConfig) and all 5 items from #1060 (deferred improvements from PR #1058 review).

Changes

#1061: PerformanceConfig in RootConfig

  • Add performance: PerformanceConfig field to RootConfig with default factory
  • Add "performance": {} to default_config_dict() for YAML merge base layer
  • Wire effective_config.performance into _build_performance_tracker in create_app
  • Document all 12 fields in operations design spec (YAML example + reference table + validation rules)
  • Tests: schema defaults/custom, YAML round-trip via load_config_from_string

#1060.1: JWT localStorage to sessionStorage

  • Migrate all auth token storage from localStorage to sessionStorage (tab-scoped)
  • Updated: auth.ts, client.ts, providers.ts (SSE auth), and all test files
  • Security comment updated with sessionStorage threat model

#1060.2: Compound condition evaluator (AND/OR/NOT)

  • Tokenizer + recursive descent parser for compound expressions with parenthesized groups
  • Operator precedence: NOT > AND > OR
  • Quick-path for simple expressions (zero overhead for backward compat)
  • Keywords inside values not split (e.g. brand == ORLANDO works correctly)
  • Never-raise contract preserved
  • 31 new tests (compound operators, precedence, parens, edge cases)

#1060.3: O(1) execution lookup by task_id

  • Add find_by_task_id to WorkflowExecutionRepository protocol
  • SQLite implementation uses json_each() + json_extract() with status index
  • Replace O(R*N) scan in execution_service._find_execution_by_task with repo delegation
  • All 4 fake repos updated (execution_service, lifecycle, api, protocol compliance)

#1060.4: Background observer dispatcher

  • Decouple observer notification from single-writer mutation pipeline
  • New _observer_queue + _observer_dispatch_loop (mirrors _processing_loop pattern)
  • Mutation pipeline uses put_nowait() instead of inline await _notify_observers()
  • Graceful shutdown drains observer queue after processing queue
  • New test: slow observer does not block mutations

#1060.5: Explicit branch metadata in YAML depends_on

  • depends_on entries support string | { id, branch } union type
  • Exporter emits { id, branch: "true"|"false" } for conditional edges
  • Importer uses explicit branch when present, falls back to counter inference
  • Warns on unrecognized branch values
  • 10 new tests across importer and exporter

Additional fixes (from pre-PR review)

  • Fix WorkflowExecutionStatus runtime import in fakes_workflow.py (was under TYPE_CHECKING)
  • Add sessionStorage.clear() to 3 missed auth test reset functions
  • Complete operations.md field table (added windows, llm_sampling_model, collaboration_weights)
  • Add try/except error resilience to _observer_dispatch_loop
  • Add queue_size to observer QueueFull log warning
  • Remove duplicate JSDoc on inferDependsOnEdgeType
  • Document operator-key-name limitation in condition evaluator
  • Update CLAUDE.md engine description (observer dispatch, compound conditions)
  • Add unclosed-paren and double-NOT edge case tests
  • Trim task_engine.py docstrings to stay under 800-line limit

Test plan

  • Python: 13,714 unit tests passing, lint + mypy clean
  • Web: 2,406 tests passing, ESLint + TypeScript clean
  • Pre-reviewed by 6 agents, 17 findings addressed

Closes #1061
Closes #1060

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

coderabbitai bot commented Apr 4, 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: bec942ec-832d-4c14-b62d-1fc9620cd784

📥 Commits

Reviewing files that changed from the base of the PR and between 54e6263 and 55d576d.

📒 Files selected for processing (1)
  • src/synthorg/engine/task_engine.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). (7)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • 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 -- Python 3.14 has PEP 649.
PEP 758 except syntax: use except A, B: (no parentheses) for exception handling.
All public functions require type hints. Use mypy strict mode.
Docstrings must use Google style and are required on public classes and functions (enforced by ruff D rules).
Create new objects instead of mutating existing ones. For non-Pydantic internal collections, use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement.
Use frozen Pydantic models for config/identity. Use separate mutable-via-copy models for runtime state that evolves.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use allow_inf_nan=False in all ConfigDict declarations.
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models.
Use @computed_field for derived values instead of storing and validating redundant fields.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Functions must be less than 50 lines and files must be less than 800 lines.
Handle errors explicitly, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files).
Line length: 88 characters (ruff).
Python: 3.14+ (PEP 649 native lazy annotations).

Files:

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

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic must have: from synthorg.observability import get_logger then logger = get_logger(name)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print for handler construction.
Logger variable name must always be 'logger' (not '_logger', not 'log').
Use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED, TOOL_INVOKE_START). Import directly from synthorg.observability.events..
Always use logger.info(EVENT, key=value) for structured logging. Never use 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.
DEBUG logging is used for object creation, internal flow, and entry/exit of key functions.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001.

Files:

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

⚙️ CodeRabbit configuration file

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

Files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : 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`. Existing code is being migrated incrementally.

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/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:

  • src/synthorg/engine/task_engine.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/task_engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to **/*.py : PEP 758 except syntax: use `except A, B:` (no parentheses) for exception handling.

Applied to files:

  • src/synthorg/engine/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.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/task_engine.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled (e.g. simulating a slow provider or stubborn coroutine), use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)` -- it is cancellation-safe

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Applied to files:

  • src/synthorg/engine/task_engine.py
🔇 Additional comments (10)
src/synthorg/engine/task_engine.py (10)

1-74: LGTM!

The module docstring accurately reflects the new observer dispatch architecture, and imports correctly reference the new event constants from the domain-specific events module.


123-131: LGTM!

The observer queue correctly uses a dedicated effective_observer_queue_size configuration, and the type annotation properly includes None for the shutdown sentinel.


163-166: LGTM!

The observer dispatch loop is spawned with a descriptive task name, following the same pattern as the processing loop.


185-200: LGTM!

The shutdown sequence correctly:

  1. Uses asyncio.get_running_loop() (deprecation fix)
  2. Bounds the sentinel enqueue with wait_for + remaining budget
  3. Gracefully handles timeout by proceeding to observer drain

The ordering ensures no events are stranded: _drain_processing() completes the processing loop before the sentinel is enqueued.


204-233: LGTM!

The drain logic correctly uses asyncio.shield() to allow the processing loop to continue draining even when wait_for times out, with proper cleanup and future-failure handling.


235-253: LGTM!

The observer drain mirrors the processing drain pattern with appropriate timeout handling and cleanup. The lack of a "complete" log is acceptable given observers are best-effort.


730-740: LGTM!

The non-blocking enqueue with explicit QueueFull logging maintains the best-effort observer contract while keeping the mutation hot path unblocked.


765-796: LGTM!

The observer dispatch loop correctly:

  • Uses the same polling pattern as the processing loop
  • Handles the None sentinel for graceful shutdown
  • Calls task_done() in both success and exception paths
  • Allows CancelledError to propagate for timeout-based shutdown

798-821: LGTM!

The observer notification logic maintains its best-effort contract with per-observer exception handling, now fully decoupled from the mutation hot path via the dispatch loop.


643-654: LGTM!

Constants are well-documented and shared across both background loops for consistent polling behavior.


Walkthrough

Adds a top-level performance: PerformanceConfig to RootConfig, includes a default empty performance entry, and passes effective_config.performance into _build_performance_tracker. Documents a performance YAML namespace and validation rules. TaskEngine now enqueues observer notifications onto a bounded _observer_queue processed by a background _observer_dispatch_loop, with queue-full/drop logging and timed observer drain on shutdown. The condition evaluator now supports case-insensitive compound AND/OR/NOT with parentheses and token limits. Adds find_by_task_id to the workflow execution repository (protocol and SQLite impl) using a JSON query. Frontend JWT storage migrated from localStorage to sessionStorage. YAML export/import for depends_on now supports explicit { id, branch } entries. Multiple tests and observability event constants were added or updated.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main changes: it references implementing #1061 (PerformanceConfig) and deferred improvements from #1060 review, which comprise the primary content of this changeset.
Description check ✅ Passed The description comprehensively covers all major changes (PerformanceConfig, localStorage→sessionStorage, compound conditions, execution lookup, observer dispatch, branch metadata) with clear sub-sections and implementation details related to the changeset.
Linked Issues check ✅ Passed All requirements from #1061 and #1060 are met: performance config wiring [#1061], JWT migration [#1060.1], compound evaluator [#1060.2], O(1) lookup [#1060.3], background dispatcher [#1060.4], and branch metadata [#1060.5].
Out of Scope Changes check ✅ Passed All changes align with stated objectives from #1061 and #1060. Additional fixes (test storage resets, CLAUDE.md docs, edge case tests) are reasonable supporting improvements that address the pre-PR review feedback mentioned in the PR description.
Docstring Coverage ✅ Passed Docstring coverage is 52.22% 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 4, 2026

Dependency Review

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

Snapshot Warnings

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

Scanned Files

None

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request introduces performance tracking configuration, implements an asynchronous observer dispatch mechanism in the TaskEngine to prevent slow observers from blocking mutations, and adds support for compound boolean operators (AND, OR, NOT) and parenthesized groups in workflow condition evaluations. Additionally, it optimizes workflow execution lookups using SQLite's JSON features and improves security by migrating JWT storage from localStorage to sessionStorage. A critical syntax error was identified in the TaskEngine where Python 2 exception handling syntax was used, which will cause a failure in Python 3 environments.

Comment on lines +761 to +762
except MemoryError, RecursionError:
raise
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

This except syntax is for Python 2. For Python 3, you should group multiple exceptions in a tuple. This will cause a SyntaxError if these exceptions are raised.

            except (MemoryError, RecursionError):
                raise

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 4, 2026

Codecov Report

❌ Patch coverage is 80.41237% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.65%. Comparing base (85b05bc) to head (55d576d).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/engine/task_engine.py 79.68% 11 Missing and 2 partials ⚠️
...horg/persistence/sqlite/workflow_execution_repo.py 7.14% 13 Missing ⚠️
src/synthorg/engine/workflow/condition_eval.py 90.00% 5 Missing and 5 partials ⚠️
src/synthorg/engine/task_engine_config.py 66.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1067      +/-   ##
==========================================
- Coverage   89.69%   89.65%   -0.05%     
==========================================
  Files         730      731       +1     
  Lines       42539    42698     +159     
  Branches     4267     4287      +20     
==========================================
+ Hits        38157    38282     +125     
- Misses       3636     3663      +27     
- Partials      746      753       +7     

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

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds configuration plumbing and several deferred engine/editor improvements, including YAML-driven performance tracking config, safer JWT storage, richer condition evaluation, faster workflow execution lookup, background observer dispatch, and round-trip-stable YAML branch metadata.

Changes:

  • Wire PerformanceConfig into RootConfig / defaults and pass it into _build_performance_tracker, with docs + config loader/schema tests.
  • Migrate web auth token persistence from localStorage to sessionStorage, updating API client/interceptors and related tests.
  • Improve workflow system ergonomics/perf: compound condition evaluator (AND/OR/NOT + parens), repository find_by_task_id, background TaskEngine observer dispatch, and explicit {id, branch} YAML depends_on metadata with importer/exporter tests.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
web/src/stores/auth.ts Switch token persistence to sessionStorage and update expiry/must-change-password handling.
web/src/pages/workflow-editor/yaml-to-nodes.ts Parse depends_on entries as `string
web/src/pages/workflow-editor/workflow-to-yaml.ts Export depends_on with explicit {id, branch} objects for conditional edges to preserve round-trip fidelity.
web/src/api/endpoints/providers.ts Use sessionStorage token for SSE model-pull auth and clear session keys on 401.
web/src/api/client.ts Update interceptor JWT storage note + switch auth token reads/clears to sessionStorage.
web/src/tests/stores/auth.test.ts Update auth-store tests for sessionStorage persistence/cleanup.
web/src/tests/router/guards.test.tsx Ensure session storage is cleared in guard-related test resets.
web/src/tests/pages/workflow-editor/yaml-to-nodes.test.ts Add tests for YAML importer depends_on branch metadata parsing and fallback inference.
web/src/tests/pages/workflow-editor/workflow-to-yaml.test.ts Add tests for YAML exporter branch metadata emission.
web/src/tests/hooks/useWebSocket.test.ts Clear session storage in test reset to avoid cross-test token leakage.
web/src/tests/hooks/useAuth.test.ts Clear session storage in test reset to avoid cross-test token leakage.
web/src/tests/api/client.test.ts Update interceptor tests to validate sessionStorage token injection and 401 cleanup.
web/src/tests/api/client-bypass.test.ts Update dev-bypass interceptor tests for sessionStorage.
tests/unit/persistence/test_protocol.py Extend protocol compliance fake with find_by_task_id.
tests/unit/engine/workflow/test_execution_service.py Update fake repo with find_by_task_id to match new protocol.
tests/unit/engine/workflow/test_execution_lifecycle.py Update fake repo with find_by_task_id to match new protocol.
tests/unit/engine/workflow/test_condition_eval.py Add extensive tests for compound condition evaluation semantics and edge cases.
tests/unit/engine/test_task_engine_observers.py Add observer-queue flushing helper and test that slow observers don’t block mutations.
tests/unit/config/test_schema.py Add RootConfig.performance default/custom tests.
tests/unit/config/test_loader.py Add YAML round-trip test for performance namespace.
tests/unit/config/conftest.py Include PerformanceConfig in RootConfigFactory.
tests/unit/api/fakes_workflow.py Fix runtime import and add fake repo find_by_task_id.
src/synthorg/persistence/workflow_execution_repo.py Add protocol method find_by_task_id.
src/synthorg/persistence/sqlite/workflow_execution_repo.py Implement find_by_task_id via SQLite JSON search + structured events.
src/synthorg/observability/events/task_engine.py Add new TaskEngine observer queue/drain observability event keys.
src/synthorg/observability/events/persistence.py Add new persistence observability event keys for task lookup.
src/synthorg/engine/workflow/execution_service.py Delegate task->execution lookup to repository method.
src/synthorg/engine/workflow/condition_eval.py Implement tokenizer + recursive-descent parser for compound conditions with quick-path for legacy expressions.
src/synthorg/engine/task_engine.py Add background observer dispatcher queue/loop and adjust shutdown/drain behavior.
src/synthorg/config/schema.py Add performance: PerformanceConfig to RootConfig.
src/synthorg/config/defaults.py Add "performance": {} to default config dict to support YAML merge base.
src/synthorg/api/app.py Pass effective_config.performance into _build_performance_tracker.
docs/design/operations.md Document performance YAML namespace fields, defaults, and validation rules.
CLAUDE.md Update repo architecture notes to reflect background observer dispatch and compound condition evaluator.

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

tokens = _tokenize(expr)
if not tokens:
return False
result, _ = _parse_or(tokens, 0, context)
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

In the compound-expression path, the parser result is returned even if it didn't consume all tokens. This means malformed expressions with trailing junk (e.g. "true OR false garbage" or "(a == 1) extra") can incorrectly evaluate to True instead of falling back to the never-raise/malformed->False behavior. Consider treating any unconsumed tokens (or unexpected closing parens) as a parse failure and returning False when the final parse position is not at the end of the token list.

Suggested change
result, _ = _parse_or(tokens, 0, context)
result, pos = _parse_or(tokens, 0, context)
if pos != len(tokens):
return False

Copilot uses AI. Check for mistakes.
Comment on lines +751 to +758
while self._running or not self._observer_queue.empty():
try:
event = await asyncio.wait_for(
self._observer_queue.get(),
timeout=self._POLL_INTERVAL_SECONDS,
)
except TimeoutError:
continue
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

Shutdown race: stop() sets _running=False before the processing loop drains, and _observer_dispatch_loop exits when (_running is False and _observer_queue is empty). If the observer queue is momentarily empty at shutdown start, the dispatcher task can terminate early and miss events enqueued later by the still-draining _processing_loop. The dispatcher loop should also stay alive while the processing task is still running (or use a separate "shutdown complete" signal) so observer delivery is truly drained after mutation draining.

Copilot uses AI. Check for mistakes.
Comment on lines +119 to +126
const depEntries: DependsOnEntry[] = []
for (const srcId of incoming.get(nodeId) ?? []) {
const srcNode = nodeMap.get(srcId)
return srcNode && !skipTypes.has(srcNode.type ?? '')
})
if (deps.length > 0) step.depends_on = deps
if (!srcNode || skipTypes.has(srcNode.type ?? '')) continue
// Find the edge from srcId to this node
const edge = edges.find((e) => e.source === srcId && e.target === nodeId)
const edgeType = (edge?.data as Record<string, unknown> | undefined)?.edgeType as string | undefined
if (edgeType === 'conditional_true') {
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

Exporter performance: inside the per-node dependency loop, this does edges.find(...) for each incoming edge, which makes YAML generation O(E^2) in dense graphs. Since an outgoing map is already built above, prefer looking up the src->target edge from outgoing.get(srcId) (or precomputing a source/target->edge map once) to keep preview generation linear.

Copilot uses AI. Check for mistakes.
Comment on lines +438 to +446
cursor = await self._db.execute(
f"SELECT {_SELECT_COLUMNS} FROM workflow_executions" # noqa: S608
" WHERE status = ?"
" AND EXISTS ("
" SELECT 1 FROM json_each(node_executions)"
" WHERE json_extract(value, '$.task_id') = ?"
" )"
" LIMIT 1",
(WorkflowExecutionStatus.RUNNING.value, task_id),
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

This query filters by RUNNING (good, uses idx_wfe_status) but still needs to scan/parse node_executions JSON via json_each() for every RUNNING execution, so the lookup is not truly O(1) and may remain a hot-path cost at scale. If task_id lookups are frequent, consider a normalized mapping (task_id -> execution_id) maintained on save, or another indexable representation, to avoid JSON scans.

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

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

139-145: ⚠️ Potential issue | 🔴 Critical

These observers are no longer safe to treat as best-effort.

put_nowait() turns observer backpressure into dropped TaskStateChanged events, but WorkflowExecutionObserver consumes those events to advance running workflows. If the queue fills here, the task mutation still succeeds while WorkflowExecutionService.handle_task_state_changed() never sees the terminal transition, so the workflow can remain RUNNING forever. This path needs lossless delivery for stateful observers, or a separate best-effort channel for telemetry-only observers.

Also applies to: 711-720

🤖 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/condition_eval.py`:
- Around line 22-25: The tokenizer and operator-detection routines are treating
tokens like AND/OR/NOT or parentheses as operators even when they appear inside
a comparison atom (e.g., "AND == yes", "status == OR", "title == Research AND
Development"); update _tokenize() to first recognize full comparison atoms (lhs,
comparator, rhs) as a single token before splitting for compound operators and
update _has_compound_operators() to check for operator tokens only after
tokenization of comparisons, ensuring that strings on the right-hand side
(including words that match operator names or contain spaces/parentheses) are
not re-classified as logical operators; refer to _tokenize(),
_has_compound_operators(), and the comparison parsing code paths where tokens
are currently split so the comparison-form escape hatch is honored.
- Around line 180-195: The parser currently accepts partial/ malformed
expressions as true; update _parse_atom_token (the block handling "(" and bare
operator tokens) to treat any unexpected/missing operands or unmatched
parentheses as a parse error that returns False and a position that signals
failure (e.g. do not claim to have successfully consumed a valid atom), and then
make evaluate_condition verify the parser consumed the entire token stream and
the top-level parse returned success (return False if pos != len(tokens) or
parse returned an error). Concretely: in the "(" branch of _parse_atom_token
ensure a parse failure inside the parentheses propagates as False and the caller
sees failure (don’t return a truthy value when closing parenthesis is missing),
treat bare operator tokens ("AND","OR","NOT",")") as parse errors (return False
and indicate failure), and add a final check in evaluate_condition that the
returned pos equals len(tokens) and the returned value is a successful parse
before returning True.
- Around line 31-32: Add a module logger and use it to record parsing failures
instead of silently swallowing them: import get_logger with "from
synthorg.observability import get_logger" and add "logger =
get_logger(__name__)" at module scope, then in the try/except that
parses/evaluates the condition_expression (the block that currently catches
parser exceptions and returns False) call logger.exception or logger.error with
the exception and include the offending condition_expression in the message
before returning False so parse errors are logged with context.

In `@src/synthorg/persistence/sqlite/workflow_execution_repo.py`:
- Around line 466-472: The deserialization call passes task_id into
_deserialize_row causing any PERSISTENCE_WORKFLOW_EXEC_DESERIALIZE_FAILED logs
to show the task id instead of the workflow execution id; fix by extracting the
execution id from the retrieved row (e.g., id = row["id"] or
row["execution_id"]) and pass that execution id into _deserialize_row instead of
task_id, then use execution.id (or the extracted id) when emitting
PERSISTENCE_WORKFLOW_EXEC_FOUND_BY_TASK so the failure and found logs reference
the correct workflow execution identifier.

In `@tests/unit/engine/test_task_engine_observers.py`:
- Around line 18-31: The helper _flush_observers currently returns silently if
the observer queue isn't drained; change it to enforce a deterministic failure:
add a timeout parameter (e.g., timeout=0.5) and after the polling loop raise an
AssertionError or asyncio.TimeoutError if engine._observer_queue is still not
empty so tests fail instead of hiding races; keep using
engine._observer_queue.empty() and the same small sleeps but wrap the logic so
unmet timeout triggers the exception, and update callers/tests to mock
time.monotonic()/asyncio.sleep() when needed per guidelines.

In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts`:
- Around line 32-93: Add a new test in workflow-to-yaml.test.ts that uses
makeNode/makeEdge to create a small graph with a parallel_branch edge (e.g.,
start -> a, start -> b as parallel branches, then both -> end) and call
generateYamlPreview(nodes, edges, 'test', 'agile'); assert the generated YAML
includes plain string depends_on entries for the parallel targets (e.g.,
expect(yaml).toContain('- a') / '- b') and does NOT include branch metadata
(expect(yaml).not.toContain('branch:') / not.toMatch(/id:\s*.../)), so the
exporter’s fallback for parallel_branch is explicitly validated.
- Around line 71-73: The regexes in the test (the expectations using
/branch:\s*.true./ and /branch:\s*.false./) are too permissive because the dots
match any char; update them to precisely match quoted boolean strings or word
boundaries — for example replace with patterns that assert quotes and the full
word like /branch:\s*['"]true['"]/ and /branch:\s*['"]false['"]/ or use word
boundaries /\btrue\b/ and /\bfalse\b/ so malformed outputs like "Xtruez" won't
pass; modify the expect calls in workflow-to-yaml.test (the three
expect(yaml).toMatch(...) assertions) accordingly.

In `@web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts`:
- Around line 136-171: Add a new test case to verify unrecognized branch values
produce a warning and fall back to inference: create a YAML where a conditional
step "check" is depended on by "step_b" with depends_on entry { id: "check",
branch: "maybe" }, call parseYamlToNodesEdges(yaml), assert result.errors has
length 0, result.warnings length > 0 and result.warnings[0] contains
'unrecognized branch', then locate the edge with findEdge(result.edges, 'check',
'step_b') and assert its data.edgeType equals 'conditional_true' to confirm
fallback inference.

In `@web/src/pages/workflow-editor/yaml-to-nodes.ts`:
- Around line 261-271: Add a non-fatal warning when explicit branch metadata is
present on a non-conditional source step: inside the branch where explicitBranch
!== undefined (the block that sets edgeType to 'conditional_true' or
'conditional_false'), check if sourceStep.type !== 'conditional' and emit a
warning (e.g., via console.warn or the project logger) that explicit branch
metadata was provided for depId/sourceStep and will be treated as a conditional
edge; keep existing behavior (setting edgeType) and do not change the
conditionalBranchCounters or fallback logic in inferDependsOnEdgeType.
🪄 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: 98f61de8-926e-433c-831c-607af6826929

📥 Commits

Reviewing files that changed from the base of the PR and between 4689816 and 3edf097.

📒 Files selected for processing (34)
  • CLAUDE.md
  • docs/design/operations.md
  • src/synthorg/api/app.py
  • src/synthorg/config/defaults.py
  • src/synthorg/config/schema.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
  • src/synthorg/engine/workflow/execution_service.py
  • src/synthorg/observability/events/persistence.py
  • src/synthorg/observability/events/task_engine.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
  • src/synthorg/persistence/workflow_execution_repo.py
  • tests/unit/api/fakes_workflow.py
  • tests/unit/config/conftest.py
  • tests/unit/config/test_loader.py
  • tests/unit/config/test_schema.py
  • tests/unit/engine/test_task_engine_observers.py
  • tests/unit/engine/workflow/test_condition_eval.py
  • tests/unit/engine/workflow/test_execution_lifecycle.py
  • tests/unit/engine/workflow/test_execution_service.py
  • tests/unit/persistence/test_protocol.py
  • web/src/__tests__/api/client-bypass.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/stores/auth.test.ts
  • web/src/api/client.ts
  • web/src/api/endpoints/providers.ts
  • web/src/pages/workflow-editor/workflow-to-yaml.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/stores/auth.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Agent
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Web
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (7)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

web/src/**/*.{ts,tsx}: Web dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and @/lib/motion presets.
Web dashboard: design token rules enforced by PostToolUse hook (scripts/check_web_design_system.py)
TypeScript/JavaScript: Node.js 22+, TypeScript 6.0+, ESLint with zero warnings enforced on web/src/**/*.{ts,tsx}
Web dashboard YAML parsing: use js-yaml library (in package.json)

Files:

  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/api/client-bypass.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/api/endpoints/providers.ts
  • web/src/api/client.ts
  • web/src/stores/auth.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/pages/workflow-editor/workflow-to-yaml.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

React testing: use fast-check for property-based testing (fc.assert + fc.property)

Files:

  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/api/client-bypass.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard styling: Tailwind CSS 4 for utility classes; never hardcode pixel values or colors

Files:

  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/api/client-bypass.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/api/endpoints/providers.ts
  • web/src/api/client.ts
  • web/src/stores/auth.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/pages/workflow-editor/workflow-to-yaml.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Python 3.14+ required; no from __future__ import annotations (PEP 649 native lazy annotations)
Use PEP 758 except syntax: except A, B: (no parentheses) -- ruff enforces this on Python 3.14
Type hints required on all public functions; mypy strict mode enforced
Docstrings in Google style, required on all public classes and functions (enforced by ruff D rules)
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.
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; separate mutable-via-copy models (using model_copy(update=...)) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Pydantic v2: use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time
Use @computed_field for derived values in Pydantic models instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens)
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models -- 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.
Line length: 88 characters (ruff enforced)
Functions must be < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).

Files:

  • src/synthorg/config/defaults.py
  • src/synthorg/api/app.py
  • tests/unit/persistence/test_protocol.py
  • src/synthorg/engine/workflow/execution_service.py
  • tests/unit/config/conftest.py
  • tests/unit/config/test_schema.py
  • src/synthorg/observability/events/task_engine.py
  • src/synthorg/config/schema.py
  • tests/unit/engine/workflow/test_execution_lifecycle.py
  • tests/unit/api/fakes_workflow.py
  • tests/unit/engine/workflow/test_execution_service.py
  • src/synthorg/observability/events/persistence.py
  • tests/unit/engine/workflow/test_condition_eval.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
  • tests/unit/engine/test_task_engine_observers.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
  • src/synthorg/persistence/workflow_execution_repo.py
  • tests/unit/config/test_loader.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr) for handler construction
Logger variable name must always be logger (not _logger, not log)
Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
Structured kwargs in 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 level
DEBUG logging 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. Vendor names allowed only in: Operations design page, .claude/ files, third-party import paths, and src/synthorg/providers/presets.py

Files:

  • src/synthorg/config/defaults.py
  • src/synthorg/api/app.py
  • src/synthorg/engine/workflow/execution_service.py
  • src/synthorg/observability/events/task_engine.py
  • src/synthorg/config/schema.py
  • src/synthorg/observability/events/persistence.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
  • src/synthorg/persistence/workflow_execution_repo.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/config/defaults.py
  • src/synthorg/api/app.py
  • src/synthorg/engine/workflow/execution_service.py
  • src/synthorg/observability/events/task_engine.py
  • src/synthorg/config/schema.py
  • src/synthorg/observability/events/persistence.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
  • src/synthorg/persistence/workflow_execution_repo.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Vendor names in test files must use test-provider, test-small-001, etc.
Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Coverage minimum 80% enforced in CI
Async testing: asyncio_mode = "auto" in conftest -- no manual @pytest.mark.asyncio needed
Default timeout: 30 seconds per test (global in pyproject.toml). Non-default overrides like timeout(60) are allowed but avoid adding per-file markers.
Prefer @pytest.mark.parametrize for testing similar cases in Python tests
Property-based testing in Python: use Hypothesis (@given + @settings). Profiles in tests/conftest.py: ci (deterministic, 10 examples), dev (1000 examples), fuzz (10,000 examples, no deadline), extreme (500,000 examples). Controlled via HYPOTHESIS_PROFILE env var.
When Hypothesis finds a failure, fix the underlying bug and add an explicit @example(...) decorator to permanently cover the case in CI
Never skip, dismiss, or ignore flaky tests -- always fix them fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For indefinite blocking, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/persistence/test_protocol.py
  • tests/unit/config/conftest.py
  • tests/unit/config/test_schema.py
  • tests/unit/engine/workflow/test_execution_lifecycle.py
  • tests/unit/api/fakes_workflow.py
  • tests/unit/engine/workflow/test_execution_service.py
  • tests/unit/engine/workflow/test_condition_eval.py
  • tests/unit/engine/test_task_engine_observers.py
  • tests/unit/config/test_loader.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/persistence/test_protocol.py
  • tests/unit/config/conftest.py
  • tests/unit/config/test_schema.py
  • tests/unit/engine/workflow/test_execution_lifecycle.py
  • tests/unit/api/fakes_workflow.py
  • tests/unit/engine/workflow/test_execution_service.py
  • tests/unit/engine/workflow/test_condition_eval.py
  • tests/unit/engine/test_task_engine_observers.py
  • tests/unit/config/test_loader.py
🧠 Learnings (47)
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Applied to files:

  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/stores/auth.ts
  • web/src/__tests__/stores/auth.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)

Applied to files:

  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/stores/auth.ts
  • web/src/__tests__/stores/auth.test.ts
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.test.{ts,tsx} : Web dashboard: Use React Hypothesis (fast-check) for property-based testing with fc.assert + fc.property

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • tests/unit/config/conftest.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.

Applied to files:

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

Applied to files:

  • tests/unit/config/conftest.py
  • src/synthorg/config/schema.py
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use storybook/test (not storybook/test) and storybook/actions (not storybook/addon-actions) import paths

Applied to files:

  • web/src/__tests__/hooks/useAuth.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)

Applied to files:

  • web/src/__tests__/hooks/useAuth.test.ts
📚 Learning: 2026-04-04T10:51:58.893Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:58.893Z
Learning: Applies to web/**/*.stories.tsx : Use `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`) for Storybook imports

Applied to files:

  • web/src/__tests__/hooks/useAuth.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`

Applied to files:

  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 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/task_engine.py
  • src/synthorg/observability/events/persistence.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/task_engine.py
  • src/synthorg/observability/events/persistence.py
  • docs/design/operations.md
📚 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/task_engine.py
  • src/synthorg/observability/events/persistence.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/task_engine.py
  • src/synthorg/observability/events/persistence.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/task_engine.py
  • src/synthorg/observability/events/persistence.py
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`

Applied to files:

  • src/synthorg/observability/events/task_engine.py
  • src/synthorg/observability/events/persistence.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/task_engine.py
  • src/synthorg/observability/events/persistence.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/task_engine.py
  • src/synthorg/observability/events/persistence.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/task_engine.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/task_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).

Applied to files:

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

Applied to files:

  • src/synthorg/config/schema.py
  • docs/design/operations.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/__tests__/**/*.test.{ts,tsx} : Use property-based testing with fast-check in React tests (`fc.assert` + `fc.property`)

Applied to files:

  • web/src/__tests__/api/client.test.ts
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : 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`. Existing code is being migrated incrementally.

Applied to files:

  • src/synthorg/engine/task_engine.py
  • tests/unit/engine/test_task_engine_observers.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 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/test_task_engine_observers.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled (e.g. simulating a slow provider or stubborn coroutine), use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)` -- it is cancellation-safe

Applied to files:

  • tests/unit/engine/test_task_engine_observers.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 tests/**/*.py : Never skip, dismiss, or ignore flaky tests; fix them fully and fundamentally by mocking `time.monotonic()` and `asyncio.sleep()` for determinism or using `asyncio.Event().wait()` for indefinite blocking

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-03-16T10:40:25.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T10:40:25.144Z
Learning: Applies to tests/**/*.py : Async testing: asyncio_mode = 'auto' — no manual pytest.mark.asyncio needed.

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard YAML parsing: use `js-yaml` library (in package.json)

Applied to files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/pages/workflow-editor/workflow-to-yaml.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/pages/workflow-editor/workflow-to-yaml.ts
🔇 Additional comments (46)
web/src/__tests__/router/guards.test.tsx (1)

53-54: LGTM!

Clearing sessionStorage before localStorage correctly aligns the test cleanup with the auth storage migration. This ensures auth-related state is properly reset between tests.

web/src/__tests__/hooks/useAuth.test.ts (1)

14-18: LGTM!

The resetStore() helper correctly clears sessionStorage to match the auth storage migration, ensuring clean test isolation.

web/src/__tests__/hooks/useWebSocket.test.ts (1)

14-20: LGTM!

Consistent with other test files—resetStores() now clears both storage types to properly isolate tests after the auth storage migration.

web/src/api/endpoints/providers.ts (1)

172-191: LGTM!

The pullModel function correctly uses sessionStorage for auth token retrieval and cleanup on 401, consistent with the migration in client.ts.

web/src/__tests__/api/client-bypass.test.ts (1)

4-34: LGTM!

Test correctly updated to use sessionStorage for setup, cleanup, and assertions. The test logic properly validates that the dev auth bypass prevents storage clearing on 401.

web/src/api/client.ts (2)

22-41: LGTM!

The security commentary is comprehensive and accurately describes the sessionStorage trade-offs. The tab-scoped storage limitation is clearly documented, and the migration to sessionStorage provides meaningful security benefits (tokens don't persist across sessions or leak to other tabs).


48-53: LGTM!

The 401 handler correctly clears all three auth-related sessionStorage keys synchronously before the Zustand cleanup, preventing stale-token retries.

web/src/__tests__/stores/auth.test.ts (5)

40-52: LGTM!

The resetStore() helper correctly clears sessionStorage and the store state, ensuring proper test isolation after the auth storage migration.


61-74: LGTM!

Tests correctly updated to assert sessionStorage persistence for token and expiry.


76-112: LGTM!

clearAuth tests correctly verify that all three auth keys are removed from sessionStorage.


171-198: LGTM!

fetchUser tests properly verify sessionStorage interactions for the must_change_password flag and 401 cleanup.


210-236: LGTM!

changePassword and logout tests correctly assert sessionStorage key management.

web/src/stores/auth.ts (7)

1-6: LGTM!

Docstring correctly updated to reflect the tab-scoped storage behavior.


56-71: LGTM!

getInitialToken() correctly reads from sessionStorage and cleans up expired/invalid tokens. The early return on valid token and cleanup of all three keys on invalid state is well-structured.


78-86: LGTM!

Token expiry scheduling correctly reads the expiry timestamp from sessionStorage for restored tokens.


121-142: LGTM!

_mustChangePasswordFallback initialization and setToken() correctly use sessionStorage for persistence.


144-160: LGTM!

clearAuth() correctly removes all three auth-related keys from sessionStorage and maintains the redirect logic.


170-194: LGTM!

fetchUser() correctly persists/clears the must_change_password flag in sessionStorage based on the user's state.


196-214: LGTM!

changePassword() correctly clears the must_change_password flag from sessionStorage when the password change succeeds.

web/src/__tests__/api/client.test.ts (3)

4-6: LGTM!

Comment accurately reflects the sessionStorage migration, and the mock hoisting requirement is correctly documented.


190-208: LGTM!

Request interceptor tests correctly verify the sessionStorage-based token injection. The afterEach cleanup is appropriate, and test names clearly describe the behavior under test.


210-251: LGTM!

The 401 interceptor tests comprehensively verify the sessionStorage clearing behavior:

  • All three auth-related keys are correctly cleared on 401 responses
  • Non-401 errors properly preserve the auth token
  • Using sessionStorage.clear() in afterEach is appropriate given the multi-key setup
web/src/pages/workflow-editor/workflow-to-yaml.ts (2)

11-12: LGTM: Well-defined discriminated type for dependency entries.

The DependsOnEntry type cleanly models the mixed-format YAML output (plain string vs object with branch metadata), enabling type-safe consumption by callers.


117-134: LGTM: Dependency serialization with explicit branch metadata.

The logic correctly:

  • Emits { id, branch: 'true' } for conditional_true edges
  • Emits { id, branch: 'false' } for conditional_false edges
  • Falls back to plain string for sequential/parallel_branch edges

This ensures round-trip fidelity with the parser's explicit-branch handling.

One minor observation: the edges.find() on line 124 is O(E) per incoming edge, but this is acceptable for typical workflow sizes and keeps the code readable.

web/src/pages/workflow-editor/yaml-to-nodes.ts (2)

38-38: LGTM: Extended type supports both legacy and new dependency formats.

The expanded type (string | number | { id: string; branch?: string })[] correctly handles:

  • Backward-compatible string/number entries
  • New object entries with optional branch metadata

228-247: LGTM: Robust parsing of mixed dependency entry formats.

The parsing logic correctly handles all valid entry shapes:

  • Objects with id and optional branch
  • Plain strings and numbers (coerced to strings)
  • Invalid shapes trigger errors

The warning for unrecognized branch values (line 240) provides helpful feedback while preserving backward compatibility via fallback inference.

web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts (1)

8-30: LGTM: Clean helper factories for test fixtures.

The makeNode and makeEdge helpers provide a concise way to construct test fixtures with sensible defaults while allowing customization of edge types and branch metadata.

web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts (2)

7-13: LGTM: Clean helper for edge lookup.

The findEdge helper provides a simple way to locate edges by source/target, making assertions readable.


15-134: Excellent coverage of the new depends_on parsing behavior.

The tests comprehensively validate:

  • Backward compatibility with plain string dependencies
  • Explicit branch: 'true' and branch: 'false' handling
  • Mixed string/object dependency arrays
  • Counter-based inference fallback

The inference test (lines 110-134) correctly verifies that two sequential string dependencies from a conditional source alternate between conditional_true and conditional_false.

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

28-28: LGTM!

The PerformanceConfig integration into RootConfig is correctly implemented:

  • Import is properly placed
  • Field uses default_factory for the nested Pydantic model
  • Docstring accurately describes the field's purpose (quality judge, CI/LLM weights, trend thresholds)
  • PerformanceConfig already enforces frozen=True and allow_inf_nan=False per coding guidelines

Also applies to: 567-568, 681-684

src/synthorg/persistence/workflow_execution_repo.py (1)

87-103: LGTM!

The new find_by_task_id protocol method is well-defined:

  • Clear contract: searches only RUNNING executions for a node containing the task ID
  • Proper type hints with NotBlankStr for the identifier
  • Google-style docstring with Args, Returns, and Raises sections
  • Return type correctly handles the not-found case with None
tests/unit/config/test_loader.py (1)

360-375: LGTM!

The round-trip test is well-constructed:

  • Covers the key performance config fields (quality judge, weights, data points)
  • Uses test-prefixed vendor names (test-judge-001, test-provider) per coding guidelines
  • Weight values (0.3 + 0.7 = 1.0) satisfy the PerformanceConfig sum validator
  • Follows the existing test patterns in TestLoadConfigFromString
src/synthorg/config/defaults.py (1)

41-41: LGTM!

The "performance": {} entry is correctly placed in alphabetical/logical order (matching RootConfig field sequence), and the empty dict allows PerformanceConfig to apply its defaults during Pydantic validation.

tests/unit/persistence/test_protocol.py (1)

416-420: LGTM!

The fake implementation correctly satisfies the WorkflowExecutionRepository protocol by adding the find_by_task_id method with matching signature. The existing test_fake_workflow_exec_repo_is_workflow_execution_repository test (lines 611-616) verifies protocol compliance via isinstance check.

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

785-790: LGTM!

This is the critical wiring change that fulfills the PR objective: effective_config.performance is now passed to _build_performance_tracker, ensuring YAML-driven quality scoring settings (quality judge model/provider, CI/LLM weights) are respected at startup instead of using hardcoded defaults.

CLAUDE.md (1)

99-99: LGTM!

Documentation accurately reflects the PR's implementation changes:

  • "background observer dispatch" documents the new _observer_queue and _observer_dispatch_loop in TaskEngine
  • "(compound AND/OR/NOT)" documents the new tokenizer + recursive-descent parser for condition expressions with precedence handling
src/synthorg/engine/workflow/execution_service.py (1)

697-702: LGTM!

Excellent refactoring that replaces the O(R×N) in-memory scan with a single repository call. The previous implementation fetched all RUNNING executions and iterated through node_executions in Python; now the SQLite implementation can use json_each() + status index for efficient lookup. The method contract is preserved while delegating the complexity to the persistence layer.

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

511-537: Performance config schema tests are well-targeted.

These assertions cover both defaults and explicit overrides for RootConfig.performance as expected.

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

28-28: Factory wiring for performance is correct.

Good update to keep RootConfigFactory aligned with the new schema field.

Also applies to: 105-105

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

111-121: find_by_task_id fake implementation looks good.

The RUNNING-status filter and deep-copy return are consistent with expected repo behavior.

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

109-119: Repository fake update is aligned with the new lookup path.

Good addition for exercising _find_execution_by_task via repository delegation.

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

24-25: New observer event constants are a good addition.

This cleanly supports structured logging for queue-full and drain-timeout observer scenarios.

tests/unit/api/fakes_workflow.py (1)

6-6: Nice fix to the fake workflow execution repository.

The runtime enum import and find_by_task_id implementation are both correct for the updated service path.

Also applies to: 95-105

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

294-299: Persistence event coverage for task-based lookup is solid.

These constants complete structured logging support for find-by-task success/failure paths.

tests/unit/engine/test_task_engine_observers.py (2)

119-119: Good synchronization additions around observer assertions.

Waiting for dispatcher progress before asserting observer side effects is the right test pattern.

Also applies to: 181-181, 209-209, 248-248


256-295: Strong coverage for the non-blocking observer requirement.

This test captures the intended background-dispatch behavior well.

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

♻️ Duplicate comments (3)
src/synthorg/engine/workflow/condition_eval.py (2)

115-142: ⚠️ Potential issue | 🟠 Major

Comparison values containing operator words still break parsing.

_has_compound_operators() and _tokenize() split on AND|OR|NOT before recognizing a full comparison atom, so status == OR and title == Research AND Development still route through the compound parser and evaluate incorrectly. The new note documents the limitation, but the workflow can still take the wrong branch at runtime.

Also applies to: 221-223

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 115 - 142, The
tokenizer currently splits on AND/OR/NOT even when those words are inside
comparison values; update _tokenize and the compound-detection logic so
comparison expressions are recognized as single atoms before keyword splitting:
modify _tokenize to first scan for comparison operators (e.g., ==, !=, <=, >=,
<, >) and treat the entire "lhs operator rhs" span as one token (so values like
"OR" or "Research AND Development" remain inside the comparison atom), and
adjust _has_compound_operators to operate on these comparison-aware tokens (or
run the comparison-atom pass first) so compound parsing only triggers for true
logical operators outside comparison atoms; reference _tokenize and
_has_compound_operators to locate the changes.

189-210: ⚠️ Potential issue | 🟠 Major

Malformed compound expressions still succeed on a valid prefix.

_parse_atom_token() returns (False, pos) for missing operands and unclosed groups instead of signaling a parse error. That means inputs like true OR or (a == 1 are accepted when the prefix is truthy, because _evaluate_inner() only rejects trailing tokens.

Also applies to: 292-302

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 189 - 210, _pars
e_atom_token currently masks malformed expressions by returning (False, pos) for
missing operands and unclosed groups, allowing prefixes to be treated as valid;
change it (and the similar logic around lines 292-302) so that instead of
returning a benign False you raise a clear parse exception (e.g., ValueError or
a new ParseError) when you encounter end-of-input after an operator, an unclosed
'(' or a bare operator token, and let that exception propagate out of
_parse_or/_parse_and/_evaluate_inner so callers reject malformed inputs rather
than silently accepting valid prefixes.
web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts (1)

76-94: ⚠️ Potential issue | 🟡 Minor

Add a positive assertion for the plain-string dependency form here.

not.toContain('branch:') only proves branch metadata is absent. This test would still pass if the depends_on entry were dropped entirely or emitted in the wrong non-branch shape.

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

In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts` around
lines 76 - 94, Test currently only asserts absence of branch metadata; update
the assertion to positively verify the plain-string dependency form produced by
generateYamlPreview for parallel_branch edges: assert the generated yaml
contains a depends_on key and the plain string items for the branch targets
(e.g., contains "depends_on:" and the list entries for "a" and "b") so the test
ensures depends_on wasn't dropped and is emitted as plain strings rather than
branch objects.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/security.md`:
- Line 88: Update the "JWT storage" table row to qualify the sessionStorage
claim: note that sessionStorage is tab-scoped but a newly opened window created
via window.open() can inherit an initial copy from the opener, so tokens may be
exposed to such child tabs unless the opener is suppressed; recommend using
rel="noopener" or window.open(..., 'noopener')/window.open(url, '_blank',
'noopener') (or the equivalent noopener option) to prevent this transfer, and
mention that cookie-based httpOnly auth is still a planned future enhancement.

In `@src/synthorg/engine/task_engine.py`:
- Around line 187-191: stop() currently uses
self._observer_queue.put_nowait(None) which can raise QueueFull and abort
shutdown before _drain_observer() runs; change this to use the async put so the
sentinel is enqueued reliably (replace put_nowait(None) with await
self._observer_queue.put(None) inside the stop() coroutine) or otherwise handle
QueueFull by awaiting until space is available, ensuring any QueueFull is
caught/awaited so _drain_observer() is always reached.
- Around line 763-774: The observer loop can break early because stop() clears
_running before _drain_processing() finishes, so if processing enqueues more
events later the dispatcher will exit and leave them stranded; change the
shutdown ordering so _drain_processing() completes before clearing the
loop-running flag (i.e. have stop() await self._drain_processing() or set a
dedicated _stopping/_drain_complete event and only set _running = False after
draining), and/or modify the timeout branch in the loop to check the
drain-completion flag (not just _running) before breaking; reference stop(),
_drain_processing(), _running, and _observer_queue to locate the relevant
changes.

In `@src/synthorg/engine/workflow/condition_eval.py`:
- Around line 78-100: _summary: Bare-key lookups in _eval_atom now use
context.get and silently return False for missing keys, hiding misconfigurations
that should raise a KeyError/WorkflowConditionEvalError._

In _eval_atom, stop using context.get(expr) for bare-key lookups; instead access
the key so a missing name raises the KeyError that the production caller (the
workflow activation helper that treats KeyError as a workflow-condition failure)
can catch and convert to a WorkflowConditionEvalError; i.e., detect bare
identifiers after comparisons and either return bool(context[expr]) or
explicitly raise the appropriate WorkflowConditionEvalError/KeyError when expr
is not present in context so missing workflow context is not silently treated as
False.

In `@web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts`:
- Around line 110-127: Update the test "falls back to counter inference for
object without branch" in yaml-to-nodes.test.ts so that the depends_on entries
use the object form instead of strings: replace the depends_on string entries
for yes_step and no_step with object-form entries like "- { id: check }" (i.e.,
use { id: check } for each depends_on item) so the test covers the branchless
object-form path used by the YAML-to-nodes conversion logic.

In `@web/src/pages/workflow-editor/yaml-to-nodes.ts`:
- Around line 261-278: The bug is that conditionalBranchCounters is incremented
for every explicit branch value, causing legacy implicit dependencies to become
desynchronized when an explicit 'false' is seen; change the explicit-branch
handling so you only advance the counter when the explicitBranch indicates the
true branch. Concretely, in the explicitBranch !== undefined branch (where you
set edgeType and check sourceStep.type), only call
conditionalBranchCounters.set(depId, branchIdx + 1) when sourceStep.type ===
'conditional' && sourceStep.step.condition && explicitBranch === 'true' (leave
the existing counter increment in the fallback/implicit branch logic unchanged);
this preserves behavior for explicit true branches while keeping explicit false
from shifting the legacy counter used by inferDependsOnEdgeType.

---

Duplicate comments:
In `@src/synthorg/engine/workflow/condition_eval.py`:
- Around line 115-142: The tokenizer currently splits on AND/OR/NOT even when
those words are inside comparison values; update _tokenize and the
compound-detection logic so comparison expressions are recognized as single
atoms before keyword splitting: modify _tokenize to first scan for comparison
operators (e.g., ==, !=, <=, >=, <, >) and treat the entire "lhs operator rhs"
span as one token (so values like "OR" or "Research AND Development" remain
inside the comparison atom), and adjust _has_compound_operators to operate on
these comparison-aware tokens (or run the comparison-atom pass first) so
compound parsing only triggers for true logical operators outside comparison
atoms; reference _tokenize and _has_compound_operators to locate the changes.
- Around line 189-210: _pars e_atom_token currently masks malformed expressions
by returning (False, pos) for missing operands and unclosed groups, allowing
prefixes to be treated as valid; change it (and the similar logic around lines
292-302) so that instead of returning a benign False you raise a clear parse
exception (e.g., ValueError or a new ParseError) when you encounter end-of-input
after an operator, an unclosed '(' or a bare operator token, and let that
exception propagate out of _parse_or/_parse_and/_evaluate_inner so callers
reject malformed inputs rather than silently accepting valid prefixes.

In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts`:
- Around line 76-94: Test currently only asserts absence of branch metadata;
update the assertion to positively verify the plain-string dependency form
produced by generateYamlPreview for parallel_branch edges: assert the generated
yaml contains a depends_on key and the plain string items for the branch targets
(e.g., contains "depends_on:" and the list entries for "a" and "b") so the test
ensures depends_on wasn't dropped and is emitted as plain strings rather than
branch objects.
🪄 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: c9fc3b12-d4f8-41cd-99a9-d976adfb89b5

📥 Commits

Reviewing files that changed from the base of the PR and between 3edf097 and b3a03c5.

📒 Files selected for processing (15)
  • docs/design/engine.md
  • docs/security.md
  • src/synthorg/config/schema.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
  • src/synthorg/observability/events/condition_eval.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
  • src/synthorg/persistence/workflow_execution_repo.py
  • tests/unit/config/test_loader.py
  • tests/unit/engine/test_task_engine_observers.py
  • tests/unit/engine/workflow/test_execution_service.py
  • tests/unit/observability/test_events.py
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Python 3.14+ required; no from __future__ import annotations (PEP 649 native lazy annotations)
Use PEP 758 except syntax: except A, B: (no parentheses) -- ruff enforces this on Python 3.14
Type hints required on all public functions; mypy strict mode enforced
Docstrings in Google style, required on all public classes and functions (enforced by ruff D rules)
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.
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; separate mutable-via-copy models (using model_copy(update=...)) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Pydantic v2: use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time
Use @computed_field for derived values in Pydantic models instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens)
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models -- 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.
Line length: 88 characters (ruff enforced)
Functions must be < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).

Files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/condition_eval.py
  • tests/unit/config/test_loader.py
  • src/synthorg/config/schema.py
  • src/synthorg/persistence/workflow_execution_repo.py
  • tests/unit/engine/test_task_engine_observers.py
  • src/synthorg/engine/workflow/condition_eval.py
  • tests/unit/engine/workflow/test_execution_service.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Vendor names in test files must use test-provider, test-small-001, etc.
Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Coverage minimum 80% enforced in CI
Async testing: asyncio_mode = "auto" in conftest -- no manual @pytest.mark.asyncio needed
Default timeout: 30 seconds per test (global in pyproject.toml). Non-default overrides like timeout(60) are allowed but avoid adding per-file markers.
Prefer @pytest.mark.parametrize for testing similar cases in Python tests
Property-based testing in Python: use Hypothesis (@given + @settings). Profiles in tests/conftest.py: ci (deterministic, 10 examples), dev (1000 examples), fuzz (10,000 examples, no deadline), extreme (500,000 examples). Controlled via HYPOTHESIS_PROFILE env var.
When Hypothesis finds a failure, fix the underlying bug and add an explicit @example(...) decorator to permanently cover the case in CI
Never skip, dismiss, or ignore flaky tests -- always fix them fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For indefinite blocking, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/observability/test_events.py
  • tests/unit/config/test_loader.py
  • tests/unit/engine/test_task_engine_observers.py
  • tests/unit/engine/workflow/test_execution_service.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/observability/test_events.py
  • tests/unit/config/test_loader.py
  • tests/unit/engine/test_task_engine_observers.py
  • tests/unit/engine/workflow/test_execution_service.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr) for handler construction
Logger variable name must always be logger (not _logger, not log)
Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
Structured kwargs in 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 level
DEBUG logging 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. Vendor names allowed only in: Operations design page, .claude/ files, third-party import paths, and src/synthorg/providers/presets.py

Files:

  • src/synthorg/observability/events/condition_eval.py
  • src/synthorg/config/schema.py
  • src/synthorg/persistence/workflow_execution_repo.py
  • src/synthorg/engine/workflow/condition_eval.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.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/observability/events/condition_eval.py
  • src/synthorg/config/schema.py
  • src/synthorg/persistence/workflow_execution_repo.py
  • src/synthorg/engine/workflow/condition_eval.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

web/src/**/*.{ts,tsx}: Web dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and @/lib/motion presets.
Web dashboard: design token rules enforced by PostToolUse hook (scripts/check_web_design_system.py)
TypeScript/JavaScript: Node.js 22+, TypeScript 6.0+, ESLint with zero warnings enforced on web/src/**/*.{ts,tsx}
Web dashboard YAML parsing: use js-yaml library (in package.json)

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

React testing: use fast-check for property-based testing (fc.assert + fc.property)

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard styling: Tailwind CSS 4 for utility classes; never hardcode pixel values or colors

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
🧠 Learnings (64)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Git commits: `<type>: <description>` -- types: feat, fix, refactor, docs, test, chore, perf, ci (enforced by commitizen)
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...
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

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

Applied to files:

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

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

Applied to files:

  • tests/unit/observability/test_events.py
  • src/synthorg/observability/events/condition_eval.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls

Applied to files:

  • tests/unit/observability/test_events.py
📚 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/condition_eval.py
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`

Applied to files:

  • src/synthorg/observability/events/condition_eval.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/condition_eval.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/condition_eval.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/condition_eval.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/condition_eval.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/condition_eval.py
📚 Learning: 2026-04-01T20:43:51.878Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T20:43:51.878Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code — use design tokens and `@/lib/motion` presets

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.

Applied to files:

  • docs/security.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: Applies to web/src/**/*.{ts,tsx} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presets

Applied to files:

  • docs/security.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: Applies to web/src/**/*.{ts,tsx} : Use React 19, TypeScript 6.0+, and design system tokens from shadcn/ui + Tailwind CSS 4 + Radix UI in web dashboard

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor, TagInput, MetadataGrid, ProjectStatusBadge, ContentTypeBadge)

Applied to files:

  • docs/security.md
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast/ToastContainer, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem, Drawer, form fields, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor) before creating new components

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/components/ui/**/*.tsx : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing

Applied to files:

  • docs/security.md
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/config/schema.py
  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).

Applied to files:

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

Applied to files:

  • src/synthorg/config/schema.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.

Applied to files:

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

Applied to files:

  • src/synthorg/config/schema.py
  • src/synthorg/engine/task_engine.py
  • docs/design/engine.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: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).

Applied to files:

  • src/synthorg/config/schema.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 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/test_task_engine_observers.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled (e.g. simulating a slow provider or stubborn coroutine), use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)` -- it is cancellation-safe

Applied to files:

  • tests/unit/engine/test_task_engine_observers.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 tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally; mock `time.monotonic()` and `asyncio.sleep()` for determinism; use `asyncio.Event().wait()` for indefinite blocking instead of `asyncio.sleep(large_number)`

Applied to files:

  • tests/unit/engine/test_task_engine_observers.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 tests/**/*.py : Never skip, dismiss, or ignore flaky tests; fix them fully and fundamentally by mocking `time.monotonic()` and `asyncio.sleep()` for determinism or using `asyncio.Event().wait()` for indefinite blocking

Applied to files:

  • tests/unit/engine/test_task_engine_observers.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 tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests -- always fix them fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()`. For indefinite blocking, use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)`.

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : Flaky tests: NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-03-16T10:40:25.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T10:40:25.144Z
Learning: Applies to tests/**/*.py : Async testing: asyncio_mode = 'auto' — no manual pytest.mark.asyncio needed.

Applied to files:

  • tests/unit/engine/test_task_engine_observers.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 : Every module with business logic MUST have `from synthorg.observability import get_logger` followed by `logger = get_logger(__name__)`.

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : 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. Variable name: always `logger`.

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.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 src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.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 : 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. Variable name: always `logger` (not `_logger`, not `log`).

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.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 : 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.

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.py
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard YAML parsing: use `js-yaml` library (in package.json)

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : 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`. Existing code is being migrated incrementally.

Applied to files:

  • src/synthorg/engine/task_engine.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/task_engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.py
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
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/task_engine.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.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/task_engine.py
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`

Applied to files:

  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
🔇 Additional comments (10)
src/synthorg/config/schema.py (3)

28-28: LGTM!

The PerformanceConfig import is correctly placed and follows the existing import organization pattern.


93-98: Documentation improvements look good.

The cost field descriptions now correctly reference "base currency" instead of hardcoded "USD", and the subscription_token description is more accurate. No functional changes.

Also applies to: 185-185


567-568: LGTM!

The performance field is correctly added following the established pattern:

  • Uses default_factory=PerformanceConfig for proper frozen model initialization
  • Docstring comprehensively documents the field's purpose (quality judge, CI/LLM weights, trend thresholds)
  • Integrates properly with _build_performance_tracker in app.py which receives effective_config.performance

Also applies to: 681-684

tests/unit/config/test_loader.py (2)

4-4: LGTM!

The refactored test helper is cleaner and more readable:

  • Never return type correctly indicates the function always raises
  • Direct raise PermissionError(msg) is simpler than the previous generator-based pattern
  • Proper typing with *a: object, **kw: object to match any call signature

Also applies to: 69-76


362-378: LGTM!

The round-trip test is well-designed:

  • Validates the YAML performance: section is correctly parsed and populated in RootConfig.performance
  • Uses compliant vendor-neutral names (test-judge-001, test-provider)
  • Weight values (0.3 + 0.7 = 1.0) satisfy the PerformanceConfig validator constraint
  • Covers the key fields consumed by _build_performance_tracker: quality_judge_model, quality_judge_provider, quality_ci_weight, quality_llm_weight

This directly validates the #1061 objective to ensure the YAML loader parses the performance: section.

src/synthorg/persistence/sqlite/workflow_execution_repo.py (1)

30-31: Repository-level task lookup looks correct.

Scoping the query to RUNNING, reusing _deserialize_row(), and logging hit/miss/error paths gives the service the fast lookup it needed without losing observability context.

Also applies to: 418-474

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

95-121: The fake repository now matches the new contract well.

Deep-copying returned executions and adding find_by_task_id() keeps the tests isolated while exercising the same lookup path the service now uses.

web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts (1)

15-108: Coverage is strong across explicit branches, warnings, and invalid shapes.

These cases exercise backward-compatible strings, explicit true/false metadata, mixed arrays, unrecognized-branch warnings, and malformed dependency inputs.

Also applies to: 136-195

web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts (1)

53-74: Conditional branch serialization is covered well.

This checks both branch polarities and the quoted 'true' / 'false' values the exporter is supposed to emit.

docs/design/engine.md (1)

220-222: The design doc now reflects the new runtime contracts clearly.

The updated sections document explicit branch metadata, boolean-expression precedence, and the observer queue’s best-effort dispatch and shutdown behavior in a way that matches the implementation changes in this PR.

Also applies to: 265-271, 370-430

Comment on lines +78 to +100
def _eval_atom(
expr: str,
context: Mapping[str, object],
) -> bool:
"""Evaluate a single atomic expression (no compound operators).

Handles boolean literals, comparisons, and key lookups.
"""
expr = expr.strip()
if not expr:
return False

lower = expr.lower()
if lower == "true":
return True
if lower == "false":
return False

result = _eval_comparison(expr, context)
if result is not None:
return result

return bool(context.get(expr))
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

Bare-key lookups now hide missing workflow context.

_eval_atom() switched has_budget-style expressions to context.get(), so absent keys silently evaluate to False. The only production caller in src/synthorg/engine/workflow/execution_activation_helpers.py:185-196 still treats KeyError as a workflow-condition failure, so this turns a misconfigured workflow into the false branch instead of a WorkflowConditionEvalError.

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 78 - 100,
_summary: Bare-key lookups in _eval_atom now use context.get and silently return
False for missing keys, hiding misconfigurations that should raise a
KeyError/WorkflowConditionEvalError._

In _eval_atom, stop using context.get(expr) for bare-key lookups; instead access
the key so a missing name raises the KeyError that the production caller (the
workflow activation helper that treats KeyError as a workflow-condition failure)
can catch and convert to a WorkflowConditionEvalError; i.e., detect bare
identifiers after comparisons and either return bool(context[expr]) or
explicitly raise the appropriate WorkflowConditionEvalError/KeyError when expr
is not present in context so missing workflow context is not silently treated as
False.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

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

237-273: ⚠️ Potential issue | 🟠 Major

Move normalization inside the guarded path.

Line 259 calls expression.strip() before the try. If malformed workflow data passes None or any other non-string here, this raises AttributeError before the WARNING log and before the fail-closed behavior; the main caller in src/synthorg/engine/workflow/execution_activation_helpers.py:185-196 does not catch that exception either.

🛡️ Proposed fix
-    expr = expression.strip()
-    if not expr:
-        return False
-
     try:
+        if not isinstance(expression, str):
+            raise TypeError("Condition expression must be a string")
+        expr = expression.strip()
+        if not expr:
+            return False
         return _evaluate_inner(expr, context)
     except MemoryError, RecursionError:
         raise
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 237 - 273, The
call to expression.strip() happens before the try, so non-string inputs (e.g.
None) raise AttributeError before the parse-error guard; move normalization into
the guarded path inside evaluate_condition: remove the top-level expr =
expression.strip() and the pre-try empty check, and inside the try block compute
expr = expression.strip() only when isinstance(expression, str) else expr = ""
(or otherwise coerce safely), then proceed to call _evaluate_inner(expr,
context); ensure the except Exception logging/return False behavior remains
unchanged so malformed/non-string inputs fail closed and are logged.
♻️ Duplicate comments (1)
src/synthorg/engine/workflow/condition_eval.py (1)

22-29: ⚠️ Potential issue | 🟠 Major

Don’t paper over the comparison regression with documentation.

The new note is accurate about the current behavior, but _has_compound_operators() and _tokenize() still split status == OR, title == Research AND Development, and label == Draft (v2) into boolean syntax. Those used to be valid simple comparisons; now they fail closed and can send a workflow down the wrong branch. The parser needs to keep full lhs == rhs / lhs != rhs atoms opaque until operators outside the comparison are identified.

Also applies to: 115-142, 223-231

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 22 - 29, The
parser currently splits operator words inside comparison values because
_has_compound_operators() and _tokenize() eagerly tokenize AND/OR/NOT
everywhere; change the tokenizer to first recognize and consume full comparison
atoms (patterns matching lhs (==|!=) rhs, including rhs containing spaces and
operator words or parens) and treat those atoms as single opaque tokens, then
only run compound-operator splitting on the remaining token stream; update
_has_compound_operators() to detect operators only outside these comparison-atom
tokens so expressions like "status == OR", "title == Research AND Development",
and "label == Draft (v2)" remain single comparison atoms.
🤖 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/condition_eval.py`:
- Around line 37-45: The function evaluate_condition() uses Mapping in its type
hints but Mapping is only imported inside the TYPE_CHECKING guard, which causes
typing.get_type_hints() to raise NameError under PEP 649; fix this by removing
the TYPE_CHECKING-only import and importing Mapping unconditionally at the
top-level (i.e., bring Mapping out of the TYPE_CHECKING block), delete the
guarded import, and ensure the runtime namespace contains Mapping so
evaluate_condition(), typing.get_type_hints, and related runtime introspection
work correctly.

In `@tests/unit/engine/workflow/test_condition_eval.py`:
- Around line 157-315: The TestCompoundConditions class repeats many cases that
only change the expression, context and expected result; refactor by replacing
groups of individual tests with pytest.mark.parametrize tests that call
evaluate_condition, e.g. create parameterized tables for AND cases, OR cases,
NOT cases, precedence/parentheses cases, edge cases and the complex expressions,
keeping the existing test names as parameter set ids where useful; update test
functions (e.g. test_and_..., test_or_..., test_not_..., test_precedence,
test_parens, test_edge_cases, test_complex_expression) to accept parameters
(expression, ctx, expected) and assert evaluate_condition(expression, ctx) is
expected, ensuring you preserve special cases like nested parens and malformed
input.

In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts`:
- Around line 79-95: The test constructs an impossible graph by using a node of
type 'parallel_branch' instead of the real split node type; update the fixture
so the fork node is created with makeNode('fork', 'parallel_split') and ensure
edges still use the 'parallel_branch' edge type; then assert the YAML uses the
split step's branches field (e.g., contains 'branches:' and branch entries)
rather than expecting child 'depends_on' strings — adjust expectations around
generateYamlPreview(nodes, edges, 'test', 'agile') accordingly to validate
correct parallel_split export.
- Around line 70-73: The current assertions on the generated yaml use
document-wide regexes and can pass without verifying a { id, branch } entry
inside a step's depends_on; instead parse the YAML string into an object
(yaml.parse or similar) and assert against the specific step's depends_on
structure (e.g., locate the dependent step by name in the parsed object and
check that its depends_on contains an entry with id: 'check' and branch:
true/false). Update the assertions that use expect(yaml).toMatch(/id:\s*check/)
and expect(yaml).toMatch(/branch:.../) to inspect the parsed object (the yaml
variable and its depends_on property) and make the same change for the other
test block referenced (lines 113-115).

In `@web/src/pages/workflow-editor/yaml-to-nodes.ts`:
- Around line 236-240: The code treats branch values as strings but YAML
booleans are parsed as booleans, so change handling to normalize obj.branch to a
string before checking: compute a branchStr = obj.branch !== undefined ?
String(obj.branch) : undefined (or use typeof check to preserve undefined), then
check branchStr === 'true' || branchStr === 'false' and assign explicitBranch =
branchStr; update the warnings to use String(obj.branch) for unrecognized
values. Apply the same normalization patch to the other branch-handling block
later in the file (the block around the 261–274 logic) so both places
consistently handle boolean and string branch values (use the same
branchStr/local variable names to locate the code).

---

Outside diff comments:
In `@src/synthorg/engine/workflow/condition_eval.py`:
- Around line 237-273: The call to expression.strip() happens before the try, so
non-string inputs (e.g. None) raise AttributeError before the parse-error guard;
move normalization into the guarded path inside evaluate_condition: remove the
top-level expr = expression.strip() and the pre-try empty check, and inside the
try block compute expr = expression.strip() only when isinstance(expression,
str) else expr = "" (or otherwise coerce safely), then proceed to call
_evaluate_inner(expr, context); ensure the except Exception logging/return False
behavior remains unchanged so malformed/non-string inputs fail closed and are
logged.

---

Duplicate comments:
In `@src/synthorg/engine/workflow/condition_eval.py`:
- Around line 22-29: The parser currently splits operator words inside
comparison values because _has_compound_operators() and _tokenize() eagerly
tokenize AND/OR/NOT everywhere; change the tokenizer to first recognize and
consume full comparison atoms (patterns matching lhs (==|!=) rhs, including rhs
containing spaces and operator words or parens) and treat those atoms as single
opaque tokens, then only run compound-operator splitting on the remaining token
stream; update _has_compound_operators() to detect operators only outside these
comparison-atom tokens so expressions like "status == OR", "title == Research
AND Development", and "label == Draft (v2)" remain single comparison atoms.
🪄 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: 25c897b6-d5e7-431b-b1d6-6a18ae23c703

📥 Commits

Reviewing files that changed from the base of the PR and between b3a03c5 and 09a876b.

📒 Files selected for processing (7)
  • docs/security.md
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
  • tests/unit/engine/workflow/test_condition_eval.py
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (7)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

web/src/**/*.{ts,tsx}: Web dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and @/lib/motion presets.
Web dashboard: design token rules enforced by PostToolUse hook (scripts/check_web_design_system.py)
TypeScript/JavaScript: Node.js 22+, TypeScript 6.0+, ESLint with zero warnings enforced on web/src/**/*.{ts,tsx}
Web dashboard YAML parsing: use js-yaml library (in package.json)

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

React testing: use fast-check for property-based testing (fc.assert + fc.property)

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard styling: Tailwind CSS 4 for utility classes; never hardcode pixel values or colors

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Python 3.14+ required; no from __future__ import annotations (PEP 649 native lazy annotations)
Use PEP 758 except syntax: except A, B: (no parentheses) -- ruff enforces this on Python 3.14
Type hints required on all public functions; mypy strict mode enforced
Docstrings in Google style, required on all public classes and functions (enforced by ruff D rules)
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.
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; separate mutable-via-copy models (using model_copy(update=...)) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Pydantic v2: use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time
Use @computed_field for derived values in Pydantic models instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens)
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models -- 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.
Line length: 88 characters (ruff enforced)
Functions must be < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).

Files:

  • src/synthorg/engine/task_engine.py
  • tests/unit/engine/workflow/test_condition_eval.py
  • src/synthorg/engine/workflow/condition_eval.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(__name__)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print(..., file=sys.stderr) for handler construction
Logger variable name must always be logger (not _logger, not log)
Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT
Structured kwargs in 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 level
DEBUG logging 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. Vendor names allowed only in: Operations design page, .claude/ files, third-party import paths, and src/synthorg/providers/presets.py

Files:

  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.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/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Vendor names in test files must use test-provider, test-small-001, etc.
Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Coverage minimum 80% enforced in CI
Async testing: asyncio_mode = "auto" in conftest -- no manual @pytest.mark.asyncio needed
Default timeout: 30 seconds per test (global in pyproject.toml). Non-default overrides like timeout(60) are allowed but avoid adding per-file markers.
Prefer @pytest.mark.parametrize for testing similar cases in Python tests
Property-based testing in Python: use Hypothesis (@given + @settings). Profiles in tests/conftest.py: ci (deterministic, 10 examples), dev (1000 examples), fuzz (10,000 examples, no deadline), extreme (500,000 examples). Controlled via HYPOTHESIS_PROFILE env var.
When Hypothesis finds a failure, fix the underlying bug and add an explicit @example(...) decorator to permanently cover the case in CI
Never skip, dismiss, or ignore flaky tests -- always fix them fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For indefinite blocking, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/engine/workflow/test_condition_eval.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_condition_eval.py
🧠 Learnings (38)
📓 Common learnings
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...
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
📚 Learning: 2026-04-01T20:43:51.878Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T20:43:51.878Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code — use design tokens and `@/lib/motion` presets

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.

Applied to files:

  • docs/security.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: Applies to web/src/**/*.{ts,tsx} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presets

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor, TagInput, MetadataGrid, ProjectStatusBadge, ContentTypeBadge)

Applied to files:

  • docs/security.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: Applies to web/src/**/*.{ts,tsx} : Use React 19, TypeScript 6.0+, and design system tokens from shadcn/ui + Tailwind CSS 4 + Radix UI in web dashboard

Applied to files:

  • docs/security.md
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast/ToastContainer, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem, Drawer, form fields, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor) before creating new components

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/components/ui/**/*.tsx : Use design tokens exclusively in new components -- no hardcoded colors, fonts, or spacing

Applied to files:

  • docs/security.md
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing

Applied to files:

  • docs/security.md
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard YAML parsing: use `js-yaml` library (in package.json)

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`

Applied to files:

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

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : 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`. Existing code is being migrated incrementally.

Applied to files:

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

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/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:

  • src/synthorg/engine/task_engine.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/task_engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.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/task_engine.py
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
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/task_engine.py
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)

Applied to files:

  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
📚 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 : Every module with business logic MUST have `from synthorg.observability import get_logger` followed by `logger = get_logger(__name__)`.

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : 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. Variable name: always `logger`.

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.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 src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.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 : 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. Variable name: always `logger` (not `_logger`, not `log`).

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.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 : 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.

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`

Applied to files:

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

Applied to files:

  • src/synthorg/engine/workflow/condition_eval.py
🔇 Additional comments (9)
docs/security.md (1)

88-88: Security caveat is now correctly documented.

Line 88 now accurately qualifies sessionStorage behavior with window.open opener inheritance and includes concrete noopener mitigations plus the planned httpOnly-cookie direction.

src/synthorg/engine/task_engine.py (8)

1-7: LGTM!

The docstring update accurately describes the decoupled observer dispatch architecture, and the new event constant imports are appropriate for the added logging.

Also applies to: 56-58


123-126: LGTM!

Bounded observer queue prevents unbounded memory growth. Using the same max_queue_size as the mutation queue is a reasonable default for the decoupled dispatch model.


163-166: LGTM!

The observer dispatch loop is correctly spawned as a sibling background task alongside the mutation processing loop.


187-193: Correct shutdown ordering addresses prior review concerns.

The changes properly address both prior review comments:

  1. Uses await self._observer_queue.put(None) instead of put_nowait(None), preventing QueueFull from aborting shutdown.
  2. Sentinel is placed only after _drain_processing() completes, ensuring all mutation-generated events are enqueued before signaling the observer loop to exit.

197-226: LGTM!

The asyncio.shield + explicit cancel pattern provides controlled shutdown, and the BaseException handler ensures in-flight futures are resolved on unexpected errors.


228-246: LGTM!

Consistent drain pattern with proper timeout handling. The warning log with remaining queue size enables monitoring of dropped observer events, which aligns with the documented best-effort semantics.


723-733: LGTM!

Non-blocking put_nowait with QueueFull fallback ensures observer slowness never blocks the mutation pipeline—aligning with the PR objective. The warning log with full context enables monitoring for dropped events.


758-786: LGTM!

The dispatch loop correctly relies on the sentinel (not _running flag) to exit, ensuring all enqueued events are processed before shutdown. This addresses the prior concern about premature dispatcher exit. Exception handling maintains best-effort semantics while propagating catastrophic errors.

Comment on lines 37 to 45
from typing import TYPE_CHECKING

from synthorg.observability import get_logger
from synthorg.observability.events.condition_eval import (
CONDITION_EVAL_PARSE_ERROR,
)

if TYPE_CHECKING:
from collections.abc import Mapping
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

🧩 Analysis chain

🏁 Script executed:

cat -n src/synthorg/engine/workflow/condition_eval.py | head -80

Repository: Aureliolo/synthorg

Length of output: 3380


🏁 Script executed:

rg -A 10 "def evaluate_condition" src/synthorg/engine/workflow/condition_eval.py

Repository: Aureliolo/synthorg

Length of output: 503


Import Mapping at runtime under the Python 3.14 baseline.

evaluate_condition() is a public API with type hints referencing Mapping, which exists only in the TYPE_CHECKING block. Under PEP 649, calling typing.get_type_hints() on this function will raise NameError because Mapping is not in the runtime namespace. Import Mapping unconditionally and drop the guard.

♻️ Proposed fix
import re
-from typing import TYPE_CHECKING
+from collections.abc import Mapping
 
 from synthorg.observability import get_logger
 from synthorg.observability.events.condition_eval import (
     CONDITION_EVAL_PARSE_ERROR,
 )
-
-if TYPE_CHECKING:
-    from collections.abc import Mapping
 
 logger = get_logger(__name__)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 37 - 45, The
function evaluate_condition() uses Mapping in its type hints but Mapping is only
imported inside the TYPE_CHECKING guard, which causes typing.get_type_hints() to
raise NameError under PEP 649; fix this by removing the TYPE_CHECKING-only
import and importing Mapping unconditionally at the top-level (i.e., bring
Mapping out of the TYPE_CHECKING block), delete the guarded import, and ensure
the runtime namespace contains Mapping so evaluate_condition(),
typing.get_type_hints, and related runtime introspection work correctly.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts`:
- Line 46: The test declares a local variable named `yaml` which shadows the
imported `js-yaml` module; rename the test variable (e.g., to `output`) where
`generateYamlPreview(nodes, edges, 'test', 'agile')` is assigned and update any
subsequent assertions/usages to that new name so it no longer conflicts with the
`js-yaml` import and matches the naming used in other tests.
🪄 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: d45fe575-8b9f-400c-b7e9-9f382a0fd2b0

📥 Commits

Reviewing files that changed from the base of the PR and between 09a876b and ffb8536.

📒 Files selected for processing (3)
  • tests/unit/engine/workflow/test_condition_eval.py
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

web/src/**/*.{ts,tsx}: Web dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and @/lib/motion presets.
Web dashboard: design token rules enforced by PostToolUse hook (scripts/check_web_design_system.py)
TypeScript/JavaScript: Node.js 22+, TypeScript 6.0+, ESLint with zero warnings enforced on web/src/**/*.{ts,tsx}
Web dashboard YAML parsing: use js-yaml library (in package.json)

Files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard styling: Tailwind CSS 4 for utility classes; never hardcode pixel values or colors

Files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

React testing: use fast-check for property-based testing (fc.assert + fc.property)

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Python 3.14+ required; no from __future__ import annotations (PEP 649 native lazy annotations)
Use PEP 758 except syntax: except A, B: (no parentheses) -- ruff enforces this on Python 3.14
Type hints required on all public functions; mypy strict mode enforced
Docstrings in Google style, required on all public classes and functions (enforced by ruff D rules)
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.
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; separate mutable-via-copy models (using model_copy(update=...)) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Pydantic v2: use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time
Use @computed_field for derived values in Pydantic models instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens)
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models -- 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.
Line length: 88 characters (ruff enforced)
Functions must be < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).

Files:

  • tests/unit/engine/workflow/test_condition_eval.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Vendor names in test files must use test-provider, test-small-001, etc.
Test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow
Coverage minimum 80% enforced in CI
Async testing: asyncio_mode = "auto" in conftest -- no manual @pytest.mark.asyncio needed
Default timeout: 30 seconds per test (global in pyproject.toml). Non-default overrides like timeout(60) are allowed but avoid adding per-file markers.
Prefer @pytest.mark.parametrize for testing similar cases in Python tests
Property-based testing in Python: use Hypothesis (@given + @settings). Profiles in tests/conftest.py: ci (deterministic, 10 examples), dev (1000 examples), fuzz (10,000 examples, no deadline), extreme (500,000 examples). Controlled via HYPOTHESIS_PROFILE env var.
When Hypothesis finds a failure, fix the underlying bug and add an explicit @example(...) decorator to permanently cover the case in CI
Never skip, dismiss, or ignore flaky tests -- always fix them fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep(). For indefinite blocking, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/engine/workflow/test_condition_eval.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_condition_eval.py
🧠 Learnings (9)
📓 Common learnings
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...
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)
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.
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard YAML parsing: use `js-yaml` library (in package.json)

Applied to files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer pytest.mark.parametrize for testing similar cases.

Applied to files:

  • tests/unit/engine/workflow/test_condition_eval.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases.

Applied to files:

  • tests/unit/engine/workflow/test_condition_eval.py
📚 Learning: 2026-03-31T20:29:10.177Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:29:10.177Z
Learning: Applies to tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases

Applied to files:

  • tests/unit/engine/workflow/test_condition_eval.py
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases in Python tests

Applied to files:

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

Applied to files:

  • tests/unit/engine/workflow/test_condition_eval.py
🔇 Additional comments (6)
tests/unit/engine/workflow/test_condition_eval.py (1)

157-354: Good refactor and coverage expansion for compound condition evaluation.

The parametrized structure is cleaner, keeps intent readable, and adds strong edge-case coverage (precedence, malformed inputs, nested parentheses, case-insensitive operators, and double-NOT) without weakening backward-compatibility assertions.

Based on learnings: Applies to tests/**/*.py : Prefer pytest.mark.parametrize for testing similar cases in Python tests.

web/src/pages/workflow-editor/yaml-to-nodes.ts (4)

38-38: LGTM!

The type correctly reflects the new schema supporting plain IDs (string/number) and structured { id, branch } objects, with branch typed loosely to handle validation at runtime.


75-77: LGTM!

The updated docstring clearly documents the new precedence: explicit branch metadata takes priority over counter-based inference.


228-247: Well-structured parsing with proper YAML boolean handling.

The String(obj.branch) normalization on line 236 correctly handles the YAML CORE_SCHEMA behavior where unquoted branch: true/branch: false are parsed as booleans. This ensures round-trip fidelity with the exporter.


259-281: Correct implementation of explicit branch precedence with proper counter semantics.

The logic correctly addresses the prior review concerns:

  • Explicit branch takes precedence over counter-based inference
  • Only explicitBranch === 'true' advances the counter (line 270), so an explicit 'false' entry doesn't desynchronize subsequent implicit entries
  • Warning emitted when explicit branch is applied to non-conditional source (lines 264-266)

This ensures mixed legacy imports (implicit) and new-format imports (explicit) can coexist correctly.

web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts (1)

33-136: Well-structured test suite addressing all prior feedback.

The tests properly validate the depends_on export contract by parsing YAML output and asserting on the actual structure rather than fragile regex matches. Coverage includes sequential, conditional, parallel_split, and mixed edge scenarios—aligning with the PR objectives for explicit { id, branch } branch metadata support.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts`:
- Around line 33-136: The tests under generateYamlPreview in
workflow-to-yaml.test.ts are fixture-only but must include property-based tests
using fast-check; add at least one fc.assert(fc.property(...)) that generates
random node/edge graphs and asserts exporter invariants: for conditional edges
produced by generateYamlPreview parsed YAML, depends_on entries for conditional
targets are objects with { id, branch } and for non-conditional edges they are
plain strings without a branch; use generateYamlPreview, yaml.load, and helper
makers (makeNode, makeEdge) to build randomized inputs and check the two
invariants (conditional -> object with branch; non-conditional -> no branch)
inside the property.
🪄 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: 5870a037-f4dc-4c21-8d0e-ccd3c211a562

📥 Commits

Reviewing files that changed from the base of the PR and between ffb8536 and 4afb6e1.

📒 Files selected for processing (1)
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: Dashboard Storybook Build
  • GitHub Check: Dashboard Type Check
  • GitHub Check: Dashboard Build
  • GitHub Check: Dashboard Test
  • GitHub Check: Dashboard Lint
  • GitHub Check: Type Check
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Preview
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

web/src/**/*.{ts,tsx}: Web dashboard: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and @/lib/motion presets.
Web dashboard: design token rules enforced by PostToolUse hook (scripts/check_web_design_system.py)
TypeScript/JavaScript: Node.js 22+, TypeScript 6.0+, ESLint with zero warnings enforced on web/src/**/*.{ts,tsx}
Web dashboard YAML parsing: use js-yaml library (in package.json)

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

React testing: use fast-check for property-based testing (fc.assert + fc.property)

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Web dashboard styling: Tailwind CSS 4 for utility classes; never hardcode pixel values or colors

Files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
🧠 Learnings (4)
📓 Common learnings
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...
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-04-04T15:08:44.009Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T15:08:44.009Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard YAML parsing: use `js-yaml` library (in package.json)

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
🔇 Additional comments (1)
web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts (1)

69-79: Good move to parse YAML and assert step-level structure.

Validating parsed workflow_definition.steps[*].depends_on/branches is robust and directly checks the export contract.

Also applies to: 97-107, 125-135

…ring

Add performance field to RootConfig so YAML-configured quality scoring
settings (quality_judge_model, quality_judge_provider, CI/LLM weights)
flow through to _build_performance_tracker at app startup instead of
silently falling back to defaults.

- Add PerformanceConfig import and field to RootConfig (schema.py)
- Add performance entry to default_config_dict (defaults.py)
- Wire effective_config.performance into _build_performance_tracker (app.py)
- Update RootConfigFactory in test conftest
- Add schema tests (defaults + custom) and YAML round-trip test
- Document performance config section in operations design spec

Closes #1061
Move auth token storage from localStorage to sessionStorage to reduce
XSS blast radius. sessionStorage is tab-scoped -- tokens no longer
persist across browser sessions or leak to other tabs.

Updated all auth-related storage calls in:
- stores/auth.ts (token lifecycle, expiry, must_change_password)
- api/client.ts (request interceptor, 401 handler)
- api/endpoints/providers.ts (SSE auth, 401 handler)
- All corresponding test files

Non-auth localStorage usage (theme, sidebar, viewport) unchanged.
Full HttpOnly cookie migration deferred pending backend CSRF support.

Ref #1060
Extend evaluate_condition() to support compound expressions with
AND/OR/NOT operators and parenthesized groups, matching the frontend
condition builder's capabilities.

Implementation uses a tokenizer + recursive descent parser:
- Tokenizer splits on word-boundary AND/OR/NOT and parens, preserving
  comparison atoms (keywords inside values like ORLANDO are not split)
- Parser implements precedence: NOT > AND > OR
- Quick-path: simple expressions without operators skip tokenization
- Never-raise contract preserved (all exceptions caught, return False)

29 new tests covering: AND/OR/NOT, precedence, parenthesized groups,
nested parens, case-insensitive operators, keyword-in-value safety,
edge cases (empty parens, malformed, trailing operators), complex
real-world expressions, and backward compatibility.

Ref #1060
Replace O(R*N) full scan in _find_execution_by_task() with a
repository-level find_by_task_id() method. The SQLite implementation
uses json_each() to search the node_executions JSON column, filtering
by RUNNING status first (leverages existing idx_wfe_status index).

- Add find_by_task_id to WorkflowExecutionRepository protocol
- Add SQLite implementation using json_each + json_extract
- Delegate execution_service._find_execution_by_task to repo method
- Add structured log events for the new query path
- Update all three FakeExecutionRepo implementations in tests

Ref #1060
Decouple observer notification from the single-writer mutation pipeline
by routing events through a dedicated background queue/dispatcher.

The mutation pipeline now enqueues TaskStateChanged events via
put_nowait() instead of awaiting observers inline. A dedicated
_observer_dispatch_loop (mirroring the existing _processing_loop
pattern) dequeues and delivers events to registered observers.

Benefits:
- Slow observers no longer block task state mutations
- Event ordering preserved (FIFO queue)
- Graceful shutdown drains the observer queue

New test: test_slow_observer_does_not_block_mutations verifies that
two mutations complete while a blocked observer holds an event gate.
Existing tests updated with _flush_observers() helper for async dispatch.

Ref #1060
Extend the YAML workflow schema so depends_on entries support both
plain string IDs and objects with explicit branch metadata:
  depends_on:
    - step_a                        # sequential (plain string)
    - { id: check, branch: 'true' } # conditional (explicit branch)

Exporter (workflow-to-yaml.ts):
- Emits { id, branch } for conditional_true/conditional_false edges
- Plain strings for sequential edges (unchanged)

Importer (yaml-to-nodes.ts):
- Parses string | { id, branch? } union entries
- Explicit branch field takes precedence over counter-based inference
- Counter-based inference preserved as fallback for backward compat

This fixes the round-trip issue where reordering YAML steps could
flip which successor is the true vs false branch of a conditional.

10 new tests across 2 test files covering: plain string backward
compat, object with branch true/false, mixed entries, counter
fallback, invalid entries, empty id, and exporter branch emission.

Ref #1060
Add missing find_by_task_id to _FakeWorkflowExecutionRepository in
test_protocol.py to match the updated WorkflowExecutionRepository
protocol.

Ref #1060
Aureliolo added 10 commits April 4, 2026 19:42
The _NOW fixture was set to 2026-04-03 which causes sessions to
expire after that date, making 5 tests fail when run after April 3.
Set _NOW to 2099-01-01 so session expiry is deterministic regardless
of wall clock time.
Pre-reviewed by 6 agents, 17 findings addressed:

- Fix WorkflowExecutionStatus runtime import in fakes_workflow.py
- Add sessionStorage.clear() to 3 missed auth test files
- Complete operations.md field table (windows, llm_sampling_model,
  collaboration_weights)
- Add try/except to _observer_dispatch_loop for error resilience
- Fix observer shutdown race with _observer_done event signal
- Add queue_size to observer QueueFull log warning
- Remove duplicate JSDoc block on inferDependsOnEdgeType
- Warn on unrecognized branch values in YAML depends_on
- Document operator-key-name limitation in condition evaluator
- Update CLAUDE.md engine description (observer dispatch, compound)
- Add unclosed-paren and double-NOT edge case tests
- Trim task_engine.py docstrings to stay under 800-line limit
Main fixed the time-sensitive session store tests by wrapping
affected assertions with _patch_now() context manager. Drop our
_NOW=2099 approach in favor of main's more targeted fix.
The _observer_done asyncio.Event caused the observer dispatch loop to
hang when stop() was not explicitly called (e.g. health tests using
TestClient lifespan). The event was never set, so the loop condition
'not _observer_done.is_set()' was always True, preventing loop exit.

Revert to the original '_running' flag condition which correctly
terminates the loop when the engine shuts down via any path.
Keep the try/except error resilience and queue_size log improvements.
…t, and Gemini

- condition_eval: add logger + event constants, re-raise MemoryError/RecursionError,
  reject trailing tokens (malformed->False), fix bare-keyword key lookup,
  add token count cap, update docstrings, extract _evaluate_inner
- task_engine: fix shutdown race with None sentinel on observer queue,
  extract _drain_processing/_drain_observer helpers (stop() under 50 lines),
  use remainder timeout for observer drain, add mutation_type to queue-full
  log, add event context to dispatch loop error log
- sqlite/workflow_execution_repo: pass execution ID (not task_id) to
  _deserialize_row in find_by_task_id
- workflow_execution_repo protocol: fix Raises docstring (QueryError)
- config/schema: fix USD->base currency, OAuth->Bearer in field descriptions
- docs/design/engine.md: update observer mechanism (async dispatch queue),
  architecture diagram, lifecycle section, condition evaluator (compound ops),
  YAML export depends_on format
- docs/security.md: update JWT storage from localStorage to sessionStorage
- yaml-to-nodes.ts: increment branch counter for explicit entries (mixed
  explicit+implicit fix), warn on explicit branch for non-conditional source
- test fixes: _flush_observers fails on timeout, deepcopy in fake
  list_by_definition, idiomatic raise helper, precise regex, new tests
  (parallel_branch, unrecognized branch value)
- task_engine: use async put (not put_nowait) for shutdown sentinel to
  avoid QueueFull aborting stop(); remove early-exit check from observer
  timeout branch -- loop now exits only on None sentinel
- condition_eval: _parse_atom_token raises ValueError for missing
  operands, unclosed parens, and bare operator tokens (malformed
  expressions correctly resolve to False via outer handler)
- yaml-to-nodes: only advance branch counter for explicit 'true' (not
  'false') so mixed explicit+implicit entries stay synchronized
- security.md: add window.open() sessionStorage inheritance caveat
  with noopener recommendation
- tests: update unclosed-paren expectation (False, not True), use
  object form in counter-inference test, add positive assertions for
  parallel_branch depends_on
- yaml-to-nodes: normalize obj.branch with String() to handle YAML
  boolean values (true/false parsed as booleans by CORE_SCHEMA)
- test_condition_eval: parametrize TestCompoundConditions (AND, OR,
  NOT, precedence, case, keyword-in-value, edge cases, complex)
- workflow-to-yaml tests: use parallel_split node type (not
  parallel_branch), parse YAML and assert on depends_on structure
  instead of fragile regex matching
@Aureliolo Aureliolo force-pushed the chore/config-fix-deferred-improvements branch from 4afb6e1 to b8050ec Compare April 4, 2026 17:45
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 4, 2026 17:46 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (4)
tests/unit/engine/test_task_engine_observers.py (1)

18-36: ⚠️ Potential issue | 🟠 Major

_flush_observers() still races the active callback.

engine._observer_queue.empty() becomes true as soon as the dispatcher dequeues an event, but src/synthorg/engine/task_engine.py:758-786 awaits _notify_observers() after that dequeue. This helper can therefore return while the observer is still running, which makes every post-flush assertion scheduler-dependent. Wait on a completion signal (queue.join() paired with task_done(), or equivalent) instead of empty().

As per coding guidelines, tests/**/*.py: Flaky tests: NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins.

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

In `@tests/unit/engine/test_task_engine_observers.py` around lines 18 - 36, The
helper _flush_observers currently races the active observer callback because it
checks engine._observer_queue.empty(); change it to wait for a completion signal
instead (use engine._observer_queue.join() or an equivalent awaitable) so the
helper only returns after all dequeued events have been fully processed by
TaskEngine._notify_observers(); if the dispatcher does not call task_done() for
each processed event, update the dispatcher logic in TaskEngine (the
dequeue/notify loop) to call engine._observer_queue.task_done() after finishing
each notification so queue.join() can reliably await completion; keep the
budget/timeout semantics by using asyncio.wait_for(join(), timeout=budget) to
raise on timeout.
src/synthorg/engine/workflow/condition_eval.py (3)

36-45: ⚠️ Potential issue | 🟠 Major

Import Mapping at runtime under the Python 3.14 baseline.

Mapping is referenced in lazy annotations throughout this module, but it only exists inside the TYPE_CHECKING block. Under PEP 649, typing.get_type_hints() on evaluate_condition() or the parser helpers will raise NameError once those annotations are evaluated. Move the import to runtime scope.

♻️ Proposed fix
 import re
-from typing import TYPE_CHECKING
+from collections.abc import Mapping
 
 from synthorg.observability import get_logger
 from synthorg.observability.events.condition_eval import (
     CONDITION_EVAL_PARSE_ERROR,
 )
-
-if TYPE_CHECKING:
-    from collections.abc import Mapping
 
 logger = get_logger(__name__)

As per coding guidelines, **/*.py: Python: 3.14+ (PEP 649 native lazy annotations).

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 36 - 45, The
module currently only imports Mapping inside the TYPE_CHECKING block which
causes NameError when annotations are evaluated (PEP 649); move the import of
Mapping out of the TYPE_CHECKING guard into runtime scope so it is available for
evaluate_condition() and any parser helper type annotations referenced by
typing.get_type_hints(), ensuring the symbol Mapping is imported alongside other
top-level imports in condition_eval.py.

78-100: ⚠️ Potential issue | 🟠 Major

Keep missing bare keys distinguishable from falsy values.

bool(context.get(expr)) makes an absent context name indistinguishable from legitimate values like False, 0, or "". The production activation path in src/synthorg/engine/workflow/execution_activation_helpers.py:175-196 still relies on KeyError from bare-key lookups to surface a misconfigured workflow as WorkflowConditionEvalError, so this now silently takes the false branch instead of failing fast.

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 78 - 100, The
current use of bool(context.get(expr)) hides missing keys; update _eval_atom to
distinguish absent names from falsy values by checking membership: if expr is
present in the provided context (use "if expr in context") return
bool(context[expr]); otherwise raise a KeyError (so callers like
execution_activation_helpers can detect a missing variable and surface
WorkflowConditionEvalError) instead of returning False. Ensure this change is
applied inside the _eval_atom function and remove reliance on context.get(expr)
for the final lookup.

115-142: ⚠️ Potential issue | 🟠 Major

Comparison atoms are still split on operator words and parentheses.

_has_compound_operators() routes any standalone AND / OR / NOT / paren hit into _tokenize(), and _tokenize() then breaks comparison atoms apart instead of preserving them. Inputs like status == OR, title == Research AND Development, or label == Ready (Phase 1) now evaluate False even though they were valid simple comparisons before this parser was added. Please recognize full comparison atoms before classifying logical operators, and add regression coverage for those cases.

Also applies to: 229-231, 285-286

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 115 - 142, The
tokenizer currently splits logical keywords/parentheses inside comparison
operands; update _tokenize (and related logic in _has_compound_operators) to
first detect and preserve full comparison atoms (i.e., sequences containing a
comparison operator such as ==, !=, <, >, <=, >= plus their LHS and RHS) before
applying _KEYWORD_RE so that expressions like "status == OR", "title == Research
AND Development", and "label == Ready (Phase 1)" are treated as a single atom;
adjust the matching order (or add a pre-scan using a comparison-operator regex)
so comparison patterns are appended as one token rather than split, and add
regression tests covering those three example cases to ensure they remain valid
simple comparisons.
🤖 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/condition_eval.py`:
- Around line 263-273: The broad except in evaluate_condition (wrapping
_evaluate_inner) swallows ValueError/TypeError/KeyError (raised for malformed
syntax) and returns False; update the handler to mirror the activation helper by
re-raising ValueError, TypeError, and KeyError so callers like
evaluate_condition() and the activation path in execution_activation_helpers.py
can convert them into WorkflowConditionEvalError; keep the existing re-raise for
MemoryError and RecursionError, and only catch generic Exception for logging and
returning False for other errors (use expression=expr[:200], exc_info=True as
before).

In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts`:
- Around line 138-205: The property test uses raw "return false" to signal
failures which yields poor diagnostics; update the inner property function to
use Jest expect assertions (e.g. expect(obj.id).toBeDefined(),
expect(obj.branch).toMatch(/^(true|false)$/), expect(typeof dep).toBe('string'),
expect(yesStep).toBeDefined(),
expect(Array.isArray(step.depends_on)).toBe(true), etc.) instead of returning
false so failing cases from generateYamlPreview / yaml.load / steps are reported
with clear messages; keep the same property structure, use the same symbols
(generateYamlPreview, yaml.load, steps, yesStep, noStep, taskCountArb) and
preserve fc.assert(..., { numRuns: 20 }).

In `@web/src/api/endpoints/providers.ts`:
- Around line 184-191: In pullModel, the 401 branch manually clears
sessionStorage but doesn't update the Zustand auth state; modify the 401
handling inside pullModel to call useAuthStore.getState().logout() (matching the
client.ts pattern) so the auth store is cleared and the redirect/state update
occurs — remove or replace the manual sessionStorage.removeItem(...) calls with
the logout call to keep behavior consistent with client.ts.

---

Duplicate comments:
In `@src/synthorg/engine/workflow/condition_eval.py`:
- Around line 36-45: The module currently only imports Mapping inside the
TYPE_CHECKING block which causes NameError when annotations are evaluated (PEP
649); move the import of Mapping out of the TYPE_CHECKING guard into runtime
scope so it is available for evaluate_condition() and any parser helper type
annotations referenced by typing.get_type_hints(), ensuring the symbol Mapping
is imported alongside other top-level imports in condition_eval.py.
- Around line 78-100: The current use of bool(context.get(expr)) hides missing
keys; update _eval_atom to distinguish absent names from falsy values by
checking membership: if expr is present in the provided context (use "if expr in
context") return bool(context[expr]); otherwise raise a KeyError (so callers
like execution_activation_helpers can detect a missing variable and surface
WorkflowConditionEvalError) instead of returning False. Ensure this change is
applied inside the _eval_atom function and remove reliance on context.get(expr)
for the final lookup.
- Around line 115-142: The tokenizer currently splits logical
keywords/parentheses inside comparison operands; update _tokenize (and related
logic in _has_compound_operators) to first detect and preserve full comparison
atoms (i.e., sequences containing a comparison operator such as ==, !=, <, >,
<=, >= plus their LHS and RHS) before applying _KEYWORD_RE so that expressions
like "status == OR", "title == Research AND Development", and "label == Ready
(Phase 1)" are treated as a single atom; adjust the matching order (or add a
pre-scan using a comparison-operator regex) so comparison patterns are appended
as one token rather than split, and add regression tests covering those three
example cases to ensure they remain valid simple comparisons.

In `@tests/unit/engine/test_task_engine_observers.py`:
- Around line 18-36: The helper _flush_observers currently races the active
observer callback because it checks engine._observer_queue.empty(); change it to
wait for a completion signal instead (use engine._observer_queue.join() or an
equivalent awaitable) so the helper only returns after all dequeued events have
been fully processed by TaskEngine._notify_observers(); if the dispatcher does
not call task_done() for each processed event, update the dispatcher logic in
TaskEngine (the dequeue/notify loop) to call engine._observer_queue.task_done()
after finishing each notification so queue.join() can reliably await completion;
keep the budget/timeout semantics by using asyncio.wait_for(join(),
timeout=budget) to raise on timeout.
🪄 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: 508e5640-3b38-454c-b39b-46e4e8348891

📥 Commits

Reviewing files that changed from the base of the PR and between 4afb6e1 and b8050ec.

📒 Files selected for processing (38)
  • CLAUDE.md
  • docs/design/engine.md
  • docs/design/operations.md
  • docs/security.md
  • src/synthorg/api/app.py
  • src/synthorg/config/defaults.py
  • src/synthorg/config/schema.py
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/workflow/condition_eval.py
  • src/synthorg/engine/workflow/execution_service.py
  • src/synthorg/observability/events/condition_eval.py
  • src/synthorg/observability/events/persistence.py
  • src/synthorg/observability/events/task_engine.py
  • src/synthorg/persistence/sqlite/workflow_execution_repo.py
  • src/synthorg/persistence/workflow_execution_repo.py
  • tests/unit/api/fakes_workflow.py
  • tests/unit/config/conftest.py
  • tests/unit/config/test_loader.py
  • tests/unit/config/test_schema.py
  • tests/unit/engine/test_task_engine_observers.py
  • tests/unit/engine/workflow/test_condition_eval.py
  • tests/unit/engine/workflow/test_execution_lifecycle.py
  • tests/unit/engine/workflow/test_execution_service.py
  • tests/unit/observability/test_events.py
  • tests/unit/persistence/test_protocol.py
  • web/src/__tests__/api/client-bypass.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/__tests__/pages/workflow-editor/yaml-to-nodes.test.ts
  • web/src/__tests__/router/guards.test.tsx
  • web/src/__tests__/stores/auth.test.ts
  • web/src/api/client.ts
  • web/src/api/endpoints/providers.ts
  • web/src/pages/workflow-editor/workflow-to-yaml.ts
  • web/src/pages/workflow-editor/yaml-to-nodes.ts
  • web/src/stores/auth.ts

Comment on lines +263 to 273
try:
return _evaluate_inner(expr, context)
except MemoryError, RecursionError:
raise
except Exception:
logger.warning(
CONDITION_EVAL_PARSE_ERROR,
expression=expr[:200],
exc_info=True,
)
return False
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

Align this blanket handler with the activation helper.

Lines 200-218 now raise ValueError for malformed syntax, but this except Exception immediately collapses that into False. The activation path in src/synthorg/engine/workflow/execution_activation_helpers.py:175-196 still converts ValueError, TypeError, and KeyError from evaluate_condition() into WorkflowConditionEvalError, so malformed workflow definitions will silently take the false edge unless that caller is updated in the same PR.

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

In `@src/synthorg/engine/workflow/condition_eval.py` around lines 263 - 273, The
broad except in evaluate_condition (wrapping _evaluate_inner) swallows
ValueError/TypeError/KeyError (raised for malformed syntax) and returns False;
update the handler to mirror the activation helper by re-raising ValueError,
TypeError, and KeyError so callers like evaluate_condition() and the activation
path in execution_activation_helpers.py can convert them into
WorkflowConditionEvalError; keep the existing re-raise for MemoryError and
RecursionError, and only catch generic Exception for logging and returning False
for other errors (use expression=expr[:200], exc_info=True as before).

- property test: replace return-false with expect() assertions for
  clear failure diagnostics in fast-check property
- providers.ts: call useAuthStore.getState().logout() on 401 in SSE
  pullModel handler (matches client.ts interceptor pattern)
- task_engine: add task_done() calls in observer dispatch loop;
  _flush_observers now uses queue.join() for deterministic waiting
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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/task_engine.py`:
- Around line 123-126: The observer queue (_observer_queue) currently reuses
self._config.max_queue_size; add a separate observer_queue_size field to
TaskEngineConfig (with a sensible default) and use it when constructing
_observer_queue (i.e., replace self._config.max_queue_size with
self._config.observer_queue_size in the TaskEngine initialization), so observer
throughput can be tuned independently from the mutation queue.
- Around line 185-192: Replace deprecated asyncio.get_event_loop() calls inside
stop() with asyncio.get_running_loop() to avoid DeprecationWarning;
specifically, use asyncio.get_running_loop().time() for computing deadline and
observer_budget (the two places currently calling
asyncio.get_event_loop().time()) so the timing logic around deadline, await
self._drain_processing(effective_timeout), and the subsequent await
self._observer_queue.put(None) remains unchanged.

In `@web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts`:
- Around line 72-73: Extract a small helper that centralizes yaml.load(output, {
schema: yaml.CORE_SCHEMA }) and returns the workflow_definition.steps array to
remove duplicated parsing and type assertions; e.g., add a function like
parseWorkflowSteps(output: string) that calls yaml.load with CORE_SCHEMA,
narrows the result to { workflow_definition: { steps: Array<Record<string,
unknown>> } } and returns workflow_definition.steps, then replace each
occurrence of the inline parsing (the `parsed` variable and the `steps`
extraction) in this test file with calls to parseWorkflowSteps(output) (update
call sites at the spots mentioned).

In `@web/src/api/endpoints/providers.ts`:
- Around line 184-198: The 401 SSE handling removes sessionStorage and attempts
to async-import the auth store but the catch only logs the error; mirror
client.ts by adding a fallback redirect in the catch so if importing
'@/stores/auth' fails you also redirect to '/login' (unless already on '/login'
or '/setup'); update the catch in the block that imports useAuthStore (the SSE
401 handling in providers.ts) to log the importErr and then check
window.location.pathname !== '/login' && !== '/setup' and set
window.location.href = '/login' as a fallback.
🪄 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: 95743b39-7cc4-4ec5-b781-4c56da6a8b75

📥 Commits

Reviewing files that changed from the base of the PR and between b8050ec and 31efd8d.

📒 Files selected for processing (4)
  • src/synthorg/engine/task_engine.py
  • tests/unit/engine/test_task_engine_observers.py
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/api/endpoints/providers.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{tsx,ts}: Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code. Use design tokens and @/lib/motion presets.
Web dashboard: use React 19, TypeScript 6.0+, Tailwind CSS 4, Zustand, shadcn/ui, Radix UI, and other specified dependencies from web/package.json.

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,js,jsx}: Always use createLogger from @/lib/logger instead of bare console.warn, console.error, or console.debug in application code
Use variable name log for logger instances (e.g., const log = createLogger('module-name'))
Use log.debug() for dev-only logging (stripped in production), log.warn() for warnings, and log.error() for errors
Pass dynamic/untrusted values as separate arguments to logger calls (not interpolated into the message string) so they go through sanitizeArg
Wrap attacker-controlled fields inside structured objects with sanitizeForLog() before embedding in logs

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,css}: Use Tailwind semantic color classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)) instead of hardcoded hex values
Use font-sans or font-mono for typography (maps to Geist tokens), never set fontFamily directly
Use density-aware spacing tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing, never hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders instead of hardcoded values
Do NOT use rgba() with hardcoded values -- use design token variables
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files and catches design system violations; fix all violations before proceeding

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx}: Do NOT hardcode Framer Motion transition durations -- use presets from @/lib/motion
CSS side-effect imports in TypeScript files should use type declarations (/// <reference types="vite/client" /> covers this); TypeScript 6 defaults noUncheckedSideEffectImports to true

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations -- Python 3.14 has PEP 649.
PEP 758 except syntax: use except A, B: (no parentheses) for exception handling.
All public functions require type hints. Use mypy strict mode.
Docstrings must use Google style and are required on public classes and functions (enforced by ruff D rules).
Create new objects instead of mutating existing ones. For non-Pydantic internal collections, use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement.
Use frozen Pydantic models for config/identity. Use separate mutable-via-copy models for runtime state that evolves.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use allow_inf_nan=False in all ConfigDict declarations.
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models.
Use @computed_field for derived values instead of storing and validating redundant fields.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Functions must be less than 50 lines and files must be less than 800 lines.
Handle errors explicitly, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files).
Line length: 88 characters (ruff).
Python: 3.14+ (PEP 649 native lazy annotations).

Files:

  • src/synthorg/engine/task_engine.py
  • tests/unit/engine/test_task_engine_observers.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic must have: from synthorg.observability import get_logger then logger = get_logger(name)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print for handler construction.
Logger variable name must always be 'logger' (not '_logger', not 'log').
Use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED, TOOL_INVOKE_START). Import directly from synthorg.observability.events..
Always use logger.info(EVENT, key=value) for structured logging. Never use 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.
DEBUG logging is used for object creation, internal flow, and entry/exit of key functions.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001.

Files:

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

⚙️ CodeRabbit configuration file

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

Files:

  • src/synthorg/engine/task_engine.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow for test categorization.
Coverage minimum: 80% (enforced in CI).
Use asyncio_mode = 'auto' for async tests -- no manual @pytest.mark.asyncio needed.
Prefer @pytest.mark.parametrize for testing similar cases.
Property-based testing: use Hypothesis (@given + @settings) with profiles configured in tests/conftest.py (ci, dev, fuzz, extreme).
When Hypothesis finds a failure, fix the underlying bug and add an explicit @example(...) decorator to permanently cover the case in CI.
For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins.
For tasks that must block indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Files:

  • tests/unit/engine/test_task_engine_observers.py

⚙️ CodeRabbit configuration file

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

Files:

  • tests/unit/engine/test_task_engine_observers.py
🧠 Learnings (31)
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Applied to files:

  • web/src/api/endpoints/providers.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)

Applied to files:

  • web/src/api/endpoints/providers.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.test.{ts,tsx} : Web dashboard: Use React Hypothesis (fast-check) for property-based testing with fc.assert + fc.property

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/__tests__/**/*.test.{ts,tsx} : Use property-based testing with fast-check in React tests (`fc.assert` + `fc.property`)

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : 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`. Existing code is being migrated incrementally.

Applied to files:

  • src/synthorg/engine/task_engine.py
  • tests/unit/engine/test_task_engine_observers.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/task_engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to **/*.py : PEP 758 except syntax: use `except A, B:` (no parentheses) for exception handling.

Applied to files:

  • src/synthorg/engine/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.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/task_engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to 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/test_task_engine_observers.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled (e.g. simulating a slow provider or stubborn coroutine), use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)` -- it is cancellation-safe

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to tests/**/*.py : 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/test_task_engine_observers.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 tests/**/*.py : Never skip, dismiss, or ignore flaky tests; fix them fully and fundamentally by mocking `time.monotonic()` and `asyncio.sleep()` for determinism or using `asyncio.Event().wait()` for indefinite blocking

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Applied to files:

  • tests/unit/engine/test_task_engine_observers.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 tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally; mock `time.monotonic()` and `asyncio.sleep()` for determinism; use `asyncio.Event().wait()` for indefinite blocking instead of `asyncio.sleep(large_number)`

Applied to files:

  • tests/unit/engine/test_task_engine_observers.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 tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : Flaky tests: NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-03-16T10:40:25.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T10:40:25.144Z
Learning: Applies to tests/**/*.py : Async testing: asyncio_mode = 'auto' — no manual pytest.mark.asyncio needed.

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to tests/**/*.py : Use asyncio_mode = 'auto' for async tests -- no manual pytest.mark.asyncio needed.

Applied to files:

  • tests/unit/engine/test_task_engine_observers.py
🔇 Additional comments (10)
web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts (3)

55-80: Strong structural assertion for conditional depends_on output.

Parsing YAML and asserting exact [{ id, branch }] values for both yes_step and no_step is the right level of strictness for this contract.


82-109: Parallel split coverage now validates the correct export path.

Great to see this test assert both fork.branches and that child steps keep plain-string depends_on without branch metadata.


138-206: Property-based invariant test is well targeted.

This gives solid regression protection for conditional-object vs sequential-string depends_on encoding across generated graphs.

web/src/api/endpoints/providers.ts (1)

172-172: LGTM — sessionStorage migration for auth token retrieval.

Correctly switches from localStorage to sessionStorage as part of the auth token storage migration objective.

src/synthorg/engine/task_engine.py (3)

758-789: LGTM - Observer dispatch loop is well-structured.

The implementation correctly:

  • Uses task_done() for both sentinel (line 774) and regular events (line 789 in finally)
  • Re-raises MemoryError/RecursionError per project conventions
  • Logs exceptions with relevant context without blocking the dispatch loop

197-246: LGTM - Two-phase drain with budget tracking is robust.

The shutdown sequence correctly:

  1. Drains processing loop within budget
  2. Sends sentinel via await put() (not put_nowait) per prior feedback
  3. Calculates remaining budget for observer drain
  4. Handles timeout and cancellation gracefully

713-725: Task is properly frozen — no TOCTOU risk with async observer dispatch.

Verification confirms that Task is a frozen Pydantic model (line 79 of src/synthorg/core/task.py: model_config = ConfigDict(frozen=True, allow_inf_nan=False)), with all collection fields as immutable tuples. The reference captured in TaskStateChanged.task cannot be mutated, so observers will see consistent state regardless of async processing timing.

tests/unit/engine/test_task_engine_observers.py (3)

18-31: LGTM - Deterministic flush helper using queue.join().

This correctly addresses the prior review feedback. Using queue.join() waits until all task_done() calls complete, ensuring events are fully processed before assertions. The timeout with AssertionError provides clear test failure diagnostics.


257-296: LGTM - Excellent test for decoupled observer dispatch.

This test correctly validates that:

  1. Mutations complete without waiting for slow observers (lines 272-293)
  2. Uses asyncio.Event().wait() for cancellation-safe indefinite blocking (per guidelines)
  3. Properly unblocks observer and flushes before teardown to avoid resource leaks

120-123: LGTM - Consistent flush pattern across observer tests.

All tests that assert on observer-received events now correctly await _flush_observers() before assertions, ensuring the background dispatcher has processed all events.

Also applies to: 182-184, 210-211, 249-255

Comment on lines +123 to +126
self._observer_queue: asyncio.Queue[TaskStateChanged | None] = asyncio.Queue(
maxsize=self._config.max_queue_size,
)
self._observer_task: asyncio.Task[None] | None = None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Observer queue shares max_queue_size with mutation queue.

Both queues use self._config.max_queue_size (default 1000). Consider whether observers warrant a separate configuration parameter — observers are best-effort and may have different throughput characteristics than the mutation pipeline. Under sustained load, 1000 may be too small and cause frequent event drops, or conversely may be unnecessarily large for observer notifications.

This is optional; the current design is reasonable for initial implementation. You could defer adding observer_queue_size to TaskEngineConfig until operational metrics indicate a need.

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

In `@src/synthorg/engine/task_engine.py` around lines 123 - 126, The observer
queue (_observer_queue) currently reuses self._config.max_queue_size; add a
separate observer_queue_size field to TaskEngineConfig (with a sensible default)
and use it when constructing _observer_queue (i.e., replace
self._config.max_queue_size with self._config.observer_queue_size in the
TaskEngine initialization), so observer throughput can be tuned independently
from the mutation queue.

- task_engine: replace get_event_loop() with get_running_loop() in stop()
- task_engine_config: add observer_queue_size field with fallback to
  max_queue_size via effective_observer_queue_size property
- providers.ts: add fallback redirect to /login in SSE 401 catch block
  (mirrors client.ts interceptor pattern)
- workflow-to-yaml tests: extract parseSteps() helper to deduplicate
  YAML parsing across 4 test cases
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/task_engine_config.py`:
- Around line 50-55: The computed property effective_observer_queue_size should
use Pydantic's `@computed_field` so it is included in serialization/schema; update
the decorator stack to have `@computed_field` above `@property` on the
effective_observer_queue_size method and add the repository's mypy ignore
comment (e.g., # type: ignore[override]) on the property to satisfy type
checking, leaving the implementation logic (checking self.observer_queue_size
and falling back to self.max_queue_size) unchanged.

In `@src/synthorg/engine/task_engine.py`:
- Around line 188-194: The shutdown path currently awaits
self._observer_queue.put(None) without bounding it by the remaining shutdown
time, so if the queue is full this can block past the deadline; change the code
in the shutdown sequence to wrap the sentinel enqueue in asyncio.wait_for using
the computed remaining budget (deadline - loop.time()) and catch
asyncio.TimeoutError to skip the put and let _drain_observer handle cancelling
the observer task; specifically modify the block that calls await
self._observer_queue.put(None) (in stop/ shutdown flow) to use
asyncio.wait_for(self._observer_queue.put(None), timeout=remaining_budget) and
handle timeout by not raising and proceeding to await
self._drain_observer(observer_budget).
🪄 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: e64f9aec-0f67-406a-ba92-e6e5958836c4

📥 Commits

Reviewing files that changed from the base of the PR and between 31efd8d and 54e6263.

📒 Files selected for processing (4)
  • src/synthorg/engine/task_engine.py
  • src/synthorg/engine/task_engine_config.py
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
  • web/src/api/endpoints/providers.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dashboard Test
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (7)
web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{tsx,ts}: Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code. Use design tokens and @/lib/motion presets.
Web dashboard: use React 19, TypeScript 6.0+, Tailwind CSS 4, Zustand, shadcn/ui, Radix UI, and other specified dependencies from web/package.json.

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,js,jsx}: Always use createLogger from @/lib/logger instead of bare console.warn, console.error, or console.debug in application code
Use variable name log for logger instances (e.g., const log = createLogger('module-name'))
Use log.debug() for dev-only logging (stripped in production), log.warn() for warnings, and log.error() for errors
Pass dynamic/untrusted values as separate arguments to logger calls (not interpolated into the message string) so they go through sanitizeArg
Wrap attacker-controlled fields inside structured objects with sanitizeForLog() before embedding in logs

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,css}: Use Tailwind semantic color classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)) instead of hardcoded hex values
Use font-sans or font-mono for typography (maps to Geist tokens), never set fontFamily directly
Use density-aware spacing tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing, never hardcode pixel values for layout spacing
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders instead of hardcoded values
Do NOT use rgba() with hardcoded values -- use design token variables
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files and catches design system violations; fix all violations before proceeding

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx}: Do NOT hardcode Framer Motion transition durations -- use presets from @/lib/motion
CSS side-effect imports in TypeScript files should use type declarations (/// <reference types="vite/client" /> covers this); TypeScript 6 defaults noUncheckedSideEffectImports to true

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: No from __future__ import annotations -- Python 3.14 has PEP 649.
PEP 758 except syntax: use except A, B: (no parentheses) for exception handling.
All public functions require type hints. Use mypy strict mode.
Docstrings must use Google style and are required on public classes and functions (enforced by ruff D rules).
Create new objects instead of mutating existing ones. For non-Pydantic internal collections, use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement.
Use frozen Pydantic models for config/identity. Use separate mutable-via-copy models for runtime state that evolves.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use allow_inf_nan=False in all ConfigDict declarations.
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models.
Use @computed_field for derived values instead of storing and validating redundant fields.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Functions must be less than 50 lines and files must be less than 800 lines.
Handle errors explicitly, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files).
Line length: 88 characters (ruff).
Python: 3.14+ (PEP 649 native lazy annotations).

Files:

  • src/synthorg/engine/task_engine_config.py
  • src/synthorg/engine/task_engine.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Every module with business logic must have: from synthorg.observability import get_logger then logger = get_logger(name)
Never use import logging / logging.getLogger() / print() in application code. Exception: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, and observability/http_handler.py may use stdlib logging and print for handler construction.
Logger variable name must always be 'logger' (not '_logger', not 'log').
Use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED, TOOL_INVOKE_START). Import directly from synthorg.observability.events..
Always use logger.info(EVENT, key=value) for structured logging. Never use 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.
DEBUG logging is used for object creation, internal flow, and entry/exit of key functions.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001.

Files:

  • src/synthorg/engine/task_engine_config.py
  • src/synthorg/engine/task_engine.py
src/**/*.py

⚙️ CodeRabbit configuration file

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

Files:

  • src/synthorg/engine/task_engine_config.py
  • src/synthorg/engine/task_engine.py
🧠 Learnings (24)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
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...
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.test.{ts,tsx} : Web dashboard: Use React Hypothesis (fast-check) for property-based testing with fc.assert + fc.property

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-04-04T17:43:42.372Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-04T17:43:42.372Z
Learning: Applies to web/src/**/*.stories.{ts,tsx} : Use `storybook/test` instead of `storybook/test` and `storybook/actions` instead of `storybook/addon-actions` (Storybook 10 import path changes)

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/__tests__/**/*.test.{ts,tsx} : Use property-based testing with fast-check in React tests (`fc.assert` + `fc.property`)

Applied to files:

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

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/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:

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

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : 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`. Existing code is being migrated incrementally.

Applied to files:

  • src/synthorg/engine/task_engine.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/task_engine.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to **/*.py : PEP 758 except syntax: use `except A, B:` (no parentheses) for exception handling.

Applied to files:

  • src/synthorg/engine/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.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/task_engine.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.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/task_engine.py
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled (e.g. simulating a slow provider or stubborn coroutine), use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)` -- it is cancellation-safe

Applied to files:

  • src/synthorg/engine/task_engine.py
📚 Learning: 2026-04-04T17:42:47.768Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T17:42:47.768Z
Learning: Applies to tests/**/*.py : For tasks that must block indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number).

Applied to files:

  • src/synthorg/engine/task_engine.py
🔇 Additional comments (5)
web/src/api/endpoints/providers.ts (2)

172-172: LGTM!

Token retrieval correctly migrated from localStorage to sessionStorage, aligning with the auth token storage changes across the frontend.


186-198: LGTM! 401 handling now fully consistent with client.ts.

The implementation correctly:

  • Clears the same three sessionStorage keys synchronously before async operations
  • Uses fire-and-forget dynamic import to avoid circular dependencies
  • Includes the fallback redirect in the catch block (addressing the previous review feedback)
  • Throws the error after cleanup so callers receive the failure

This matches the axios 401 interceptor pattern in client.ts exactly.

web/src/__tests__/pages/workflow-editor/workflow-to-yaml.test.ts (3)

10-40: Well-factored test helpers and YAML parsing utility.

makeNode, makeEdge, and parseSteps make the suite easier to extend while keeping assertions focused on exporter behavior.


42-140: Scenario coverage is strong for all dependency modes.

These tests clearly exercise sequential, conditional, parallel-split, and mixed graphs against the updated exporter contract.


142-207: Property-based invariant test is correctly implemented.

Great use of randomized coverage to enforce conditional-vs-sequential depends_on invariants beyond fixed fixtures.
As per coding guidelines, web/src/__tests__/**/*.test.{ts,tsx}: “Use property-based testing with fast-check (fc.assert + fc.property).”

Comment on lines +50 to +55
@property
def effective_observer_queue_size(self) -> int:
"""Resolved observer queue size (falls back to max_queue_size)."""
if self.observer_queue_size is not None:
return self.observer_queue_size
return self.max_queue_size
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

🧩 Analysis chain

🏁 Script executed:

# Read the file to examine the complete context around the flagged property
head -70 src/synthorg/engine/task_engine_config.py | tail -40

Repository: Aureliolo/synthorg

Length of output: 1432


🏁 Script executed:

# Also check the full file structure and imports
head -30 src/synthorg/engine/task_engine_config.py

Repository: Aureliolo/synthorg

Length of output: 1149


🏁 Script executed:

# Check if computed_field is already imported elsewhere in the file
rg "computed_field|@property" src/synthorg/engine/task_engine_config.py -n

Repository: Aureliolo/synthorg

Length of output: 78


🏁 Script executed:

# Verify the Pydantic model class definition and ConfigDict
ast-grep --pattern $'class $NAME(BaseModel) {
  $$$
}'

Repository: Aureliolo/synthorg

Length of output: 44


🏁 Script executed:

# Check if computed_field is used elsewhere in the codebase to verify the pattern
rg "@computed_field" src/ --type py -A 2 | head -40

Repository: Aureliolo/synthorg

Length of output: 2377


🏁 Script executed:

# Verify the file doesn't already import computed_field
grep "computed_field" src/synthorg/engine/task_engine_config.py

Repository: Aureliolo/synthorg

Length of output: 44


🏁 Script executed:

# Check Pydantic version in the project
rg "pydantic" pyproject.toml requirements.txt setup.py 2>/dev/null | head -5

Repository: Aureliolo/synthorg

Length of output: 310


Switch to @computed_field with the codebase pattern.

The property effective_observer_queue_size is a derived value and should use Pydantic's @computed_field to be included in serialization and schema. Follow the established repo pattern: stack @computed_field with @property and add the mypy type-ignore comment.

♻️ Proposed fix
-from pydantic import BaseModel, ConfigDict, Field
+from pydantic import BaseModel, ConfigDict, Field, computed_field
...
+    `@computed_field`  # type: ignore[prop-decorator]
+    `@property`
     def effective_observer_queue_size(self) -> int:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@property
def effective_observer_queue_size(self) -> int:
"""Resolved observer queue size (falls back to max_queue_size)."""
if self.observer_queue_size is not None:
return self.observer_queue_size
return self.max_queue_size
`@computed_field` # type: ignore[prop-decorator]
`@property`
def effective_observer_queue_size(self) -> int:
"""Resolved observer queue size (falls back to max_queue_size)."""
if self.observer_queue_size is not None:
return self.observer_queue_size
return self.max_queue_size
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/engine/task_engine_config.py` around lines 50 - 55, The computed
property effective_observer_queue_size should use Pydantic's `@computed_field` so
it is included in serialization/schema; update the decorator stack to have
`@computed_field` above `@property` on the effective_observer_queue_size method and
add the repository's mypy ignore comment (e.g., # type: ignore[override]) on the
property to satisfy type checking, leaving the implementation logic (checking
self.observer_queue_size and falling back to self.max_queue_size) unchanged.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 4, 2026 18:42 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit 2cac2d3 into main Apr 4, 2026
32 of 34 checks passed
@Aureliolo Aureliolo deleted the chore/config-fix-deferred-improvements branch April 4, 2026 19:12
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 4, 2026 19:12 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Apr 6, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.6.2](v0.6.1...v0.6.2)
(2026-04-06)


### Features

* add issue analyzer script for priority/scope management
([#1084](#1084))
([1ccba27](1ccba27))
* config fixes and deferred improvements from PR
[#1058](#1058) review
([#1067](#1067))
([2cac2d3](2cac2d3)),
closes [#1061](#1061)
[#1060](#1060)
* cumulative risk-unit action budgets
([#806](#806)) and automated
reporting ([#245](#245))
([#1063](#1063))
([4689816](4689816))
* fine-tuning pipeline + CompositeBackend + workflow lifecycle
([#1065](#1065))
([85b05bc](85b05bc)),
closes [#1001](#1001)
[#850](#850)
[#1058](#1058)
* memory consolidation upgrades (LLM Merge, Search-and-Ask, diversity
penalty, distillation capture)
([#1071](#1071))
([174e2be](174e2be)),
closes [#704](#704)
* migrate web dashboard from Radix UI to Base UI, activate CSP nonce,
rebuild org chart page, and fix agent routing
([#1083](#1083))
([ebc6921](ebc6921))
* v0.7.0 engine foundations -- structured failure diagnosis + auditable
decisions ([#1072](#1072))
([d341d37](d341d37))
* workflow templates and versioning with diff and rollback
([#1069](#1069))
([7af94de](7af94de)),
closes [#1006](#1006)
[#1008](#1008)


### Documentation

* unify REST API docs under /docs/openapi/ and patch sitemap
([#1073](#1073))
([af19382](af19382))


### Maintenance

* bump hypothesis from 6.151.10 to 6.151.11 in the all group
([#1086](#1086))
([3176318](3176318))
* bump nginxinc/nginx-unprivileged from `f99cc61` to `601c823` in
/docker/web in the all group
([#1085](#1085))
([5eb99ac](5eb99ac))
* bump the all group in /web with 3 updates
([#1087](#1087))
([8deae44](8deae44))

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

---------

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add PerformanceConfig to RootConfig for YAML-driven quality scoring configuration chore: deferred improvements from PR #1058 review

2 participants