Skip to content

fix: improve name generation step UX and fix sentinel expansion bug#739

Merged
Aureliolo merged 4 commits intomainfrom
feat/improve-name-generation-step
Mar 22, 2026
Merged

fix: improve name generation step UX and fix sentinel expansion bug#739
Aureliolo merged 4 commits intomainfrom
feat/improve-name-generation-step

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Fix: GET /setup/name-locales was expanding the __all__ sentinel into 57 individual locale codes via resolve_locales(), causing the "All (worldwide)" toggle to always show as OFF on fresh instances. The endpoint now returns the raw sentinel; resolution only happens in the name-generation path.
  • UX: Hide region groups when "All" is toggled ON (no need to show 12 regions when worldwide is selected).
  • UX: Switch region layout from single-column stack to responsive 2-3 column grid with wider container.
  • UX: Default template selection to "Startup" instead of "Start Blank" for a better out-of-box experience.
  • Docs: Add the Names step to the user guide setup wizard description (was missing).

Test plan

  • All 10333 tests pass (94% coverage)
  • mypy strict -- no issues
  • ruff lint + format -- clean
  • Vue type-check, lint, and 773 Vitest tests pass
  • New unit tests for _read_name_locales(resolve=False) with sentinel and individual codes
  • New endpoint test for GET /name-locales with explicitly stored __all__
  • Pre-reviewed by 13 agents, 7 findings addressed

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 22, 2026

Dependency Review

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

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b10f8d95-eaa5-497e-813f-34696b3783f1

📥 Commits

Reviewing files that changed from the base of the PR and between f28611f and ba1c5bd.

📒 Files selected for processing (15)
  • docs/design/organization.md
  • docs/user_guide.md
  • src/synthorg/api/controllers/setup.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/observability/events/setup.py
  • src/synthorg/observability/events/template.py
  • src/synthorg/templates/builtins/agency.yaml
  • src/synthorg/templates/builtins/dev_shop.yaml
  • src/synthorg/templates/builtins/full_company.yaml
  • src/synthorg/templates/builtins/product_team.yaml
  • src/synthorg/templates/builtins/research_lab.yaml
  • src/synthorg/templates/builtins/solo_founder.yaml
  • src/synthorg/templates/builtins/startup.yaml
  • src/synthorg/templates/locales.py
  • tests/unit/api/controllers/test_setup.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). (5)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

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

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/synthorg/observability/events/template.py
  • src/synthorg/templates/locales.py
  • src/synthorg/observability/events/setup.py
  • src/synthorg/api/controllers/setup_models.py
  • tests/unit/api/controllers/test_setup.py
  • src/synthorg/api/controllers/setup.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Create new objects instead of mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Functions should be less than 50 lines; files should be less than 800 lines.
Every module with business logic MUST import logger: from synthorg.observability import get_logger then logger = get_logger(__name__).
Never use import logging, logging.getLogger(), or print() in application code.
Variable name for logger is always logger (not _logger, not log).
Always use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Use structured logging: always logger.info(EVENT, key=value)—never logger.info("msg %s", val).
All error paths must log at WARNING or ERROR with context before raising.
All state transitions must log at INFO.
DEBUG logging is for object creation, internal flow, and entry/exit of key functions.
Pure data models, enums, and re-exports do NOT need logging.
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases.

Files:

  • src/synthorg/observability/events/template.py
  • src/synthorg/templates/locales.py
  • src/synthorg/observability/events/setup.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup.py
{**/*.py,cli/**/*.go}

📄 CodeRabbit inference engine (CLAUDE.md)

Line length is 88 characters (enforced by ruff).

Files:

  • src/synthorg/observability/events/template.py
  • src/synthorg/templates/locales.py
  • src/synthorg/observability/events/setup.py
  • src/synthorg/api/controllers/setup_models.py
  • tests/unit/api/controllers/test_setup.py
  • src/synthorg/api/controllers/setup.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use test markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow.
Test timeout is 30 seconds per test globally in pyproject.toml—do not add per-file pytest.mark.timeout(30) markers; only non-default overrides like timeout(60) are allowed.
Prefer @pytest.mark.parametrize for testing similar cases.
In tests, use test-provider, test-small-001, etc. instead of vendor-specific names.
Use Hypothesis for property-based testing in Python with @given and @settings decorators. Use profiles: ci (50 examples, default) and dev (1000 examples).
For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins.
For tasks that must block indefinitely until cancelled (e.g., simulating slow providers), use asyncio.Event().wait() instead of asyncio.sleep(large_number)—it is cancellation-safe and carries no timing assumptions.

Files:

  • tests/unit/api/controllers/test_setup.py
🧠 Learnings (14)
📚 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 : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings

Applied to files:

  • src/synthorg/templates/locales.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/setup.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/setup.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/setup.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/setup.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/setup.py
📚 Learning: 2026-03-22T16:44:15.487Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-22T16:44:15.487Z
Learning: Applies to src/synthorg/**/*.py : Always use constants from domain-specific modules under `synthorg.observability.events` for event names (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/synthorg/templates/builtins/agency.yaml
📚 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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers

Applied to files:

  • src/synthorg/api/controllers/setup.py
🧬 Code graph analysis (2)
src/synthorg/api/controllers/setup_models.py (1)
web/src/api/types.ts (1)
  • UpdateAgentNameRequest (904-906)
src/synthorg/api/controllers/setup.py (6)
src/synthorg/api/controllers/setup_models.py (2)
  • UpdateAgentNameRequest (202-211)
  • SetupAgentSummary (70-93)
web/src/api/types.ts (2)
  • UpdateAgentNameRequest (904-906)
  • SetupAgentSummary (871-880)
src/synthorg/api/errors.py (1)
  • NotFoundError (167-175)
src/synthorg/api/guards.py (1)
  • require_write_access (64-91)
src/synthorg/api/controllers/setup_agents.py (2)
  • get_existing_agents (244-300)
  • agent_dict_to_summary (407-441)
src/synthorg/templates/locales.py (1)
  • resolve_locales (183-212)
🪛 YAMLlint (1.38.0)
src/synthorg/templates/builtins/full_company.yaml

[warning] 141-141: comment not indented like content

(comments-indentation)


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

(syntax)

🔇 Additional comments (24)
docs/design/organization.md (1)

243-243: LGTM!

The example locale list now uses Portuguese instead of Japanese, which correctly reflects the Latin-script locales available in LOCALE_REGIONS (Japanese produces non-Latin names and isn't in the supported set).

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

93-98: LGTM!

New event constants follow the established setup.<entity>.<action> naming convention and are properly typed with Final[str].

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

41-43: LGTM!

New event constant follows the established naming convention and is properly categorized under the locale resolution section.

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

37-47: LGTM!

Clearing agent names to empty strings enables dynamic name generation during setup. This aligns with the new PUT /agents/{agent_index}/name and POST /agents/{agent_index}/randomize-name endpoints.

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

38-70: LGTM!

Agent names consistently cleared across all five roles, enabling dynamic name generation during the setup flow.

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

44-106: LGTM!

All ten agent names consistently cleared for dynamic generation, matching the pattern across other built-in templates.

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

203-211: LGTM!

Good use of the domain-specific event constant with structured logging kwargs. The lazy import pattern avoids circular dependencies and is appropriately marked with noqa: PLC0415.

src/synthorg/api/controllers/setup_models.py (1)

202-212: LGTM!

The model follows established patterns: frozen=True with extra="forbid" for immutable request validation, NotBlankStr for name validation, and max_length=200 consistent with other name fields in this module. The Google-style docstring is complete.

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

38-89: LGTM!

Blanking agent name fields aligns with the new setup flow where names are assigned via PUT /agents/{index}/name or POST /agents/{index}/randomize-name endpoints.

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

44-118: LGTM!

Consistent with the template-wide change to blank agent names, enabling name assignment during the setup wizard.

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

64-172: LGTM!

Agent names correctly blanked across static and dynamic (Jinja-templated) agent definitions. The static analysis warnings about YAML syntax on lines 141-142 are false positives—this is a Jinja2 template file where {% for ... %} is valid templating syntax.

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

38-82: LGTM!

Consistent with the template-wide change to support name assignment during setup.

docs/user_guide.md (1)

71-75: LGTM!

Documentation accurately reflects the new seven-step setup wizard flow, including the new "Choose name locales" step and the default Startup template selection. The past review comment about mentioning the Startup default has been addressed.

tests/unit/api/controllers/test_setup.py (5)

782-846: LGTM!

TestUpdateAgentName provides good coverage: successful rename with persistence verification, 404 for out-of-range index, and 400 for blank/whitespace name validation. Tests follow project conventions with @pytest.mark.unit and use generic provider names.


849-890: LGTM!

TestRandomizeAgentName tests appropriately verify that a non-empty name is generated and persisted, plus the 404 error for invalid indices.


919-934: LGTM!

Updated test correctly reflects the new behavior where GET /name-locales returns the raw ["__all__"] sentinel instead of resolving to concrete locale codes. The docstring clearly explains the rationale.


956-979: LGTM!

New test validates the round-trip behavior: storing ["__all__"] explicitly returns the sentinel unchanged, confirming the fix for the sentinel expansion bug.


1270-1316: LGTM!

Tests for _read_name_locales(resolve=False) properly verify both that the __all__ sentinel passes through unchanged and that invalid locale codes are not filtered when resolution is disabled.

src/synthorg/api/controllers/setup.py (6)

95-110: LGTM!

Good refactor extracting _validate_agent_index as a reusable helper. It centralizes the bounds checking, logging, and error message formatting that was previously duplicated.


451-507: LGTM!

The update_agent_name endpoint correctly:

  • Guards against post-setup modifications
  • Uses the lock for read-modify-write atomicity
  • Updates the agent immutably
  • Logs the state transition at INFO level

509-572: LGTM!

The randomize_agent_name endpoint correctly reads locale preferences before the critical section (appropriate since locales are independent of agent state), then performs the atomic update under the lock.


624-629: Core bug fix confirmed.

Calling _read_name_locales(settings_svc, resolve=False) ensures the endpoint returns the raw ["__all__"] sentinel to the frontend, allowing the "All (worldwide)" toggle to display correctly on fresh instances.


1008-1031: LGTM!

Clean extraction of JSON parsing logic into _parse_locale_json with appropriate warning logs on failure cases.


1034-1081: LGTM!

The resolve parameter cleanly separates the two use cases:

  • resolve=True (default): For name generation, expands __all__ and filters invalid codes
  • resolve=False: For GET endpoint, returns raw stored value including sentinel

The docstring clearly documents both modes.


Walkthrough

This change adds a new "Choose name locales" step to the setup wizard and shifts subsequent steps, updating docs to describe a seven-step flow. Backend setup controllers gained agent-name endpoints (update and randomize), name-locales parsing with an option to return raw stored values (including the sentinel __all__), and validation helpers. Several built-in templates had agent name cleared. Frontend changes hide region groups when the all-worldwide sentinel is selected, widen the name-locale layout, and default the company template to "startup".

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and concisely describes the primary changes: fixing a sentinel expansion bug and improving name generation step UX.
Description check ✅ Passed The description is well-structured and clearly relates to the changeset, covering the bug fix, UX improvements, documentation updates, and test results.
Docstring Coverage ✅ Passed Docstring coverage is 91.30% 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.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 22, 2026 15:49 — with GitHub Actions Inactive
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

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

This pull request addresses a bug in locale handling and introduces several UX improvements to the setup wizard. It also updates the documentation to reflect the changes in the setup process. The primary goal is to provide a smoother and more intuitive experience for new users during the initial setup.

Highlights

  • Locale Handling: Fixed a bug where the __all__ sentinel for locales was being expanded prematurely, causing issues with the 'All (worldwide)' toggle in the setup wizard.
  • UX Improvements: Improved the user experience by hiding region groups when 'All' locales are selected, switching to a responsive grid layout for region selection, and defaulting the template selection to 'Startup'.
  • Documentation: Updated the user guide to include the 'Names' step in the setup wizard description.
Using Gemini Code Assist

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

Invoking Gemini

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

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

Customization

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

Limitations & Feedback

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

Footnotes

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

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 22, 2026

Codecov Report

❌ Patch coverage is 95.31250% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.27%. Comparing base (9ec2163) to head (ba1c5bd).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/api/controllers/setup.py 94.73% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #739      +/-   ##
==========================================
+ Coverage   92.26%   92.27%   +0.01%     
==========================================
  Files         573      573              
  Lines       29608    29655      +47     
  Branches     2868     2870       +2     
==========================================
+ Hits        27318    27365      +47     
  Misses       1810     1810              
  Partials      480      480              

☔ 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

@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

Caution

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

⚠️ Outside diff range comments (1)
src/synthorg/api/controllers/setup.py (1)

873-933: ⚠️ Potential issue | 🟠 Major

Keep the raw resolve=False path sanitized.

This helper now returns any stored list verbatim when resolve=False. A legacy or manually edited company/name_locales value can therefore leak duplicate, unknown, or non-string entries through GET /setup/name-locales, which the selector cannot render cleanly and PUT /setup/name-locales will reject on the next save. Preserve __all__, but still sanitize explicit codes before returning them. Pulling that raw-list cleanup into a small helper would also bring _read_name_locales() back under the 50-line limit.

Suggested fix
     if resolve:
         from synthorg.templates.locales import resolve_locales  # noqa: PLC0415
 
         parsed = resolve_locales(parsed)
+    else:
+        from synthorg.templates.locales import (  # noqa: PLC0415
+            ALL_LOCALES_SENTINEL,
+            VALID_LOCALE_CODES,
+        )
+
+        if ALL_LOCALES_SENTINEL in parsed:
+            parsed = [ALL_LOCALES_SENTINEL]
+        else:
+            sanitized = [
+                loc
+                for loc in parsed
+                if isinstance(loc, str) and loc in VALID_LOCALE_CODES
+            ]
+            parsed = list(dict.fromkeys(sanitized))
     return parsed or None
As per coding guidelines "Validate input at system boundaries (user input, external APIs, config files)" and "Keep functions under 50 lines and files under 800 lines".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/api/controllers/setup.py` around lines 873 - 933, The
resolve=False branch of _read_name_locales currently returns the stored list
verbatim and must instead sanitize it: add a small helper (e.g.
_sanitize_name_locales) and call it when resolve is False to (1) allow the
special sentinel "__all__" unchanged, (2) filter the list to only strings, strip
whitespace, drop empty strings, remove duplicates while preserving order, and
(3) drop unknown/invalid locale codes (but do not expand them) — keep
resolve=True behavior using resolve_locales unchanged; refactor so
_read_name_locales delegates raw-list cleanup to that helper to stay under 50
lines.
🤖 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/user_guide.md`:
- Line 72: Update the "Create your company" step to note that the wizard now
opens with the "Startup" template preselected by default and that agents will be
auto-created when a template is selected; instruct users to choose "Start Blank"
if they want an empty organization with no auto-created agents, and adjust the
sentence that currently implies templates are only applied after an explicit
choice to reflect this default selection behavior.

In `@web/src/components/setup/SetupCompany.vue`:
- Line 19: selectedTemplate is prefilled with 'startup' before fetchTemplates()
completes, causing accidental submission of a startup template; change the flow
so selectedTemplate starts as null (remove the 'startup' default), set
selectedTemplate only after fetchTemplates() resolves and confirms the template
exists (e.g., pick 'startup' only if it's present in the fetched templates), and
update the form submit logic to guard against null (disable or prevent submit
when selectedTemplate is null) so failures or slow loads can't auto-select
startup; reference the selectedTemplate ref and the fetchTemplates() resolution
to implement this.

---

Outside diff comments:
In `@src/synthorg/api/controllers/setup.py`:
- Around line 873-933: The resolve=False branch of _read_name_locales currently
returns the stored list verbatim and must instead sanitize it: add a small
helper (e.g. _sanitize_name_locales) and call it when resolve is False to (1)
allow the special sentinel "__all__" unchanged, (2) filter the list to only
strings, strip whitespace, drop empty strings, remove duplicates while
preserving order, and (3) drop unknown/invalid locale codes (but do not expand
them) — keep resolve=True behavior using resolve_locales unchanged; refactor so
_read_name_locales delegates raw-list cleanup to that helper to stay under 50
lines.
🪄 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: 2e1124ff-ac4b-4017-90ca-cad1695ffbc6

📥 Commits

Reviewing files that changed from the base of the PR and between 8d90f5c and f28611f.

📒 Files selected for processing (6)
  • docs/user_guide.md
  • src/synthorg/api/controllers/setup.py
  • tests/unit/api/controllers/test_setup.py
  • web/src/components/common/NameLocaleSelector.vue
  • web/src/components/setup/SetupCompany.vue
  • web/src/components/setup/SetupNameLocale.vue
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{py,ts,tsx,js,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain line length of 88 characters (enforced by ruff)

Files:

  • web/src/components/setup/SetupNameLocale.vue
  • web/src/components/setup/SetupCompany.vue
  • web/src/components/common/NameLocaleSelector.vue
  • src/synthorg/api/controllers/setup.py
  • tests/unit/api/controllers/test_setup.py
**/*.{py,ts,tsx,js,vue,go}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,js,vue,go}: Keep functions under 50 lines and files under 800 lines
Handle errors explicitly and never silently swallow exceptions
Validate input at system boundaries (user input, external APIs, config files)

Files:

  • web/src/components/setup/SetupNameLocale.vue
  • web/src/components/setup/SetupCompany.vue
  • web/src/components/common/NameLocaleSelector.vue
  • src/synthorg/api/controllers/setup.py
  • tests/unit/api/controllers/test_setup.py
web/src/components/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Vue 3 components must follow PrimeVue + Tailwind CSS patterns

Files:

  • web/src/components/setup/SetupNameLocale.vue
  • web/src/components/setup/SetupCompany.vue
  • web/src/components/common/NameLocaleSelector.vue
web/src/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint and vue-tsc for linting and type checking in the Vue dashboard

Files:

  • web/src/components/setup/SetupNameLocale.vue
  • web/src/components/setup/SetupCompany.vue
  • web/src/components/common/NameLocaleSelector.vue
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Build docs with Zensical; config in mkdocs.yml; docs are in Markdown in docs/ directory

Files:

  • docs/user_guide.md
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use Python 3.14+ with PEP 649 native lazy annotations; no from __future__ import annotations
Use PEP 758 except syntax: except A, B: without parentheses (enforced by ruff on Python 3.14)
All public functions must have type hints; mypy strict mode is enforced
Use Google-style docstrings on all public classes and functions (enforced by ruff D rules)
Implement immutability by creating new objects rather than mutating existing ones; use copy.deepcopy() at construction and MappingProxyType for read-only enforcement on internal collections
For dict/list fields in frozen Pydantic models, use copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use Pydantic v2 conventions: BaseModel, model_validator, computed_field, ConfigDict; use @computed_field for derived values instead of storing redundant fields
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models instead of manual whitespace validators, including optional and tuple variants
Use asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls) for structured concurrency

Files:

  • src/synthorg/api/controllers/setup.py
  • tests/unit/api/controllers/test_setup.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; always use the centralized logger
Always use logger variable name logger (not _logger, not log)
Use event name constants from synthorg.observability.events.<domain> instead of string literals (e.g., API_REQUEST_STARTED from events.api)
Use structured logging with kwargs: logger.info(EVENT, key=value) instead of string formatting like logger.info("msg %s", val)
Log all error paths at WARNING or ERROR level with context before raising
Log all state transitions at INFO level
Use DEBUG level for object creation, internal flow, and entry/exit of key functions
Pure data models, enums, and re-exports do NOT require logging
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples; use generic names like example-provider, example-large-001, test-provider
Never use vendor-agnostic names in vendor-specific contexts like LiteLLM imports or third-party module names; vendor names allowed only in: Operations design page, .claude/ files, third-party import paths
Use NotBlankStr for all identifier/name fields in Pydantic models to prevent empty/whitespace-only values
Separating config (frozen Pydantic models) from runtime state (mutable models using model_copy); never mix static config fields with mutable runtime fields in one model

Files:

  • src/synthorg/api/controllers/setup.py
src/synthorg/api/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use RFC 9457 error format for REST API responses

Files:

  • src/synthorg/api/controllers/setup.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow to categorize tests
Maintain 80% minimum code coverage (enforced in CI)
Prefer @pytest.mark.parametrize for testing similar cases
Use Python Hypothesis for property-based testing with @given and @settings decorators; use HYPOTHESIS_PROFILE env var to control examples (ci: 50, dev: 1000)
Never skip or dismiss flaky tests; fix them fundamentally by mocking time.monotonic() and asyncio.sleep() for timing-sensitive tests and using asyncio.Event().wait() instead of asyncio.sleep(large_number) for indefinite blocking

Files:

  • tests/unit/api/controllers/test_setup.py
🧠 Learnings (1)
📚 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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers

Applied to files:

  • src/synthorg/api/controllers/setup.py
🧬 Code graph analysis (2)
src/synthorg/api/controllers/setup.py (2)
src/synthorg/settings/service.py (1)
  • SettingsService (152-637)
src/synthorg/templates/locales.py (1)
  • resolve_locales (183-208)
tests/unit/api/controllers/test_setup.py (1)
src/synthorg/api/controllers/setup.py (1)
  • _read_name_locales (873-933)
🔇 Additional comments (1)
web/src/components/common/NameLocaleSelector.vue (1)

150-150: Good use of v-show here.

This keeps the "All" mode uncluttered without remounting the region grid, so toggling back preserves the existing region state.

const companyName = ref('')
const companyDescription = ref('')
const selectedTemplate = ref<string | null>(null)
const selectedTemplate = ref<string | null>('startup')
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

Defer the startup default until templates finish loading.

Line 19 preselects startup before fetchTemplates() resolves. The form can now submit template_name: "startup" while no startup card is rendered yet, or after template loading fails, so users can get auto-created startup agents without ever seeing that selection.

Suggested fix
-const selectedTemplate = ref<string | null>('startup')
+const selectedTemplate = ref<string | null>(null)
+const templatesLoading = ref(true)
 onMounted(async () => {
-  await setup.fetchTemplates()
-  // fetchTemplates catches errors internally; surface to component.
-  if (setup.error) {
-    error.value = setup.error
-  }
+  try {
+    await setup.fetchTemplates()
+    if (!setup.error && setup.templates.some((tmpl) => tmpl.name === 'startup')) {
+      selectedTemplate.value = 'startup'
+    }
+    // fetchTemplates catches errors internally; surface to component.
+    if (setup.error) {
+      error.value = setup.error
+    }
+  } finally {
+    templatesLoading.value = false
+  }
 })
-            :disabled="!isValid"
+            :disabled="!isValid || templatesLoading"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/setup/SetupCompany.vue` at line 19, selectedTemplate is
prefilled with 'startup' before fetchTemplates() completes, causing accidental
submission of a startup template; change the flow so selectedTemplate starts as
null (remove the 'startup' default), set selectedTemplate only after
fetchTemplates() resolves and confirms the template exists (e.g., pick 'startup'
only if it's present in the fetched templates), and update the form submit logic
to guard against null (disable or prevent submit when selectedTemplate is null)
so failures or slow loads can't auto-select startup; reference the
selectedTemplate ref and the fetchTemplates() resolution to implement this.

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 several improvements to the setup process. It fixes a bug where the __all__ sentinel for name locales was being expanded prematurely, which is a good correction. The UX for name locale selection is enhanced by hiding region groups when 'All' is selected and using a more responsive grid layout. The default template is also now 'Startup' for a better out-of-the-box experience. The documentation has been updated accordingly.

The backend and frontend changes look solid. However, I've found a couple of issues in the tests where shared state is modified without proper cleanup, which could lead to test flakiness. I've added comments with suggestions to address this.

Comment on lines +837 to +840
resp = test_client.get("/api/v1/setup/name-locales")
assert resp.status_code == 200
data = resp.json()["data"]
assert data["locales"] == ["en_US", "fr_FR"]
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 test modifies the shared repo._store but doesn't clean up after itself because the try...finally block was removed. This can lead to test flakiness, as the modified state can affect other tests. Please restore the try...finally block to ensure the store is cleaned up, even if assertions fail.

        try:
            resp = test_client.get("/api/v1/setup/name-locales")
            assert resp.status_code == 200
            data = resp.json()["data"]
            assert data["locales"] == ["en_US", "fr_FR"]
        finally:
            repo._store.pop(("company", "name_locales"), None)

Comment on lines +851 to +861
app_state = test_client.app.state.app_state
repo = app_state.persistence._settings_repo
now = datetime.now(UTC).isoformat()
repo._store[("company", "name_locales")] = (
json.dumps(["__all__"]),
now,
)
resp = test_client.get("/api/v1/setup/name-locales")
assert resp.status_code == 200
data = resp.json()["data"]
assert data["locales"] == ["__all__"]
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

Similar to the previous test, this new test test_returns_all_sentinel_when_explicitly_stored modifies the shared repo._store but doesn't clean it up. This can cause state to leak between tests and lead to flakiness. Please wrap the test logic in a try...finally block to ensure cleanup. Other new tests in this file already follow this pattern.

        app_state = test_client.app.state.app_state
        repo = app_state.persistence._settings_repo
        now = datetime.now(UTC).isoformat()
        repo._store[("company", "name_locales")] = (
            json.dumps(["__all__"]),
            now,
        )
        try:
            resp = test_client.get("/api/v1/setup/name-locales")
            assert resp.status_code == 200
            data = resp.json()["data"]
            assert data["locales"] == ["__all__"]
        finally:
            repo._store.pop(("company", "name_locales"), None)

Aureliolo and others added 4 commits March 22, 2026 17:46
The GET /setup/name-locales endpoint was resolving the __all__ sentinel
into 57 individual locale codes via resolve_locales(), even on fresh
instances. The frontend checks for exactly ["__all__"] to show the
toggle as ON, so it always appeared OFF.

Add a resolve parameter to _read_name_locales() -- the GET endpoint
uses resolve=False to return the raw sentinel, while the name
generation path keeps resolve=True for expanded codes.

Also hide region groups when All is toggled ON and switch to a
responsive multi-column grid layout for the region selector.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move resolve_locales import inside conditional branch (only imported
  when resolve=True)
- Improve docstring precision for resolve=False empty-list behavior
- Add unit tests for _read_name_locales(resolve=False) with sentinel
  and individual codes
- Add endpoint test for GET /name-locales with explicitly stored __all__
- Remove unnecessary try/finally cleanup in test (function-scoped fixture)
- Add Names step to user guide setup wizard description

Pre-reviewed by 13 agents, 7 findings addressed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Most users want a working template out of the box. Startup (3 agents)
is the smallest practical team and the best default for getting started.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add PUT /setup/agents/{index}/name endpoint for renaming agents
- Add POST /setup/agents/{index}/randomize-name endpoint using Faker
  with saved locale preferences
- Update SetupReviewOrg.vue with inline editable name fields and
  randomize (dice) button per agent
- Clear predefined names from all 7 built-in templates so agents
  get random Faker-generated names (template schema still supports
  predefined names for custom templates)
- Fix GET /setup/name-locales sentinel expansion bug (from prior
  commits, review findings addressed here)
- Add logging to _check_has_name_locales silent catch
- Extract _parse_locale_json and _validate_agent_index helpers
- Replace raw string event in locales.py with constant
- Defer startup template default until fetchTemplates resolves
- Restore try/finally cleanup in tests
- Fix docs: Japanese locale example, startup default mention,
  design spec locale example
- Address 10 findings from CodeRabbit, Gemini, and local agents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo force-pushed the feat/improve-name-generation-step branch from f28611f to ba1c5bd Compare March 22, 2026 16:49
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 22, 2026 16:50 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit f03fd05 into main Mar 22, 2026
31 checks passed
@Aureliolo Aureliolo deleted the feat/improve-name-generation-step branch March 22, 2026 16:55
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 22, 2026 16:55 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 22, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.4.7](v0.4.6...v0.4.7)
(2026-03-22)


### Features

* add system user for CLI-to-backend authentication
([#710](#710))
([dc6bd3f](dc6bd3f))
* dev channel builds with incremental pre-releases between stable
releases ([#715](#715))
([0e8a714](0e8a714))
* replace hardcoded name pools with Faker multi-locale name generation
([#714](#714))
([5edc6ec](5edc6ec))


### Bug Fixes

* dev-release tag creation, dependabot coverage, go -C cli convention
([#730](#730))
([7634843](7634843))
* improve name generation step UX and fix sentinel expansion bug
([#739](#739))
([f03fd05](f03fd05))
* settings page UX polish -- toggle bug, source badges, form
improvements ([#712](#712))
([d16a0ac](d16a0ac))
* switch dev tags to semver and use same release pipeline as stable
([#729](#729))
([4df6b9b](4df6b9b)),
closes [#713](#713)
* unify CLI image discovery and standardize Go tooling
([#738](#738))
([712a785](712a785))
* use PAT in dev-release workflow to trigger downstream pipelines
([#716](#716))
([d767aa3](d767aa3))


### CI/CD

* bump astral-sh/setup-uv from 7.4.0 to 7.6.0 in
/.github/actions/setup-python-uv in the minor-and-patch group
([#731](#731))
([7887257](7887257))
* bump the minor-and-patch group with 3 updates
([#735](#735))
([7cd253a](7cd253a))
* bump wrangler from 4.75.0 to 4.76.0 in /.github in the minor-and-patch
group ([#732](#732))
([a6cafc7](a6cafc7))
* clean up all dev releases and tags on stable release
([#737](#737))
([8d90f5c](8d90f5c))


### Maintenance

* bump the minor-and-patch group across 2 directories with 2 updates
([#733](#733))
([2b60069](2b60069))
* bump the minor-and-patch group with 3 updates
([#734](#734))
([859bc25](859bc25))
* fix dependabot labels and add scope tags
([#736](#736))
([677eb15](677eb15))
* remove redundant pytest.mark.timeout(30) markers
([#740](#740))
([9ec2163](9ec2163))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant