Skip to content

perf: run only affected modules in pre-push hooks#992

Merged
Aureliolo merged 4 commits intomainfrom
feat/improve-local-test-speed
Apr 2, 2026
Merged

perf: run only affected modules in pre-push hooks#992
Aureliolo merged 4 commits intomainfrom
feat/improve-local-test-speed

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

Local git push takes 3-4 minutes when Python files are touched because the pre-push hook runs full mypy (src/ tests/) and the full pytest unit suite (~5000+ tests) every time -- even for a one-file change. CI runs the same checks anyway.

This PR replaces the full-suite pre-push hooks with git-diff-based module mappers that only check changed modules, cutting typical push time to ~20-30 seconds.

Changes

  • scripts/run_affected_tests.py (new): Maps src/synthorg/<module>/ changes to tests/unit/<module>/ via git diff against origin/main and runs only affected tests
  • scripts/run_affected_mypy.py (new): Runs mypy on changed module dirs only (both src and corresponding test dirs)
  • .pre-commit-config.yaml: Updated mypy and pytest-unit pre-push hooks to use the new scripts
  • pyproject.toml: Added S603/S607 ruff ignores for scripts/ (subprocess calls expected in hook scripts)

Blast radius rules

Changes to foundational modules trigger a full run (these are imported everywhere):

  • core/, config/, observability/
  • Any conftest.py
  • Top-level src/synthorg/__init__.py or constants.py

Expected performance

Scenario Before After
Typical 1-2 module change ~3-4 min ~20-30s
Foundational module change ~3-4 min ~2-2.5 min (full, same)
No Python files changed ~3-4 min Instant (skip)

Test plan

  • Verified both scripts with 7 simulated input scenarios (normal modules, blast radius, conftest, top-level, no-test module, test-only, mixed)
  • Verified real targeted pytest run (tests/unit/budget -- 731 tests in 30s)
  • Verified real targeted mypy run (src/synthorg/budget tests/unit/budget -- clean)
  • CI continues running the full suite as the correctness backstop

Replace full mypy + full pytest unit suite in pre-push hooks with
git-diff-based module mappers that only check changed modules.

New scripts:
- scripts/run_affected_tests.py: maps src/synthorg/<module>/ changes
  to tests/unit/<module>/ and runs only those tests
- scripts/run_affected_mypy.py: runs mypy on changed module dirs only

Blast radius rules: changes to core/, config/, observability/, or
conftest.py trigger a full run (these are imported everywhere).

Expected improvement: ~3-4 min -> ~20-30s for typical 1-2 module changes.
Full suite continues to run in CI as the correctness backstop.
Copilot AI review requested due to automatic review settings April 2, 2026 08:39
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 766440e4-51e6-4231-b02e-4a4091936429

📥 Commits

Reviewing files that changed from the base of the PR and between 3c18360 and ec80204.

📒 Files selected for processing (1)
  • scripts/run_affected_tests.py

Walkthrough

Updated pre-push hooks in .pre-commit-config.yaml to invoke new scripts instead of running mypy and pytest directly. Added executable scripts scripts/run_affected_mypy.py and scripts/run_affected_tests.py which compute a git base, detect changed Python files, and run mypy or unit pytest for affected modules (with full runs for foundational or conftest changes). pyproject.toml now ignores Bandit rules S603 and S607 for scripts/**/*.py. Documentation entries describing hook behavior were updated.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'perf: run only affected modules in pre-push hooks' accurately and concisely describes the main change: optimizing pre-push hook performance by limiting checks to affected modules only.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about performance problems, the specific changes made, blast-radius rules, and expected performance improvements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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 2, 2026

Dependency Review

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

Snapshot Warnings

⚠️: No snapshots were found for the head SHA ec80204.
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 pre-push hooks to run mypy and pytest only on modules affected by changes, improving CI efficiency. The review identified several issues where the use of .is_dir() instead of .exists() prevents the scripts from correctly identifying and testing individual files, and highlighted that the mypy script should be updated to include integration tests and ensure better coverage for top-level test files.

Comment on lines +126 to +135
if src_dir.is_dir():
paths.append(f"src/synthorg/{mod}")
test_dir = _REPO_ROOT / "tests" / "unit" / mod
if test_dir.is_dir():
paths.append(f"tests/unit/{mod}")

# Also include directly-changed test dirs not covered by src_modules.
for tp in sorted(test_paths):
if tp not in paths and (_REPO_ROOT / tp).is_dir():
paths.append(tp)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The use of .is_dir() prevents mypy from running on individual Python files that are located directly under src/synthorg/ or tests/unit/ (and are not part of the blast radius). Additionally, when a source module changes, the script should check for corresponding tests in all configured _TEST_KINDS (e.g., integration tests), not just unit tests.

Suggested change
if src_dir.is_dir():
paths.append(f"src/synthorg/{mod}")
test_dir = _REPO_ROOT / "tests" / "unit" / mod
if test_dir.is_dir():
paths.append(f"tests/unit/{mod}")
# Also include directly-changed test dirs not covered by src_modules.
for tp in sorted(test_paths):
if tp not in paths and (_REPO_ROOT / tp).is_dir():
paths.append(tp)
if src_dir.exists():
paths.append(f"src/synthorg/{mod}")
for kind in _TEST_KINDS:
t_path = f"tests/{kind}/{mod}"
if (_REPO_ROOT / t_path).exists():
paths.append(t_path)
# Also include directly-changed test paths not covered by src_modules.
for tp in sorted(test_paths):
if tp not in paths and (_REPO_ROOT / tp).exists():
paths.append(tp)

test_dirs.append(str(smoke.relative_to(_REPO_ROOT)))
else:
test_dir = _REPO_ROOT / "tests" / "unit" / mod
if test_dir.is_dir():
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

Using .is_dir() here will cause individual test files located directly in tests/unit/ (e.g., tests/unit/test_auth.py) to be skipped if they are modified. Using .exists() ensures both directories and standalone test files are included.

Suggested change
if test_dir.is_dir():
if test_dir.exists():

Comment on lines +94 to +96
and parts[2] not in _TOP_LEVEL_SRC
and parts[2] != "test_smoke.py"
):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Excluding test_smoke.py and top-level test files (like __init__.py) from the test_module classification means they won't be type-checked by mypy even if they are modified. These should be included to ensure full type coverage.

        and parts[1] in _TEST_KINDS
    ):

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates pre-push hooks to run targeted mypy/pytest based on git diff against origin/main, reducing local push time while keeping CI as the full-suite backstop.

Changes:

  • Add scripts/run_affected_tests.py to run only affected tests/unit/<module> (or full unit suite on “blast radius” changes).
  • Add scripts/run_affected_mypy.py to run mypy only on affected module dirs (or full run on “blast radius” changes).
  • Wire both scripts into pre-push hooks and add ruff per-file ignores for expected subprocess usage in scripts/.

Reviewed changes

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

File Description
scripts/run_affected_tests.py Computes changed modules and runs affected unit tests only
scripts/run_affected_mypy.py Computes changed modules and runs mypy on affected dirs only
.pre-commit-config.yaml Swaps pre-push mypy/pytest hooks to call the new scripts
pyproject.toml Adds S603/S607 ignores for scripts/**/*.py

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

Comment on lines +34 to +46
def _git(*args: str) -> str:
"""Run a git command and return stripped stdout."""
result = subprocess.run(
["git", *args],
capture_output=True,
text=True,
cwd=_REPO_ROOT,
check=False,
)
if result.returncode != 0:
print(f"git {' '.join(args)} failed: {result.stderr}", file=sys.stderr)
return ""
return result.stdout.strip()
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The script documents exit code 2 for "script error", but _git() currently just prints an error and returns an empty string. That can lead to false-success behavior (e.g., if git diff fails, py_changed becomes empty and the hook returns 0, skipping tests). Consider treating any git command failure as a hard error: propagate a non-zero status (return 2 from main()), or raise and catch an exception so the hook fails closed rather than skipping checks.

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +49
def _git(*args: str) -> str:
"""Run a git command and return stripped stdout."""
result = subprocess.run(
["git", *args],
capture_output=True,
text=True,
cwd=_REPO_ROOT,
check=False,
)
if result.returncode != 0:
print(f"git {' '.join(args)} failed: {result.stderr}", file=sys.stderr)
return ""
return result.stdout.strip()
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Same issue as the test runner script: _git() swallows failures by returning "", but the script advertises exit code 2 for errors and may incorrectly skip mypy if git commands fail (empty changed -> "No Python files changed" -> exit 0). Suggest failing closed by returning 2 from main() when any required git command fails (or raising an exception) so pre-push doesn't silently bypass type checks.

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +99
if (
len(parts) >= _MIN_MODULE_DEPTH
and parts[0] == "tests"
and parts[1] in _TEST_KINDS
and parts[2] not in _TOP_LEVEL_SRC
and parts[2] != "test_smoke.py"
):
return "test_module", None, f"tests/{parts[1]}/{parts[2]}"

return "other", None, None
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

_classify_path() explicitly excludes tests/unit/test_smoke.py (and other top-level tests/<kind>/*.py files), which means changing tests/unit/test_smoke.py will make the pre-push mypy hook print "Changed files don't map to any mypy targets -- skipping." and exit 0. Consider mapping these top-level test files to a concrete mypy target (e.g., include the file itself, or treat it as run-all) so edits to smoke tests are still type-checked.

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

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

Inline comments:
In `@scripts/run_affected_mypy.py`:
- Around line 128-130: The current logic in scripts/run_affected_mypy.py only
adds unit test directories for a changed source module (it builds test_dir =
_REPO_ROOT / "tests" / "unit" / mod and appends "tests/unit/{mod}"); update that
routine to also check for an integration test directory (_REPO_ROOT / "tests" /
"integration" / mod) and, if present, append "tests/integration/{mod}" to paths
so integration tests for the changed module are included in the mypy run; adjust
any variables or loops that build paths/test_paths accordingly (referencing
test_dir, mod, and paths).

In `@scripts/run_affected_tests.py`:
- Around line 34-69: The helper functions _git, _merge_base, _changed_files and
the constants _BLAST_RADIUS_MODULES, _TOP_LEVEL_SRC, _MIN_MODULE_DEPTH are
duplicated across two scripts; extract them into a single shared module (e.g.,
scripts._git_utils) and import that module from both scripts so logic is
centralized. Move the implementations of _git, _merge_base, _changed_files and
the three constants into the new module, update both scripts to import and call
the functions/constants from the shared module, and ensure the new module uses
the same _REPO_ROOT or accepts it as an argument to preserve behavior.
- Line 156: The current calls that return _run_pytest(["tests/"]) run full
pytest discovery across all test folders; change those call sites to return
_run_pytest(["tests/unit/"]) so pytest only discovers unit tests and avoids
scanning integration/e2e directories—locate the return statements that call
_run_pytest(["tests/"]) in scripts/run_affected_tests.py (the two occurrences
currently returning _run_pytest(["tests/"])) and update the argument to
["tests/unit/"].
🪄 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: 1ccb7bdc-82d3-4ed5-8ef2-e0e629d53184

📥 Commits

Reviewing files that changed from the base of the PR and between 17b58db and be77edf.

📒 Files selected for processing (4)
  • .pre-commit-config.yaml
  • pyproject.toml
  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Deploy Preview
  • GitHub Check: Agent
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use from __future__ import annotations in Python files; Python 3.14 has PEP 649 native lazy annotations
Use except A, B: syntax (no parentheses) for exception handling in Python 3.14 as enforced by ruff
Use line length of 88 characters for Python code (enforced by ruff)

Files:

  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
scripts/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use scripts/ directory for CI/build utilities and development-time validation with relaxed ruff rules allowing print() and deferred imports

Files:

  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
pyproject.toml

📄 CodeRabbit inference engine (CLAUDE.md)

All Python versions must be pinned using == in pyproject.toml; use uv sync to install dependencies

Files:

  • pyproject.toml
🧠 Learnings (22)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Run pre-commit hooks including: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint, go vet, no-em-dashes, no-redundant-timeout, eslint-web
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint (Dockerfile linting).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (CLI, conditional), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard, zero warnings, conditional)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to scripts/**/*.py : Use `scripts/` directory for CI/build utilities and development-time validation with relaxed ruff rules allowing `print()` and deferred imports
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to scripts/**/*.py : Scripts in `scripts/` (CI/build utilities and development-time validation hooks) — relaxed ruff rules: `print` and deferred imports allowed
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Type-check Python code with `uv run mypy src/ tests/` (strict mode)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to src/**/*.py : Provide type hints for all public functions and use mypy strict mode
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run all pre-commit hooks with `uv run pre-commit run --all-files`
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).

Applied to files:

  • .pre-commit-config.yaml
  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci

Applied to files:

  • .pre-commit-config.yaml
  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to .pre-commit-config.yaml : Pre-commit.ci: autoupdate disabled (`autoupdate_schedule: never`) — Dependabot owns hook version bumps via `pre-commit` ecosystem

Applied to files:

  • .pre-commit-config.yaml
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint (Dockerfile linting).

Applied to files:

  • .pre-commit-config.yaml
  • scripts/run_affected_mypy.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Run pre-commit hooks including: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint, go vet, no-em-dashes, no-redundant-timeout, eslint-web

Applied to files:

  • .pre-commit-config.yaml
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (CLI, conditional), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard, zero warnings, conditional)

Applied to files:

  • .pre-commit-config.yaml
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run all pre-commit hooks with `uv run pre-commit run --all-files`

Applied to files:

  • .pre-commit-config.yaml
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run unit tests with `uv run python -m pytest tests/ -m unit -n auto`; integration tests with `-m integration -n auto`; e2e tests with `-m e2e -n auto`

Applied to files:

  • .pre-commit-config.yaml
  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Type-check Python code with `uv run mypy src/ tests/` (strict mode)

Applied to files:

  • .pre-commit-config.yaml
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Lint Python code with `uv run ruff check src/ tests/`; auto-fix with `--fix`; format with `uv run ruff format src/ tests/`

Applied to files:

  • .pre-commit-config.yaml
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run full pytest suite with coverage: `uv run python -m pytest tests/ -n auto --cov=synthorg --cov-fail-under=80`

Applied to files:

  • .pre-commit-config.yaml
  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to scripts/**/*.py : Scripts in `scripts/` (CI/build utilities and development-time validation hooks) — relaxed ruff rules: `print` and deferred imports allowed

Applied to files:

  • .pre-commit-config.yaml
  • scripts/run_affected_tests.py
  • pyproject.toml
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Docker workflow (`docker.yml`): build + Trivy/Grype scan + push to GHCR + cosign sign + SLSA L3 provenance. CVE triage: `.github/.trivyignore.yaml`, `.github/.grype.yaml`

Applied to files:

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

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to pyproject.toml : Docs group in `pyproject.toml` includes: `zensical`, `mkdocstrings[python]`, `griffe-pydantic`

Applied to files:

  • pyproject.toml
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to scripts/**/*.py : Scripts in scripts/ have relaxed ruff rules: print and deferred imports allowed.

Applied to files:

  • pyproject.toml
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to scripts/**/*.py : Use `scripts/` directory for CI/build utilities and development-time validation with relaxed ruff rules allowing `print()` and deferred imports

Applied to files:

  • pyproject.toml
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to pyproject.toml : Dev group in `pyproject.toml` includes: test group + ruff, mypy, pre-commit, commitizen, pip-audit

Applied to files:

  • pyproject.toml
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to src/**/*.py : Provide type hints for all public functions and use mypy strict mode

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to **/*.py : Include type hints for all public functions; enforce with mypy strict mode

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-15T22:16:31.632Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T22:16:31.632Z
Learning: Applies to **/*.py : Type hints: all public functions and classes require type hints, enforced with mypy strict mode.

Applied to files:

  • scripts/run_affected_mypy.py
🔇 Additional comments (11)
scripts/run_affected_mypy.py (6)

37-49: LGTM!

The _git helper properly handles subprocess calls with check=False, captures output, and logs failures to stderr. Using cwd=_REPO_ROOT ensures consistent behavior regardless of where the hook is invoked.


52-57: LGTM!

The fallback to HEAD~1 when origin/main is unavailable (shallow clones, detached HEAD) is a sensible approach. The empty string case is handled gracefully in main() by triggering a full mypy run.


60-68: LGTM!

The combination of base...HEAD (committed changes since merge-base) and HEAD (uncommitted working tree changes) correctly captures everything that will land on the remote after push.


71-99: LGTM!

The path classification logic correctly handles the various cases: conftest.py anywhere triggers full run, blast-radius modules (core, config, observability) trigger full run, and regular modules are mapped to their corresponding directories.


140-144: LGTM!

Using sys.executable -m mypy ensures the correct Python environment is used. The return code is properly propagated.


147-180: LGTM!

The main function orchestrates the workflow cleanly with appropriate logging for each decision path. The fallback behaviors are sensible: empty merge base runs full mypy, no Python changes skips, and foundational changes trigger full runs.

pyproject.toml (1)

151-152: LGTM!

Adding S603 and S607 ignores for scripts/**/*.py is appropriate. These Bandit rules flag subprocess calls and partial executable paths, which are expected and necessary in pre-push hook scripts that invoke git, mypy, and pytest.

.pre-commit-config.yaml (1)

66-80: LGTM!

The hook configurations are correctly updated to use the new affected-modules scripts. Keeping files: ^(src/|tests/).*\.py$ ensures the hooks only trigger when Python files are involved, while pass_filenames: false allows the scripts to determine their own targets via git diff analysis.

scripts/run_affected_tests.py (3)

99-128: LGTM!

The mapping logic correctly handles the module-to-test-directory relationship. The special case for "." mapping to test_smoke.py makes sense for isolated smoke test changes.


131-145: LGTM!

The pytest invocation correctly includes -m unit marker filter, -n auto for parallel execution, and -q for quieter output suitable for hook context. Based on learnings, this matches the expected command pattern.


148-177: LGTM!

The main function handles all edge cases appropriately with clear logging. The decision flow is sound: empty merge-base → full run, no Python changes → skip, foundational changes → full run, no mapped tests → skip, otherwise → targeted run.

Comment on lines +34 to +69
def _git(*args: str) -> str:
"""Run a git command and return stripped stdout."""
result = subprocess.run(
["git", *args],
capture_output=True,
text=True,
cwd=_REPO_ROOT,
check=False,
)
if result.returncode != 0:
print(f"git {' '.join(args)} failed: {result.stderr}", file=sys.stderr)
return ""
return result.stdout.strip()


def _merge_base() -> str:
"""Find the merge base between HEAD and origin/main."""
base = _git("merge-base", "HEAD", "origin/main")
if not base:
# Fallback: if origin/main doesn't exist (shallow clone, detached),
# diff against HEAD~1 so we at least check *something*.
base = _git("rev-parse", "HEAD~1")
return base


def _changed_files(base: str) -> list[str]:
"""Return files changed between *base* and HEAD."""
# Include both committed and staged/unstaged changes so the hook catches
# everything that will land on the remote.
committed = _git("diff", "--name-only", f"{base}...HEAD")
uncommitted = _git("diff", "--name-only", "HEAD")
all_files: set[str] = set()
for block in (committed, uncommitted):
if block:
all_files.update(block.splitlines())
return sorted(all_files)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider extracting shared utilities.

The _git, _merge_base, _changed_files functions and constants (_BLAST_RADIUS_MODULES, _TOP_LEVEL_SRC, _MIN_MODULE_DEPTH) are duplicated verbatim in run_affected_mypy.py. While self-contained scripts are easier to maintain independently, extracting these to a shared module (e.g., scripts/_git_utils.py) would reduce duplication.

This is a trade-off: keeping scripts self-contained avoids import complexity in hook execution, but increases maintenance burden when logic needs to change (e.g., adding a new blast-radius module).

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

In `@scripts/run_affected_tests.py` around lines 34 - 69, The helper functions
_git, _merge_base, _changed_files and the constants _BLAST_RADIUS_MODULES,
_TOP_LEVEL_SRC, _MIN_MODULE_DEPTH are duplicated across two scripts; extract
them into a single shared module (e.g., scripts._git_utils) and import that
module from both scripts so logic is centralized. Move the implementations of
_git, _merge_base, _changed_files and the three constants into the new module,
update both scripts to import and call the functions/constants from the shared
module, and ensure the new module uses the same _REPO_ROOT or accepts it as an
argument to preserve behavior.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.93%. Comparing base (1ff462a) to head (ec80204).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #992   +/-   ##
=======================================
  Coverage   91.93%   91.93%           
=======================================
  Files         643      643           
  Lines       34897    34897           
  Branches     3423     3423           
=======================================
  Hits        32082    32082           
  Misses       2211     2211           
  Partials      604      604           

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

- Fail-closed on git errors (raise _GitError instead of returning empty)
- Add _SAFE_MODULE_NAME regex to prevent path traversal via crafted paths
- Include integration test dirs in mypy for changed source modules
- Use tests/unit/ instead of tests/ for full runs (avoid scanning e2e/integration)
- Handle test_smoke.py in mypy script (was silently skipped)
- Remove dead py.typed entry from _TOP_LEVEL_SRC
- Fix docstrings: exit code 2, conftest wording, .py-only note, merge-base comment
- Update CLAUDE.md pre-push hooks description (affected modules only)
- Update contributing.md pre-push description
- Update getting_started.md to list pre-push hooks
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 `@scripts/run_affected_mypy.py`:
- Around line 13-17: The module docstring claims exit code 2 for a git command
failure, but main() catches _GitError and falls back to running full mypy, so 2
is never returned; update the top-level docstring to reflect the actual
"fail-closed" behavior (i.e., only return mypy's exit codes 0 or 1) or
explicitly state that git failures cause a full mypy run and thus no distinct
exit code 2 is produced; look at main() and the _GitError handling to ensure the
docstring matches the implemented behavior.

In `@scripts/run_affected_tests.py`:
- Around line 13-17: The module-level docstring currently claims exit code 2 is
returned for git failures, but the script catches _GitError and falls back to
running the full test suite (returning pytest's exit code) so exit code 2 is
never produced; update the docstring in scripts/run_affected_tests.py to remove
or correct the line about exit code 2 (or reword it to state that git failures
fall back to running all tests and the script returns the pytest exit code), and
ensure the docstring references the actual behavior implemented around the
_GitError handling (the fallback-to-full-run logic) so documentation matches
code.
🪄 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: 8345be4d-8ea7-4995-8089-44a7414dd240

📥 Commits

Reviewing files that changed from the base of the PR and between be77edf and 2429465.

📒 Files selected for processing (5)
  • CLAUDE.md
  • docs/getting_started.md
  • docs/guides/contributing.md
  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use from __future__ import annotations in Python files; Python 3.14 has PEP 649 native lazy annotations
Use except A, B: syntax (no parentheses) for exception handling in Python 3.14 as enforced by ruff
Use line length of 88 characters for Python code (enforced by ruff)

Files:

  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
scripts/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use scripts/ directory for CI/build utilities and development-time validation with relaxed ruff rules allowing print() and deferred imports

Files:

  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
🧠 Learnings (38)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint (Dockerfile linting).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Run pre-commit hooks including: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint, go vet, no-em-dashes, no-redundant-timeout, eslint-web
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (CLI, conditional), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard, zero warnings, conditional)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to scripts/**/*.py : Scripts in `scripts/` (CI/build utilities and development-time validation hooks) — relaxed ruff rules: `print` and deferred imports allowed
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to scripts/**/*.py : Use `scripts/` directory for CI/build utilities and development-time validation with relaxed ruff rules allowing `print()` and deferred imports
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to web/src/**/*.{ts,tsx} : PostToolUse hook (`scripts/check_web_design_system.py`) enforces design system rules on every Edit/Write to `web/src/`
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).

Applied to files:

  • CLAUDE.md
  • docs/getting_started.md
  • docs/guides/contributing.md
  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci

Applied to files:

  • CLAUDE.md
  • docs/getting_started.md
  • docs/guides/contributing.md
  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (CLI, conditional), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard, zero warnings, conditional)

Applied to files:

  • CLAUDE.md
  • docs/getting_started.md
  • docs/guides/contributing.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint (Dockerfile linting).

Applied to files:

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

Applied to files:

  • CLAUDE.md
  • docs/guides/contributing.md
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Run pre-commit hooks including: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint, go vet, no-em-dashes, no-redundant-timeout, eslint-web

Applied to files:

  • CLAUDE.md
  • docs/getting_started.md
  • docs/guides/contributing.md
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: All commits must be GPG/SSH signed on `main` branch via branch protection

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: GPG/SSH signed commits required on `main` via branch protection — all commits must be signed

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.

Applied to files:

  • CLAUDE.md
  • docs/getting_started.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to cli/**/*.go : golangci-lint + go vet enforced on CLI code via pre-commit (conditional on `cli/**/*.go`) and pre-push hooks

Applied to files:

  • CLAUDE.md
  • docs/getting_started.md
  • docs/guides/contributing.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run all pre-commit hooks with `uv run pre-commit run --all-files`

Applied to files:

  • docs/getting_started.md
  • docs/guides/contributing.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Install via `uv sync` (installs all deps including dev group by default)

Applied to files:

  • docs/getting_started.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Lint Python code with `uv run ruff check src/ tests/`; auto-fix with `--fix`; format with `uv run ruff format src/ tests/`

Applied to files:

  • docs/getting_started.md
  • docs/guides/contributing.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run unit tests with `uv run python -m pytest tests/ -m unit -n auto`; integration tests with `-m integration -n auto`; e2e tests with `-m e2e -n auto`

Applied to files:

  • docs/getting_started.md
  • scripts/run_affected_tests.py
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • docs/getting_started.md
📚 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/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.

Applied to files:

  • docs/getting_started.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Lint Go code with `golangci-lint` and `go vet`. Run tests with `-race` flag to detect race conditions.

Applied to files:

  • docs/getting_started.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Gitleaks scans for secrets via pre-commit hook and weekly CI job

Applied to files:

  • docs/guides/contributing.md
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to scripts/**/*.py : Scripts in `scripts/` (CI/build utilities and development-time validation hooks) — relaxed ruff rules: `print` and deferred imports allowed

Applied to files:

  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run full pytest suite with coverage: `uv run python -m pytest tests/ -n auto --cov=synthorg --cov-fail-under=80`

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to scripts/**/*.py : Use `scripts/` directory for CI/build utilities and development-time validation with relaxed ruff rules allowing `print()` and deferred imports

Applied to files:

  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.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 : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.

Applied to files:

  • scripts/run_affected_tests.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:

  • scripts/run_affected_tests.py
  • scripts/run_affected_mypy.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:

  • scripts/run_affected_tests.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:

  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to tests/**/*.py : Use pytest markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to tests/**/*.py : 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:

  • scripts/run_affected_tests.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests; fix them fully and fundamentally by mocking `time.monotonic()`, `asyncio.sleep()`, or using `asyncio.Event().wait()` for indefinite blocking

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-04-01T06:54:44.133Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-01T06:54:44.133Z
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:

  • scripts/run_affected_tests.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to src/**/*.py : Provide type hints for all public functions and use mypy strict mode

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to **/*.py : Include type hints for all public functions; enforce with mypy strict mode

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-15T22:16:31.632Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T22:16:31.632Z
Learning: Applies to **/*.py : Type hints: all public functions and classes require type hints, enforced with mypy strict mode.

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Type-check Python code with `uv run mypy src/ tests/` (strict mode)

Applied to files:

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

Applied to files:

  • scripts/run_affected_mypy.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:

  • scripts/run_affected_mypy.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:

  • scripts/run_affected_mypy.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 : Tests must use test-provider, test-small-001, etc. for vendor-agnostic test data.

Applied to files:

  • scripts/run_affected_mypy.py
🔇 Additional comments (14)
docs/guides/contributing.md (1)

161-161: LGTM!

Documentation accurately reflects the new affected-modules behavior for pre-push hooks, with appropriate cross-reference to the full hook table.

CLAUDE.md (1)

180-180: LGTM!

Pre-push hook documentation correctly describes the affected-modules behavior and the blast-radius triggers (core, config, observability, conftest). This matches the implementation in the new scripts.

docs/getting_started.md (2)

53-53: LGTM!

Accurate documentation that pre-commit install now covers the pre-push stage alongside pre-commit and commit-msg.


72-75: LGTM!

The hook table clearly documents the new pre-push hooks with accurate descriptions of affected-modules behavior and appropriate conditional triggers for Go CLI and web dashboard.

scripts/run_affected_mypy.py (5)

42-61: LGTM!

The _GitError exception and _git helper function are well-implemented with proper error handling and fail-closed behavior.


64-85: LGTM!

The merge-base computation with HEAD~1 fallback handles shallow clones gracefully. Including both committed and uncommitted changes provides a good safety net for pre-push validation.


88-117: LGTM!

The path classification logic correctly identifies all relevant categories. The defensive choice to treat unsafe module names as "top_level_src" (triggering full runs) is appropriate for a pre-push safety gate.


120-164: LGTM!

The path mapping logic correctly handles module-to-target resolution. The inclusion of both unit and integration test directories (addressing the past review feedback) ensures comprehensive type-checking coverage.


167-209: LGTM!

The mypy invocation and main entry point are well-structured with clear user feedback and proper fail-closed behavior.

scripts/run_affected_tests.py (5)

39-82: LGTM!

Git helper functions are correctly implemented. The code duplication with run_affected_mypy.py was previously noted as a nitpick trade-off for self-contained scripts.


85-115: LGTM!

Path classification correctly identifies test categories and handles special cases like test_smoke.py for root-level test changes.


118-147: LGTM!

Test directory mapping correctly handles both module-specific tests and the special test_smoke.py case for root-level changes.


150-164: LGTM!

Pytest invocation correctly uses the unit marker and parallel execution. The -n auto is technically redundant with addopts in pyproject.toml but explicit specification ensures the intended behavior.


167-202: LGTM!

The main entry point correctly implements fail-closed behavior with full unit suite fallback. The use of tests/unit/ for full runs (addressing past review feedback) avoids unnecessary discovery overhead in integration/e2e directories.

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 `@scripts/run_affected_tests.py`:
- Around line 106-110: The is_root boolean expression redundantly checks
parts[2] == "test_smoke.py" and parts[2] in _TOP_LEVEL_SRC even though not
_SAFE_MODULE_NAME.match(parts[2]) already covers non-identifier names (the regex
^[a-zA-Z_][a-zA-Z0-9_]*$ cannot match strings with '.'), so either remove those
redundant disjuncts from the is_root assignment or, if they are kept for
readability, add a clarifying comment above the is_root calculation that
explains why test_smoke.py and entries in _TOP_LEVEL_SRC are explicitly listed
despite the regex check; update the expression in the is_root calculation or add
the comment accordingly and keep references to is_root, _SAFE_MODULE_NAME,
parts[2], "test_smoke.py", and _TOP_LEVEL_SRC so reviewers can verify the
change.
🪄 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: 204b4509-770f-4194-96ea-08a9bd0894b6

📥 Commits

Reviewing files that changed from the base of the PR and between 2429465 and 3c18360.

📒 Files selected for processing (2)
  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use from __future__ import annotations in Python files; Python 3.14 has PEP 649 native lazy annotations
Use except A, B: syntax (no parentheses) for exception handling in Python 3.14 as enforced by ruff
Use line length of 88 characters for Python code (enforced by ruff)

Files:

  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
scripts/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use scripts/ directory for CI/build utilities and development-time validation with relaxed ruff rules allowing print() and deferred imports

Files:

  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
🧠 Learnings (24)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint (Dockerfile linting).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Run pre-commit hooks including: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint, go vet, no-em-dashes, no-redundant-timeout, eslint-web
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (CLI, conditional), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard, zero warnings, conditional)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to scripts/**/*.py : Scripts in `scripts/` (CI/build utilities and development-time validation hooks) — relaxed ruff rules: `print` and deferred imports allowed
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to scripts/**/*.py : Use `scripts/` directory for CI/build utilities and development-time validation with relaxed ruff rules allowing `print()` and deferred imports
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to web/src/**/*.{ts,tsx} : PostToolUse hook (`scripts/check_web_design_system.py`) enforces design system rules on every Edit/Write to `web/src/`
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Pre-push hooks: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional on cli/**/*.go) (fast gate before push, skipped in pre-commit.ci — dedicated CI jobs already run these).

Applied to files:

  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci

Applied to files:

  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to src/**/*.py : Provide type hints for all public functions and use mypy strict mode

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to scripts/**/*.py : Scripts in `scripts/` (CI/build utilities and development-time validation hooks) — relaxed ruff rules: `print` and deferred imports allowed

Applied to files:

  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to **/*.py : Include type hints for all public functions; enforce with mypy strict mode

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-15T22:16:31.632Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T22:16:31.632Z
Learning: Applies to **/*.py : Type hints: all public functions and classes require type hints, enforced with mypy strict mode.

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Type-check Python code with `uv run mypy src/ tests/` (strict mode)

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to scripts/**/*.py : Use `scripts/` directory for CI/build utilities and development-time validation with relaxed ruff rules allowing `print()` and deferred imports

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Run pre-commit hooks including: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint, go vet, no-em-dashes, no-redundant-timeout, eslint-web

Applied to files:

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

Applied to files:

  • scripts/run_affected_mypy.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:

  • scripts/run_affected_mypy.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:

  • scripts/run_affected_mypy.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:

  • scripts/run_affected_mypy.py
  • scripts/run_affected_tests.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 : Tests must use test-provider, test-small-001, etc. for vendor-agnostic test data.

Applied to files:

  • scripts/run_affected_mypy.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run unit tests with `uv run python -m pytest tests/ -m unit -n auto`; integration tests with `-m integration -n auto`; e2e tests with `-m e2e -n auto`

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Run full pytest suite with coverage: `uv run python -m pytest tests/ -n auto --cov=synthorg --cov-fail-under=80`

Applied to files:

  • scripts/run_affected_tests.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:

  • scripts/run_affected_tests.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:

  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to tests/**/*.py : Use pytest markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to tests/**/*.py : 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:

  • scripts/run_affected_tests.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 : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-04-02T08:03:40.390Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:03:40.390Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests; fix them fully and fundamentally by mocking `time.monotonic()`, `asyncio.sleep()`, or using `asyncio.Event().wait()` for indefinite blocking

Applied to files:

  • scripts/run_affected_tests.py
📚 Learning: 2026-04-01T06:54:44.133Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-01T06:54:44.133Z
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:

  • scripts/run_affected_tests.py
🔇 Additional comments (13)
scripts/run_affected_mypy.py (7)

40-83: LGTM!

The git helper functions correctly implement the fail-closed pattern with appropriate error handling.

Note: These utilities are duplicated with run_affected_tests.py. A previous review suggested extracting shared code to a common module—this remains a valid optional improvement for reducing maintenance burden.


1-20: LGTM!

The docstring accurately reflects the expanded scope (covering both tests/unit/ and tests/integration/ directories) and the mypy-aligned exit codes.


22-37: LGTM!

The _TEST_KINDS constant appropriately extends mypy coverage to both unit and integration test directories, ensuring type errors in integration tests are caught when their source modules change.


86-115: LGTM!

The 3-value return design cleanly separates concerns: module captures source modules for bidirectional mapping, while test_path captures directly-changed test locations. The distinction between test_file and test_module categories correctly handles both standalone test files and module directories.


118-128: LGTM!

The helper cleanly encapsulates the module-to-paths mapping logic, including both source and test directories (unit + integration). Existence checks prevent mypy errors from missing optional directories.


131-162: LGTM!

The two-phase path collection ensures comprehensive coverage: source module changes pull in their corresponding tests, while directly-changed test paths are also included. The deduplication and existence checks prevent duplicate or invalid mypy targets.


165-207: LGTM!

The main function mirrors the test script's structure with appropriate adaptations for mypy. The full ["src/", "tests/"] fallback on errors ensures type safety even when change detection fails.

scripts/run_affected_tests.py (6)

1-20: LGTM!

The docstring accurately describes the script's behavior, exit codes, and fallback strategy. Imports are minimal and appropriate for the task.


22-34: LGTM!

Constants are appropriately defined. The _SAFE_MODULE_NAME regex effectively validates Python package names and prevents path traversal attacks by rejecting paths containing dots or special characters.


37-56: LGTM!

The _git helper follows secure subprocess practices (no shell execution, explicit error handling). The error messages include both the command and stderr output for debugging.


59-80: LGTM!

Good defensive implementation: the merge-base fallback handles shallow clones and detached HEADs, while combining committed and uncommitted changes ensures nothing slips through pre-push checks.


116-145: LGTM!

The early-exit pattern for blast-radius changes is efficient, and existence checks ensure pytest receives only valid paths. The special handling for root-level test files (test_smoke.py) aligns with the project's test layout.


148-200: LGTM!

The main function has clear control flow with appropriate fallbacks. Using sys.executable ensures pytest runs with the same interpreter as the script. The informative print statements aid debugging when hooks run.

@Aureliolo Aureliolo merged commit 7956e23 into main Apr 2, 2026
10 of 12 checks passed
@Aureliolo Aureliolo deleted the feat/improve-local-test-speed branch April 2, 2026 09:12
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 2, 2026 09:12 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Apr 2, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.5.7](v0.5.6...v0.5.7)
(2026-04-02)


### Features

* comparison page -- SynthOrg vs agent orchestration frameworks
([#994](#994))
([6f937ef](6f937ef)),
closes [#981](#981)
* event-driven and budget-driven ceremony scheduling strategies
([#995](#995))
([f88e7b0](f88e7b0)),
closes [#971](#971)
[#972](#972)
* template packs for post-setup additive team expansion
([#996](#996))
([b45e14a](b45e14a)),
closes [#727](#727)


### Performance

* preload JetBrains Mono font, remove unused api.github.com preconnect
([#998](#998))
([2a189c2](2a189c2))
* run only affected modules in pre-push hooks
([#992](#992))
([7956e23](7956e23))


### Maintenance

* bump astro from 6.1.2 to 6.1.3 in /site in the all group
([#988](#988))
([17b58db](17b58db))
* bump the all group across 1 directory with 2 updates
([#989](#989))
([1ff462a](1ff462a))

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

2 participants