fix: per-worktree git-hook isolation + hookify gate migration + MSW drift fix#1949
Conversation
Pre-reviewed by 10 agents; 8 findings addressed (OpenCode plugin/opencode.json parity, fail-closed git-status reconcile + _merge_base + 4 bash gates + check_no_bulk_edit on malformed envelopes, path-traversal regex comment).
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Summary of ChangesHello, 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 introduces structural improvements to development tooling, specifically focusing on Git hook isolation across worktrees and the enforcement of development conventions. By moving hookify rules into active PreToolUse gates and adding a reconcile guard for test runs, the changes significantly reduce flaky 'files modified' errors and improve environment consistency. Additionally, it streamlines Mock Service Worker maintenance by automating its generation. Highlights
Using Gemini Code AssistThe 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
Customization To customize the 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 Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. 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
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📜 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). (8)
🧰 Additional context used📓 Path-based instructions (2){src,tests}/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
tests/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
⚙️ CodeRabbit configuration file
Files:
🧠 Learnings (2)📓 Common learnings📚 Learning: 2026-05-05T09:04:46.195ZApplied to files:
🔇 Additional comments (8)
WalkthroughThis PR migrates hook enforcement from inert 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request migrates "hookify" rules from declarative Markdown files to committed shell scripts and Python gates, ensuring consistent enforcement across Claude Code, OpenCode, and pre-commit. It introduces a venv-agnostic git-hook wrapper system using a relative core.hooksPath to provide worktree isolation and prevent broken hooks. Key additions include a gate against stdlib logging in application code, a worktree reconciliation step in run_affected_tests.py to revert tracked files modified during test runs, and an update to the bulk-edit guard to allow native tool-based replacements. Review feedback identifies necessary improvements to regex patterns for pytest flag enforcement to handle = syntax, a type check for JSON payloads in the bulk-edit guard to prevent potential AttributeErrors, and a more precise check for Git renames to avoid false positives with filenames containing ->.
|
|
||
| # An explicit, NON-zero worker count is always wrong: pyproject | ||
| # addopts already applies -n=8 --dist=loadfile. Omit the flag. | ||
| if echo "$COMMAND" | grep -qE '(^|[[:space:]])(-n[[:space:]]*([1-9][0-9]*|auto|logical)|--numprocesses[[:space:]]+([1-9][0-9]*|auto|logical))(\b|$)'; then |
There was a problem hiding this comment.
The regex for -n and --numprocesses does not account for the = assignment syntax (e.g., -n=8 or --numprocesses=8), which is supported by pytest. This allows developers to bypass the parallel execution enforcement by using the equals sign.
| if echo "$COMMAND" | grep -qE '(^|[[:space:]])(-n[[:space:]]*([1-9][0-9]*|auto|logical)|--numprocesses[[:space:]]+([1-9][0-9]*|auto|logical))(\b|$)'; then | |
| if echo "$COMMAND" | grep -qE '(^|[[:space:]])(-n([[:space:]]*|=)([1-9][0-9]*|auto|logical)|--numprocesses([[:space:]]+|=)([1-9][0-9]*|auto|logical))(\b|$)'; then |
|
|
||
| # xdist-disable: only legitimate to read ONE test's full log, so | ||
| # require a node id. A bare directory/suite run with -n0 is blocked. | ||
| if echo "$COMMAND" | grep -qE '(^|[[:space:]])(-n[[:space:]]*0|--numprocesses[[:space:]]+0|--dist[[:space:]]+no|-p[[:space:]]+no:xdist)(\b|$)'; then |
There was a problem hiding this comment.
Similar to the previous check, the regex for disabling xdist does not handle the = assignment syntax (e.g., -n=0, --numprocesses=0, or --dist=no), providing a way to bypass the enforcement.
| if echo "$COMMAND" | grep -qE '(^|[[:space:]])(-n[[:space:]]*0|--numprocesses[[:space:]]+0|--dist[[:space:]]+no|-p[[:space:]]+no:xdist)(\b|$)'; then | |
| if echo "$COMMAND" | grep -qE '(^|[[:space:]])(-n([[:space:]]*|=)0|--numprocesses([[:space:]]+|=)0|--dist([[:space:]]+|=)no|-p[[:space:]]+no:xdist)(\b|$)'; then |
| payload = json.loads(raw) | ||
| except json.JSONDecodeError: | ||
| return 0 | ||
|
|
||
| tool_name = payload.get("tool_name", "") | ||
| tool_input = payload.get("tool_input") or {} | ||
|
|
||
| if tool_name == "Edit" and tool_input.get("replace_all") is True: | ||
| except json.JSONDecodeError as exc: |
There was a problem hiding this comment.
The script assumes json.loads(raw) always returns a dictionary. However, json.loads can return other types (like a list or a primitive), which would cause an AttributeError when calling .get() later. A type check should be added to ensure the payload is a dictionary. Following the project's Python 3.14 style, multiple exceptions can be caught without parentheses.
payload = json.loads(raw)
if not isinstance(payload, dict):
raise ValueError(f"Expected JSON object, got {type(payload).__name__}")
except json.JSONDecodeError, ValueError as exc:References
- PEP 758 makes except A, B: (comma-separated, no parens) valid Python. This codebase uses that form intentionally. (link)
| # Porcelain v1: 2 status chars, a space, then the path(s). | ||
| payload = line[3:] | ||
| if " -> " in payload: | ||
| old, new = payload.split(" -> ", 1) | ||
| paths.add(old.strip()) | ||
| paths.add(new.strip()) | ||
| else: | ||
| paths.add(payload.strip()) |
There was a problem hiding this comment.
The logic for identifying renames is too broad as it splits any line containing -> regardless of the Git status. If a filename literally contains -> (which is valid in many filesystems), this will incorrectly parse it as a rename and attempt to restore non-existent paths, leading to a false-positive failure of the hook. The check should be restricted to lines with the R (Rename) status.
| # Porcelain v1: 2 status chars, a space, then the path(s). | |
| payload = line[3:] | |
| if " -> " in payload: | |
| old, new = payload.split(" -> ", 1) | |
| paths.add(old.strip()) | |
| paths.add(new.strip()) | |
| else: | |
| paths.add(payload.strip()) | |
| # Porcelain v1: 2 status chars, a space, then the path(s). | |
| status = line[:2] | |
| payload = line[3:] | |
| if "R" in status and " -> " in payload: | |
| old, new = payload.split(" -> ", 1) | |
| paths.add(old.strip()) | |
| paths.add(new.strip()) | |
| else: | |
| paths.add(payload.strip()) |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.opencode/plugins/synthorg-hooks.ts:
- Around line 30-34: The comment block starting with "The former
.claude/hookify.*.md rules were migrated to the committed scripts..." contains
migration-framing prose and should be replaced with a short "why" comment that
explains the current implementation rationale only; remove references to
migration history or reviewer/issue back-refs and instead state the purpose
(e.g., that the listed scripts centralize hook rules for OpenCode and Claude
Code) so the comment in synthorg-hooks.ts contains only implementation
rationale.
In `@docs/reference/convention-gates.md`:
- Line 71: Update the markdown so the inline code span doesn't include a
trailing space: change the snippet `` `cd ` `` to `` `cd` `` and place any
necessary trailing space outside the backticks; apply this change in the
description of check_no_cd_prefix.sh in the convention-gates documentation where
the `cd ` example appears to satisfy markdownlint MD038.
In `@scripts/check_enforce_parallel_tests.sh`:
- Around line 65-75: The regex checks on COMMAND for explicit -n/--numprocesses
and for single-process flags miss equals-sign forms (e.g. -n=8,
--numprocesses=0, --dist=no), so update both grep -qE patterns to accept either
whitespace or an equals sign after the option names; specifically modify the
expressions that reference COMMAND (the first if checking -n/--numprocesses and
the second if checking -n0/--numprocesses0/--dist/no/-p no:xdist) to allow
(?:[[:space:]]*|=) between the flag and its value and to handle --dist=no and
-p=no:xdist forms so equals-sign variants cannot bypass the gate, leaving the
deny/exit logic unchanged.
🪄 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 (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 740bf58f-3d77-4616-b753-dc0280ad2230
📒 Files selected for processing (40)
.claude/hookify.block-double-push.md.claude/hookify.block-pr-create.md.claude/hookify.enforce-parallel-tests.md.claude/hookify.function-length.md.claude/hookify.missing-logger.md.claude/hookify.no-cd-prefix.md.claude/hookify.no-future-annotations.md.claude/hookify.no-local-coverage.md.claude/hookify.pep758-except.md.claude/settings.json.gitattributes.opencode/plugins/synthorg-hooks.ts.pre-commit-config.yamlCLAUDE.mddata/runtime_stats.yamldocs/architecture/decisions.mddocs/getting_started.mddocs/reference/convention-gates.mdopencode.jsonscripts/check_enforce_parallel_tests.shscripts/check_image_signatures.pyscripts/check_no_bulk_edit.pyscripts/check_no_cd_prefix.shscripts/check_no_local_coverage.shscripts/check_no_pr_create.shscripts/check_no_stdlib_logging.pyscripts/git-hooks/_run-hook.shscripts/git-hooks/commit-msgscripts/git-hooks/pre-commitscripts/git-hooks/pre-pushscripts/install_git_hooks.shscripts/run_affected_tests.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_check_no_stdlib_logging.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pytests/unit/scripts/test_run_affected_tests.pyweb/.gitignoreweb/package.jsonweb/public/mockServiceWorker.js
💤 Files with no reviewable changes (11)
- .claude/hookify.function-length.md
- .claude/hookify.no-future-annotations.md
- .claude/hookify.missing-logger.md
- opencode.json
- .claude/hookify.block-pr-create.md
- .claude/hookify.no-cd-prefix.md
- .claude/hookify.no-local-coverage.md
- .claude/hookify.enforce-parallel-tests.md
- .claude/hookify.pep758-except.md
- .claude/hookify.block-double-push.md
- web/public/mockServiceWorker.js
📜 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). (9)
- GitHub Check: Lighthouse Site
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Test E2E
- GitHub Check: Test Unit
- GitHub Check: Test Integration
- GitHub Check: Dashboard Test
- GitHub Check: Build Web Assets (melange)
- GitHub Check: CodSpeed Web benchmarks
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (10)
!(src/synthorg/persistence/**)
📄 CodeRabbit inference engine (CLAUDE.md)
Only
src/synthorg/persistence/may import sqlite/psycopg or emit raw SQL
Files:
web/.gitignorescripts/git-hooks/pre-pushscripts/git-hooks/pre-commit.gitattributesscripts/install_git_hooks.shscripts/check_no_pr_create.shweb/package.jsonscripts/check_no_cd_prefix.shscripts/git-hooks/commit-msgdocs/getting_started.mdscripts/git-hooks/_run-hook.shdata/runtime_stats.yamlscripts/check_no_local_coverage.sh.claude/settings.jsondocs/architecture/decisions.mdscripts/check_image_signatures.pyscripts/check_enforce_parallel_tests.shscripts/check_no_stdlib_logging.pytests/unit/scripts/test_check_no_stdlib_logging.py.opencode/plugins/synthorg-hooks.tsCLAUDE.md.pre-commit-config.yamltests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pyscripts/run_affected_tests.pydocs/reference/convention-gates.mdscripts/check_no_bulk_edit.pytests/unit/scripts/test_run_affected_tests.py
scripts/**/*.sh
📄 CodeRabbit inference engine (CLAUDE.md)
GitHub queries: use
gh issue listvia Bash, NOT MCPlist_issues
Files:
scripts/install_git_hooks.shscripts/check_no_pr_create.shscripts/check_no_cd_prefix.shscripts/git-hooks/_run-hook.shscripts/check_no_local_coverage.shscripts/check_enforce_parallel_tests.sh
{README.md,docs/**/*.md,data/runtime_stats.yaml}
📄 CodeRabbit inference engine (CLAUDE.md)
Source numerics in README + public docs from
data/runtime_stats.yamlvia<!--RS:NAME-->markers
Files:
docs/getting_started.mddata/runtime_stats.yamldocs/architecture/decisions.mddocs/reference/convention-gates.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Use
d2for architecture/nested containers,mermaidfor flowcharts/sequence/pipelines; Markdown tables for tabular data
Files:
docs/getting_started.mddocs/architecture/decisions.mddocs/reference/convention-gates.md
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use Configuration Precedence: DB > env > code default via
SettingsService/ConfigResolver(Cat-1) or env > code default (Cat-2,read_only_post_init); Cat-3 bootstrap secrets are pure env; noos.environ.getoutside startupNo
from __future__ import annotations(3.14 has PEP 649); PEP 758 except:except A, B:no parens unless binding
Files:
scripts/check_image_signatures.pyscripts/check_no_stdlib_logging.pytests/unit/scripts/test_check_no_stdlib_logging.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pyscripts/run_affected_tests.pyscripts/check_no_bulk_edit.pytests/unit/scripts/test_run_affected_tests.py
**/*.{py,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Comments WHY only; no reviewer citations / issue back-refs / migration framing; enforced by
check_no_review_origin_in_code.py+check_no_migration_framing.py
Files:
scripts/check_image_signatures.pyscripts/check_no_stdlib_logging.pytests/unit/scripts/test_check_no_stdlib_logging.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pyscripts/run_affected_tests.pyscripts/check_no_bulk_edit.pytests/unit/scripts/test_run_affected_tests.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Test markers:
@pytest.mark.{unit,integration,e2e,slow}; asyncauto; timeout 30s global; coverage 80% minWindows: unit tests use
WindowsSelectorEventLoopPolicy(3.14 IOCP teardown race); subprocess tests override backTest doubles: use
FakeClockfor Clock seam,mock_of[T](**overrides)for typed-boundary substitutions,SimpleNamespacefor attribute-bags; bareMagicMockat typed boundaries forbidden; enforced byscripts/check_mock_spec.pyHypothesis: 10 deterministic CI examples; failures are real bugs (fix + add
@example(...))Flaky tests: NEVER skip/xfail; fix fundamentally; use
asyncio.Event().wait()notsleep(large)
Files:
tests/unit/scripts/test_check_no_stdlib_logging.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pytests/unit/scripts/test_run_affected_tests.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/scripts/test_check_no_stdlib_logging.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pytests/unit/scripts/test_run_affected_tests.py
{src/**/*.py,tests/**/*.py}
📄 CodeRabbit inference engine (CLAUDE.md)
Vendor-agnostic: NEVER use real vendor names in project code/tests; use
example-provider,test-provider,example-{large,medium,small}-001
Files:
tests/unit/scripts/test_check_no_stdlib_logging.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pytests/unit/scripts/test_run_affected_tests.py
{,.opencode/plugins/}synthorg-hooks.ts
📄 CodeRabbit inference engine (AGENTS.md)
synthorg-hooks.ts plugin uses PreToolUse/PostToolUse hooks that call the same validation scripts as Claude Code hooks
Files:
.opencode/plugins/synthorg-hooks.ts
.pre-commit-config.yaml
📄 CodeRabbit inference engine (CLAUDE.md)
Pre-commit/pre-push hooks:
.pre-commit-config.yaml; tool-call gates:.claude/settings.jsonPreToolUse
Files:
.pre-commit-config.yaml
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Read `docs/design/` page before implementing; deviations need approval per DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Use no region/currency/locale privileges; use metric units; use British English
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Every convention PR ships its enforcement gate
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: After issue: branch + commit + push (no auto-PR); use `/pre-pr-review`; after PR: use `/aurelio-review-pr` for external feedback; fix EVERYTHING valid
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Signed commits required on protected refs (GPG/SSH or GitHub App via `synthorg-repo-bot`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Squash merge; PR body becomes squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: After every squash merge → `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Web configuration: see `web/CLAUDE.md`; CLI: see `cli/CLAUDE.md` (use `go -C cli`, never `cd cli`); Shell: see `~/.claude/rules/common/bash.md`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:26:08.777Z
Learning: Framework: Python 3.14+ for synthorg (src layout), BUSL-1.1 → Apache 2.0 after Change Date; React 19 for web; Go binary for CLI
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, do not require adding `<!--RS:...-->` “Doc Numeric Claims (MANDATORY)” numeric macros for Python version numbers mentioned in documentation prose (e.g., “Python 3.14”, “Python 3.15”). The `scripts/check_doc_numeric_macros.py` gate only applies to `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`, and it only flags digits adjacent to specific stat nouns (tests/providers/agents/stars/releases), not language version mentions like “Python 3.14”.
Applied to files:
docs/getting_started.mddocs/architecture/decisions.mdCLAUDE.mddocs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo, account for the CI gate `check_doc_numeric_macros.py`: it skips fenced code blocks entirely, and it only flags digits that are adjacent to these stat nouns: `tests`, `providers`, `agents`, `stars`, `releases`. Therefore, numeric examples such as CLI flag values (e.g., `--num-workers=4` in fenced bash blocks) and prose version numbers (e.g., `3.14`/`3.15`) are not expected to trigger this check; prioritize changes only when digits appear next to one of the listed nouns (e.g., “5 tests”, “10 stars”, etc.).
Applied to files:
docs/getting_started.mddocs/architecture/decisions.mdCLAUDE.mddocs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing markdown files for the "Doc Numeric Claims (MANDATORY)" RS-marker rule, only require/flag missing RS markers in the files that are actually in-scope for the rule. The scope is enforced via an identical _SCOPED_FILES allowlist in scripts/check_doc_numeric_macros.py and scripts/inject_runtime_stats.py, and currently includes: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. For any other markdown files (e.g., docs/getting_started.md, docs/guides/*), missing RS markers for numeric claims are no-ops and should NOT be flagged.
Applied to files:
docs/getting_started.mddocs/architecture/decisions.mddocs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo against the `check_doc_numeric_macros.py` gate, account for its documented behavior: it skips fenced code blocks entirely, and it only flags digits that are adjacent to specific stat nouns (`tests`, `providers`, `agents`, `stars`, `releases`). As a result, CLI-style numbers (e.g., `--num-workers=4`) inside fenced bash code blocks should never be treated as violations of this gate; only non-fenced text needs checking, and only around those specific nouns.
Applied to files:
docs/getting_started.mddocs/architecture/decisions.mddocs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:19.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/guides/contributing.md:95-95
Timestamp: 2026-05-16T18:36:19.195Z
Learning: In the SynthOrg repo, the “Doc Numeric Claims (MANDATORY)” RS-marker rule should be applied only to these docs: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. This rule is enforced by scripts/check_doc_numeric_macros.py (with runtime substitution by scripts/inject_runtime_stats.py), so reviewers should not flag similar numeric-claim issues in other paths (e.g., anything under docs/guides/). When checking those scoped files, the rule skips fenced code blocks and only flags digits that are adjacent to stat nouns (tests/providers/agents/stars/releases). Numeric CLI flags like “--num-workers=4” inside fenced bash code blocks are not subject to this rule.
Applied to files:
docs/architecture/decisions.mddocs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, follow the `Doc Numeric Claims (MANDATORY)` rule enforced by `scripts/check_doc_numeric_macros.py` only for these markdown files: `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`. The gate flags digits that appear adjacent to the stat nouns `tests`, `providers`, `agents`, `stars`, and `releases`—those numeric claims must use the required `<!--RS:...-->` macro format. Do not apply this rule to prose that mentions Python version numbers (e.g., “Python 3.14” / “Python 3.15”); those should not be flagged as requiring `<!--RS:...-->`.
Applied to files:
docs/architecture/decisions.mddocs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: In the synthorg repo, the “Doc Numeric Claims (MANDATORY)” RS-marker rule is enforced only for this exact set of Markdown files: README.md, docs/index.md, docs/roadmap/index.md, docs/architecture/decisions.md, and docs/reference/convention-gates.md. During code reviews, do not raise RS-marker/numeric-claims findings for numeric values in any other files (e.g., docs/getting_started.md, docs/guides/*, docs/reference/conventions.md), since they are not checked or injected by scripts/check_doc_numeric_macros.py or scripts/inject_runtime_stats.py.
Applied to files:
docs/architecture/decisions.mddocs/reference/convention-gates.md
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
scripts/check_image_signatures.pyscripts/check_no_stdlib_logging.pytests/unit/scripts/test_check_no_stdlib_logging.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_git_hooks_wrapper.pytests/unit/scripts/test_pretooluse_bash_gates.pyscripts/run_affected_tests.pyscripts/check_no_bulk_edit.pytests/unit/scripts/test_run_affected_tests.py
🪛 markdownlint-cli2 (0.22.1)
docs/reference/convention-gates.md
[warning] 71-71: Spaces inside code span elements
(MD038, no-space-in-code)
🔇 Additional comments (22)
docs/architecture/decisions.md (1)
139-168: LGTM!CLAUDE.md (1)
19-19: LGTM!Also applies to: 37-37, 107-107
.gitattributes (1)
28-34: LGTM!scripts/git-hooks/_run-hook.sh (1)
1-45: LGTM!scripts/git-hooks/commit-msg (1)
1-7: LGTM!scripts/git-hooks/pre-commit (1)
1-7: LGTM!scripts/git-hooks/pre-push (1)
1-7: LGTM!scripts/install_git_hooks.sh (1)
1-33: LGTM!scripts/check_no_bulk_edit.py (1)
1-90: LGTM!scripts/run_affected_tests.py (1)
72-79: LGTM!Also applies to: 104-122, 833-970
tests/unit/scripts/test_check_no_bulk_edit.py (1)
1-134: LGTM!tests/unit/scripts/test_check_no_stdlib_logging.py (1)
1-86: LGTM!tests/unit/scripts/test_git_hooks_wrapper.py (1)
1-143: LGTM!tests/unit/scripts/test_pretooluse_bash_gates.py (1)
1-211: LGTM!tests/unit/scripts/test_run_affected_tests.py (1)
16-16: LGTM!Also applies to: 42-46, 873-1029
scripts/check_no_pr_create.sh (1)
1-40: LGTM!scripts/check_no_cd_prefix.sh (1)
1-43: LGTM!scripts/check_no_local_coverage.sh (1)
1-47: LGTM!scripts/check_no_stdlib_logging.py (1)
1-129: LGTM!.claude/settings.json (1)
49-65: LGTM!.pre-commit-config.yaml (1)
212-224: LGTM!.opencode/plugins/synthorg-hooks.ts (1)
342-352: LGTM!
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1949 +/- ##
=======================================
Coverage 85.22% 85.22%
=======================================
Files 1846 1846
Lines 107867 107867
Branches 9298 9298
=======================================
+ Hits 91931 91933 +2
+ Misses 13677 13675 -2
Partials 2259 2259 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…t equals-assignment pytest flag forms in check_enforce_parallel_tests.sh, fail closed on non-object JSON in check_no_bulk_edit.py, restrict rename payload split to R/C porcelain status in run_affected_tests.py, WHY-only comment in synthorg-hooks.ts, clear markdownlint MD038 in convention-gates.md, dismiss CodeQL alert 296 as false positive, add test coverage
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/check_no_bulk_edit.py (1)
81-87:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHarden
tool_input/commandtyping to keep fail-closed behaviour explicit.
payloadis validated as a dict, buttool_inputandtool_input["command"]are not. A non-dicttool_input(or non-stringcommand) can raiseAttributeError/TypeError, producing a crash path instead of your explicit malformed-envelope denial flow.Suggested patch
- tool_name = payload.get("tool_name", "") - tool_input = payload.get("tool_input") or {} + tool_name = payload.get("tool_name", "") + tool_input_raw = payload.get("tool_input") + if tool_input_raw is None: + tool_input: dict[str, object] = {} + elif not isinstance(tool_input_raw, dict): + print( + "BLOCKED: malformed PreToolUse JSON envelope (tool_input must be object); " + "check_no_bulk_edit fails closed.", + file=sys.stderr, + ) + return 2 + else: + tool_input = tool_input_raw if tool_name == "Bash": - command = tool_input.get("command", "") or "" + command_raw = tool_input.get("command", "") + if command_raw is None: + command = "" + elif not isinstance(command_raw, str): + print( + "BLOCKED: malformed PreToolUse JSON envelope (tool_input.command must be string); " + "check_no_bulk_edit fails closed.", + file=sys.stderr, + ) + return 2 + else: + command = command_raw for pattern in _BULK_BASH_PATTERNS:🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/check_no_bulk_edit.py` around lines 81 - 87, The code currently assumes payload["tool_input"] is a dict and tool_input["command"] is a string, which can raise exceptions; inside the tool_name == "Bash" branch (where tool_input, command and _BULK_BASH_PATTERNS are used) explicitly validate types: ensure tool_input is a dict (fallback to {} if not) and coerce or validate command to a str (fallback to "" or treat as malformed and run the denial path) before iterating over _BULK_BASH_PATTERNS, so any non-dict/non-string input yields the same explicit fail-closed behavior rather than raising AttributeError/TypeError.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@scripts/check_no_bulk_edit.py`:
- Around line 81-87: The code currently assumes payload["tool_input"] is a dict
and tool_input["command"] is a string, which can raise exceptions; inside the
tool_name == "Bash" branch (where tool_input, command and _BULK_BASH_PATTERNS
are used) explicitly validate types: ensure tool_input is a dict (fallback to {}
if not) and coerce or validate command to a str (fallback to "" or treat as
malformed and run the denial path) before iterating over _BULK_BASH_PATTERNS, so
any non-dict/non-string input yields the same explicit fail-closed behavior
rather than raising AttributeError/TypeError.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: ac39ec62-d73e-4a92-a1ae-d156f2c8bb70
📒 Files selected for processing (7)
.opencode/plugins/synthorg-hooks.tsdocs/reference/convention-gates.mdscripts/check_enforce_parallel_tests.shscripts/check_no_bulk_edit.pyscripts/run_affected_tests.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_pretooluse_bash_gates.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: Build Web Assets (melange)
- GitHub Check: Test Unit
- GitHub Check: Test E2E
- GitHub Check: Test Integration
- GitHub Check: Dashboard Test
- GitHub Check: Lighthouse Site
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Use
d2for architecture / nested containers diagrams; usemermaidfor flowcharts / sequence / pipelinesUse Markdown tables for tabular data
Files:
docs/reference/convention-gates.md
{,.opencode/plugins/}synthorg-hooks.ts
📄 CodeRabbit inference engine (AGENTS.md)
synthorg-hooks.ts plugin uses PreToolUse/PostToolUse hooks that call the same validation scripts as Claude Code hooks
Files:
.opencode/plugins/synthorg-hooks.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use markers:
@pytest.mark.{unit,integration,e2e,slow}for testsAsync tests use
automode; timeout 30s global; coverage 80% minimumWindows: unit tests use
WindowsSelectorEventLoopPolicy(3.14 IOCP teardown race); subprocess tests override backTest doubles:
FakeClockfor Clock seam,mock_of[T](**overrides)for typed-boundary substitutions,SimpleNamespacefor attribute-bagsBare
MagicMockat a typed boundary (constructor / fn arg / annotated local / typed fixture return) is blocked byscripts/check_mock_spec.py(zero-tolerance, no baseline)FakeClock and
mock_ofimport fromtests._shared; inject viaclock=and the helper's spec subscriptHypothesis: 10 deterministic CI examples; failures are real bugs (fix + add
@example(...))Flaky tests: NEVER skip/xfail; fix fundamentally. Use
asyncio.Event().wait()notsleep(large)
Files:
tests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_pretooluse_bash_gates.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_pretooluse_bash_gates.py
{src/synthorg/**/*.py,tests/**/*.py}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use real vendor names in project code/tests; use
example-provider,test-provider,example-{large,medium,small}-001
Files:
tests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_pretooluse_bash_gates.py
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Read `docs/design/` page before implementing; deviations need approval. See DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Use no region/locale privilege; metric units; British English
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Every convention PR ships its enforcement gate
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: YAML is a company-template ingestion format, not a precedence tier
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Enforced by `scripts/check_no_magic_numbers.py`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Timeout/slow failures = source-code regression; test baseline files are PreToolUse-blocked
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Per-invocation bypass for gate baselines: `ALLOW_BASELINE_GROWTH=1 git commit ...` (requires explicit user approval)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: After issue: branch + commit + push (no auto-PR); use `/pre-pr-review` (`gh pr create` is blocked by `scripts/check_no_pr_create.sh`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: After PR: use `/aurelio-review-pr` for external feedback
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Fix EVERYTHING valid; no deferring
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Use `uv sync` for all dependencies; `uv sync --group docs` for docs toolchain
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Enforced by `check_no_review_origin_in_code.py` + `check_no_migration_framing.py`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Enforced by `check_domain_error_hierarchy.py`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Enforced by `check_boundary_typed.py`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Enforced by `check_logger_exception_str_exc.py`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Use xdist `-n 8 --dist=loadfile` (auto-applied via pyproject `addopts`; loadfile prevents 3.14+Windows ProactorEventLoop leak)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Real vendor names allowed in `.claude/`, third-party imports, `providers/presets.py`, `web/public/provider-logos/`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Enforced by `check_dual_backend_test_parity.py`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Git commit format: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Signed commits required on protected refs (GPG/SSH or GitHub App via `synthorg-repo-bot`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Branch naming: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Use pre-commit/pre-push hooks from `.pre-commit-config.yaml`; tool-call gates in `.claude/settings.json` PreToolUse
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: Squash merge PR body becomes squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: GitHub queries: use `gh issue list` via Bash, NOT MCP `list_issues`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: After every squash merge, use `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T22:50:14.600Z
Learning: D2 theme 200 (Dark Mauve), D2 CLI pinned to v0.7.1 in CI
📚 Learning: 2026-05-16T18:36:19.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/guides/contributing.md:95-95
Timestamp: 2026-05-16T18:36:19.195Z
Learning: In the SynthOrg repo, the “Doc Numeric Claims (MANDATORY)” RS-marker rule should be applied only to these docs: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. This rule is enforced by scripts/check_doc_numeric_macros.py (with runtime substitution by scripts/inject_runtime_stats.py), so reviewers should not flag similar numeric-claim issues in other paths (e.g., anything under docs/guides/). When checking those scoped files, the rule skips fenced code blocks and only flags digits that are adjacent to stat nouns (tests/providers/agents/stars/releases). Numeric CLI flags like “--num-workers=4” inside fenced bash code blocks are not subject to this rule.
Applied to files:
docs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, follow the `Doc Numeric Claims (MANDATORY)` rule enforced by `scripts/check_doc_numeric_macros.py` only for these markdown files: `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`. The gate flags digits that appear adjacent to the stat nouns `tests`, `providers`, `agents`, `stars`, and `releases`—those numeric claims must use the required `<!--RS:...-->` macro format. Do not apply this rule to prose that mentions Python version numbers (e.g., “Python 3.14” / “Python 3.15”); those should not be flagged as requiring `<!--RS:...-->`.
Applied to files:
docs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: In the synthorg repo, the “Doc Numeric Claims (MANDATORY)” RS-marker rule is enforced only for this exact set of Markdown files: README.md, docs/index.md, docs/roadmap/index.md, docs/architecture/decisions.md, and docs/reference/convention-gates.md. During code reviews, do not raise RS-marker/numeric-claims findings for numeric values in any other files (e.g., docs/getting_started.md, docs/guides/*, docs/reference/conventions.md), since they are not checked or injected by scripts/check_doc_numeric_macros.py or scripts/inject_runtime_stats.py.
Applied to files:
docs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, do not require adding `<!--RS:...-->` “Doc Numeric Claims (MANDATORY)” numeric macros for Python version numbers mentioned in documentation prose (e.g., “Python 3.14”, “Python 3.15”). The `scripts/check_doc_numeric_macros.py` gate only applies to `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`, and it only flags digits adjacent to specific stat nouns (tests/providers/agents/stars/releases), not language version mentions like “Python 3.14”.
Applied to files:
docs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo, account for the CI gate `check_doc_numeric_macros.py`: it skips fenced code blocks entirely, and it only flags digits that are adjacent to these stat nouns: `tests`, `providers`, `agents`, `stars`, `releases`. Therefore, numeric examples such as CLI flag values (e.g., `--num-workers=4` in fenced bash blocks) and prose version numbers (e.g., `3.14`/`3.15`) are not expected to trigger this check; prioritize changes only when digits appear next to one of the listed nouns (e.g., “5 tests”, “10 stars”, etc.).
Applied to files:
docs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing markdown files for the "Doc Numeric Claims (MANDATORY)" RS-marker rule, only require/flag missing RS markers in the files that are actually in-scope for the rule. The scope is enforced via an identical _SCOPED_FILES allowlist in scripts/check_doc_numeric_macros.py and scripts/inject_runtime_stats.py, and currently includes: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. For any other markdown files (e.g., docs/getting_started.md, docs/guides/*), missing RS markers for numeric claims are no-ops and should NOT be flagged.
Applied to files:
docs/reference/convention-gates.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo against the `check_doc_numeric_macros.py` gate, account for its documented behavior: it skips fenced code blocks entirely, and it only flags digits that are adjacent to specific stat nouns (`tests`, `providers`, `agents`, `stars`, `releases`). As a result, CLI-style numbers (e.g., `--num-workers=4`) inside fenced bash code blocks should never be treated as violations of this gate; only non-fenced text needs checking, and only around those specific nouns.
Applied to files:
docs/reference/convention-gates.md
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
scripts/run_affected_tests.pytests/unit/scripts/test_check_no_bulk_edit.pytests/unit/scripts/test_pretooluse_bash_gates.pyscripts/check_no_bulk_edit.py
…t tool_input / non-string command in check_no_bulk_edit Bash path (extracted _scan_bash helper), add coverage
<!-- HIGHLIGHTS_START --> ## Highlights > _AI-generated summary (model: `openai/gpt-4.1-mini` via GitHub Models). Commit-based changelog below._ ### What you'll notice - Frontend WP-6 update with UX polish improves user interface and workflow. - Dashboard and training endpoint improvements enhance observability and dispatch behavior. - Web storybook now supports change detection for more responsive UI interactions. - Git hooks now isolated per worktree for cleaner repository management. - Providers automatically detect native streaming support in Litellm models. ### What's new - Added a new pipeline to convert Pydantic DTOs to TypeScript for better front-end compatibility. ### Under the hood - Refactored settings to three precedence categories, removing YAML tier for simpler configuration. - Completed RootConfig mirror coverage for enhanced configuration consistency. - Adopted API conventions with better query performance and forbidden extra fields for stricter validation. - Improved persistence, layer discipline, and restart safety in core work packages. - CI updated with split test jobs and tightened coverage gates for better test quality. - Switched to direct Trivy binary for security scans, removing previous Trivy action dependency. - Enhanced memory management with per-call processing options and better observability during speech-to-text encoding. - Various dependency updates for Python, infrastructure, and lock files maintain security and stability. - Removed TypeScript DTO type-tightening overlays to simplify type management. - Codebase audit tightened skill sets to prevent false positivity in class detection by 2026. <!-- HIGHLIGHTS_END --> :robot: I have created a release *beep* *boop* --- ## [0.8.5](v0.8.4...v0.8.5) (2026-05-17) ### Features * **codegen:** pydantic-to-typescript DTO pipeline + parity gate (closes [#1889](#1889)) ([#1909](#1909)) ([0265ef5](0265ef5)) * **storybook:** enable changeDetection + trim web/CLAUDE.md ([#1939](#1939)) ([3b1f4c0](3b1f4c0)) * **web,setup:** WP-6 frontend + UX polish ([#1941](#1941)) ([d9ca76d](d9ca76d)) ### Bug Fixes * correct invalid git for-each-ref syntax in post-merge-cleanup skill ([#1946](#1946)) ([69a1649](69a1649)) * dashboard polish, training endpoint dispatch, and observability cleanup ([#1911](#1911)) ([b61e9e8](b61e9e8)) * per-worktree git-hook isolation + hookify gate migration + MSW drift fix ([#1949](#1949)) ([e3f8495](e3f8495)) * **providers:** read supports_native_streaming from litellm model info ([#1942](#1942)) ([60364ca](60364ca)) * security and audit coverage (closes [#1883](#1883)) ([#1904](#1904)) ([d8ebf55](d8ebf55)) ### Performance * **ci:** mypy --num-workers=4 + enable ruff TID255 ([#1944](#1944)) ([484c1d3](484c1d3)) ### Refactoring * **ci:** drop aquasecurity/trivy-action, use direct trivy binary ([#1940](#1940)) ([df1f946](df1f946)) * **memory:** per-call processing_kwargs + observability for ST encode ([#1943](#1943)) ([3aa9d20](3aa9d20)) * Phase 7 follow-up — complete RootConfig mirror coverage (closes [#1907](#1907)) ([#1914](#1914)) ([605500b](605500b)) * **settings:** collapse precedence to three categories; drop YAML tier (closes [#1890](#1890)) ([#1910](#1910)) ([efd54c9](efd54c9)) * WP-3 API conventions + query performance + project-wide extra=forbid ([#1953](#1953)) ([504d579](504d579)), closes [#1918](#1918) * WP-4 settings + cross-cutting (clock seam, contextvars, dispatch, plugin surfaces) ([#1954](#1954)) ([7207d92](7207d92)) * **wp1:** persistence + layer discipline + restart safety ([#1945](#1945)) ([57586fb](57586fb)) ### Documentation * **wp5:** public-facing truth refresh ([#1924](#1924)) ([afb5cc5](afb5cc5)) ### CI/CD * split test job by marker with airtight aggregate coverage gate ([#1948](#1948)) ([0b818d5](0b818d5)), closes [#1938](#1938) [#1937](#1937) ### Maintenance * **codebase-audit:** tighten skill to prevent 2026-05-15 FP classes ([#1923](#1923)) ([9317ed1](9317ed1)) * Lock file maintenance ([#1913](#1913)) ([c08a355](c08a355)) * Lock file maintenance ([#1950](#1950)) ([8940ab1](8940ab1)) * remove TS DTO type-tightening overlays ([#1915](#1915)) ([d296214](d296214)), closes [#1906](#1906) * Update Infrastructure dependencies ([#1928](#1928)) ([d19fae5](d19fae5)) * Update Python dependencies ([#1929](#1929)) ([75cc2c8](75cc2c8)) * **wp7:** hygiene, stubs, test/CI/tooling, doc gaps, boundary patterns doc ([#1926](#1926)) ([c29eb32](c29eb32)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: synthorg-repo-bot[bot] <279117679+synthorg-repo-bot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes #1947
Summary
scripts/git-hooks/{_run-hook.sh,pre-commit,pre-push,commit-msg}+ relativecore.hooksPath=scripts/git-hooks(set byscripts/install_git_hooks.sh). Each worktree resolves its own venv at runtime viagit rev-parse --show-toplevel;UV_FROZEN=1/--frozenso no hook can rewriteuv.lock. Noextensions.worktreeConfig, no per-worktreepre-commit install, deletion-safe by construction. Verified empirically: relativecore.hooksPathresolves per-worktree on Git-for-Windows + linked worktrees..gitattributespins the extensionless wrappers to LF.pytest-unit"files were modified": structural fix (UV_FROZEN) + a pre/postgit statusreconcile guard inscripts/run_affected_tests.pythat reverts only run-induced tracked changes (never the developer's work), fails closed if post-run status is unreadable.long-running-loopsfailure: root-caused as collateral of the shared-hooks band-aid +uv.lockchurn (gate is read-only, passes clean on the rebased branch); structurally fixed by the per-worktree venv. Interpreter+frozen invariant pinned by a wrapper contract test..claude/settings.jsonPreToolUse:check_no_pr_create.sh,check_no_cd_prefix.sh,check_no_local_coverage.sh,check_enforce_parallel_tests.sh;.pre-commit-config.yaml:check_no_stdlib_logging.py), 9 inert.claude/hookify.*.mddeleted, decision recorded indocs/architecture/decisions.md.pep758-except/function-lengthclassified advisory (902 pre-existing / ruffPLR0915proxy).enforce-parallel-testsrewritten to correct intent (no explicit-n;-n0only for a single::test; benchmarks exempt) since the literal rule would have blocked the documentedpytest -m unit.web/public/mockServiceWorker.jsgitignored + regenerated by a guardedwebpostinstall. CI drift-guard out of scope (owned by CI: split test job by type (unit / integration / e2e) for faster signal #1938)..opencode/plugins/synthorg-hooks.ts(replaced drifted inline regexes withrunHookScript), removed deleted-hookify refs fromopencode.json.Test plan
tests/unit/scripts/test_git_hooks_wrapper.py(pins the pre-commithook-implargv contract),test_pretooluse_bash_gates.py,test_check_no_stdlib_logging.py,test_check_no_bulk_edit.py, plus reconcile-guard + fail-closed + malformed-envelope cases intest_run_affected_tests.py. 119 gate tests green.uv run ruff check src/ tests/ scripts/,ruff format --check,mypy(changed) clean; fulltests/unit/scripts/(1480) pass (2 Windows symlink-privilege skips).long-running loops,convention-gate inventory,no-stdlib-logging) passed green with no "files were modified by this hook".Review coverage
/pre-pr-review: 10 agents. issue-resolution-verifier confirmed all 5 acceptance criteria RESOLVED. 8 actionable findings (OpenCode parity + fail-closed hardening) implemented with test coverage; code-simplifier's one PCRE-in-grep -Eregression caught empirically and reverted. Triage:_audit/pre-pr-review/triage.md.