docs: add OpenCode parity setup and hookify rule documentation#1095
docs: add OpenCode parity setup and hookify rule documentation#1095
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (11)
WalkthroughAdds numerous OpenCode agent specification files covering reviews (code, security, tests, Go, Python, frontend, infra, docs, logging, persistence, resilience, concurrency, type design, design tokens, etc.) and a new 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
There was a problem hiding this comment.
Code Review
This pull request establishes dual-tool compatibility between Claude Code and OpenCode by introducing a shared-source architecture. Key additions include 21 specialized review agents, custom command wrappers, and OpenCode plugins for memory management and hook mirroring. New validation scripts and "hookify" rules enforce project standards such as parallel testing and restricted bash file writes. Feedback focuses on improving the SynthOrgHooks plugin by handling cases where validation scripts are missing and ensuring actual error messages are logged when design system checks fail.
| execSync( | ||
| `python scripts/check_web_design_system.py`, | ||
| { timeout: 10000, encoding: "utf-8" }, | ||
| ); |
.opencode/plugins/synthorg-hooks.ts
Outdated
| } catch { | ||
| // Log but don't block -- PostToolUse is advisory | ||
| console.error("Design system check failed for:", output.args.file_path); | ||
| } |
There was a problem hiding this comment.
Pull request overview
This PR adds OpenCode parity documentation/config and introduces an OpenCode hooks plugin intended to mirror Claude Code validations (plus documents new hookify rules).
Changes:
- Add OpenCode configuration/docs (
opencode.json,AGENTS.md,.opencode/commands/*,.opencode/agents/*). - Add OpenCode plugins for hook enforcement and shared memory (
.opencode/plugins/*.ts). - Add new validation hook scripts and document hookify rules (
scripts/*.sh,CLAUDE.md,.claude/hookify.*.md).
Reviewed changes
Copilot reviewed 44 out of 45 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/check_git_c_cwd.sh | New validation hook to block pointless git -C usage. |
| scripts/check_bash_no_write.sh | New validation hook to block file writes via Bash. |
| opencode.json | New OpenCode root config (provider/models + instruction files list). |
| CLAUDE.md | Documents new validation hooks and hookify rules. |
| AGENTS.md | New OpenCode-facing agent/command/plugin documentation. |
| .opencode/plugins/synthorg-hooks.ts | Implements hookify rule enforcement + runs validation scripts from OpenCode. |
| .opencode/plugins/memory.ts | Adds an OpenCode memory persistence plugin. |
| .opencode/commands/*.md | Adds OpenCode command wrappers for existing skills. |
| .opencode/agents/*.md | Adds OpenCode agent definitions mirroring Claude agent prompts. |
| .gitignore | Ignores .opencode/plugins/node_modules/. |
| .claude/skills/*/SKILL.md | Adds tool-parity-checker agent trigger conditions. |
| .claude/hookify.*.md | Adds 4 new hookify rule docs. |
| .claude/agents/tool-parity-checker.md | Adds parity-checker agent prompt. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.opencode/plugins/synthorg-hooks.ts
Outdated
| try { | ||
| const input = JSON.stringify({ tool_input: toolInput }); | ||
| const result = execSync(`echo '${input.replace(/'/g, "\\'")}' | bash ${scriptPath}`, { | ||
| timeout: timeoutMs, | ||
| encoding: "utf-8", | ||
| stdio: ["pipe", "pipe", "pipe"], | ||
| }); | ||
| return result; |
There was a problem hiding this comment.
runHookScript builds a shell command using echo '…' | bash … and escapes single quotes as \', but in POSIX shells a single quote cannot be escaped inside single-quoted strings. Any tool input containing ' (common in shell commands) will break the hook runner and may skip intended blocking. Prefer invoking bash with stdin passed via execSync/execFileSync input (no shell), or use spawn/execFile with args and input to avoid quoting issues entirely.
| * Mirrors the Claude Code hooks defined in .claude/settings.json and | ||
| * .claude/settings.local.json by calling the same shell scripts. | ||
| * | ||
| * Claude Code hooks: | ||
| * PreToolUse (Bash): scripts/check_push_rebased.sh | ||
| * PreToolUse (Bash): scripts/check_bash_no_write.sh | ||
| * PreToolUse (Bash): scripts/check_git_c_cwd.sh | ||
| * PostToolUse (Edit|Write): scripts/check_web_design_system.py | ||
| * |
There was a problem hiding this comment.
The header comment claims Claude Code PreToolUse hooks include check_bash_no_write.sh and check_git_c_cwd.sh, but .claude/settings.json currently only references scripts/check_push_rebased.sh (and PostToolUse runs check_web_design_system.py). This makes the plugin documentation misleading and complicates parity checks; update the comment to reflect the actual committed hooks (or update Claude Code settings if these scripts are intended to run there).
| if [[ "$NORM_ARG" == "$NORM_PWD" ]]; then | ||
| echo '{"decision":"block","reason":"BLOCKED: git -C points to the current working directory. Just use git directly -- the Bash tool already runs in the project root."}' | ||
| fi |
There was a problem hiding this comment.
This script blocks by printing { "decision": "block", ... } but exits 0 and doesn't emit the hookSpecificOutput.permissionDecision=deny JSON shape used by the other PreToolUse hooks (and expected by Claude Code hooks). If/when this is wired into .claude/settings*.json, it will silently allow the action. Align it with the other hook scripts: emit hookSpecificOutput JSON and exit 2 on deny.
scripts/check_bash_no_write.sh
Outdated
| # Output redirection to a file: > file, >> file, > /path, > "./path" | ||
| # Skip >/dev/null, >&2, etc. | ||
| if printf '%s\n' "$COMMAND" | grep -qE '>\s*"?(/[^d>]|\.\.?/|[a-zA-Z]:\\)'; then | ||
| deny "Do not use shell redirects (> or >>) to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification." | ||
| fi |
There was a problem hiding this comment.
The redirect detection regex intends to allow /dev/null, but (/[^d>]) accidentally exempts any absolute path starting with /d (e.g., /docs, /data) from being blocked. This creates a clear bypass for file writes and doesn't match the script's stated purpose. Tighten the allowlist to only /dev/null (and similar) instead of excluding all /d* paths.
scripts/check_bash_no_write.sh
Outdated
| # Output redirection to a file: > file, >> file, > /path, > "./path" | ||
| # Skip >/dev/null, >&2, etc. | ||
| if printf '%s\n' "$COMMAND" | grep -qE '>\s*"?(/[^d>]|\.\.?/|[a-zA-Z]:\\)'; then | ||
| deny "Do not use shell redirects (> or >>) to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification." | ||
| fi | ||
|
|
||
| # echo/printf > filename.ext (catches echo "text" > file.txt) | ||
| if printf '%s\n' "$COMMAND" | grep -qE '\b(echo|printf)\b.*>\s*\S+\.\S+'; then | ||
| deny "Do not use echo/printf with redirects to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification." | ||
| fi |
There was a problem hiding this comment.
Output-redirection checks only match absolute paths, .//../, or Windows drive paths. Common writes like cat > file.txt or echo hi > file in the current directory (no leading ./) are not blocked, which contradicts the stated goal of preventing file writes via Bash. Consider blocking any >/>> redirection to a non-FD/non-/dev/null target (with a small allowlist), regardless of whether the path is relative.
| // check_push_rebased.sh -- block push if branch is behind main | ||
| if (command.includes("git push")) { | ||
| const result = runHookScript( | ||
| "scripts/check_push_rebased.sh", | ||
| { command }, | ||
| 15000, | ||
| ); | ||
| if (result && result.includes("block")) { | ||
| throw new Error(result); | ||
| } | ||
| } |
There was a problem hiding this comment.
The check_push_rebased.sh hook denies by emitting JSON with "permissionDecision": "deny" and exiting 2, but the plugin only blocks when the returned stdout includes the substring "block". As written, rebased-check failures will not throw and git push will be allowed. Parse the hook JSON and enforce on permissionDecision === "deny" (or at least check for "deny" consistently).
| ## OpenCode Agents | ||
|
|
||
| The project defines 21 review agents for automated code review, organized in a 2-tier model routing architecture: | ||
| - **Quality tier** (Sonnet 4.5): code-reviewer, python-reviewer, frontend-reviewer, go-reviewer, conventions-enforcer, logging-audit, resilience-audit, api-contract-drift | ||
| - **Parallel tier** (Haiku 4.5): async-concurrency-reviewer, comment-analyzer, design-token-audit, docs-consistency, frontend-reviewer, go-conventions-enforcer, go-security-reviewer, infra-reviewer, issue-resolution-verifier, persistence-reviewer, pr-test-analyzer, security-reviewer, silent-failure-hunter, test-quality-reviewer, type-design-analyzer | ||
|
|
||
| Agents verify: correctness, security, type design, documentation consistency, API contracts, logging practices, resilience patterns, infrastructure, test quality, and frontend design tokens. |
There was a problem hiding this comment.
The document states "21 review agents" but the two tier lists include 8 (quality) + 15 (parallel) entries, with frontend-reviewer duplicated across tiers. As written, the count doesn't match (unique agents appear to be 22). Either fix the stated number or adjust the lists so they reflect the actual agent inventory.
| | Skill references this `subagent_type` | Use this OpenCode agent instead | | ||
| |---|---| | ||
| | `pr-review-toolkit:code-reviewer` | `.opencode/agents/code-reviewer.md` | | ||
| | `everything-claude-code:python-reviewer` | `.opencode/agents/python-reviewer.md` | | ||
| | `pr-review-toolkit:pr-test-analyzer` | `.opencode/agents/pr-test-analyzer.md` | | ||
| | `pr-review-toolkit:silent-failure-hunter` | `.opencode/agents/silent-failure-hunter.md` | | ||
| | `pr-review-toolkit:comment-analyzer` | `.opencode/agents/comment-analyzer.md` | | ||
| | `pr-review-toolkit:type-design-analyzer` | `.opencode/agents/type-design-analyzer.md` | | ||
| | `everything-claude-code:security-reviewer` | `.opencode/agents/security-reviewer.md` | | ||
| | `everything-claude-code:database-reviewer` | `.opencode/agents/persistence-reviewer.md` | | ||
| | `everything-claude-code:go-reviewer` | `.opencode/agents/go-reviewer.md` | | ||
| | `pr-review-toolkit:code-simplifier` | `.opencode/agents/code-reviewer.md` (use code-reviewer with simplification focus) | | ||
| | `.claude/agents/design-token-audit.md` | `.opencode/agents/design-token-audit.md` | | ||
| | `.claude/agents/tool-parity-checker.md` | `.opencode/agents/tool-parity-checker.md` (read from `.claude/agents/`) | | ||
|
|
There was a problem hiding this comment.
The markdown table rows start with || (empty first column), which doesn't match the 2-column header and will render incorrectly. Use a single leading | per row so each row has exactly two cells.
| | Skill references this `subagent_type` | Use this OpenCode agent instead | | ||
| |---|---| | ||
| | `pr-review-toolkit:code-reviewer` | `.opencode/agents/code-reviewer.md` | | ||
| | `everything-claude-code:python-reviewer` | `.opencode/agents/python-reviewer.md` | | ||
| | `pr-review-toolkit:pr-test-analyzer` | `.opencode/agents/pr-test-analyzer.md` | | ||
| | `pr-review-toolkit:silent-failure-hunter` | `.opencode/agents/silent-failure-hunter.md` | | ||
| | `pr-review-toolkit:comment-analyzer` | `.opencode/agents/comment-analyzer.md` | | ||
| | `pr-review-toolkit:type-design-analyzer` | `.opencode/agents/type-design-analyzer.md` | | ||
| | `everything-claude-code:security-reviewer` | `.opencode/agents/security-reviewer.md` | | ||
| | `everything-claude-code:database-reviewer` | `.opencode/agents/persistence-reviewer.md` | | ||
| | `everything-claude-code:go-reviewer` | `.opencode/agents/go-reviewer.md` | | ||
| | `.claude/agents/design-token-audit.md` | `.opencode/agents/design-token-audit.md` | | ||
| | `.claude/agents/tool-parity-checker.md` | `.opencode/agents/tool-parity-checker.md` (read from `.claude/agents/`) | | ||
|
|
There was a problem hiding this comment.
Same table formatting issue as pre-pr-review.md: rows begin with ||, which creates an extra empty column and breaks rendering for the 2-column table. Normalize the rows to start with a single |.
| | Skill references this | Use this OpenCode agent instead | | ||
| |---|---| | ||
| | `pr-review-toolkit:code-reviewer` | `.opencode/agents/code-reviewer.md` | | ||
| | `everything-claude-code:python-reviewer` | `.opencode/agents/python-reviewer.md` | | ||
| | `everything-claude-code:security-reviewer` | `.opencode/agents/security-reviewer.md` | | ||
| | `everything-claude-code:database-reviewer` | `.opencode/agents/persistence-reviewer.md` | | ||
| | `everything-claude-code:go-reviewer` | `.opencode/agents/go-reviewer.md` | | ||
|
|
There was a problem hiding this comment.
Same table formatting issue as other OpenCode adapter docs: the mapping table rows start with ||, which adds an unintended empty column and can render incorrectly. Update rows to use a single leading | so the table is consistently 2 columns.
There was a problem hiding this comment.
Actionable comments posted: 42
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/agents/tool-parity-checker.md:
- Around line 34-55: The parity checker only verifies file existence and exact
text; update it to also confirm plugin registration and more flexible doc
linking: when `.claude/settings.json` hooks are modified, after reading
`opencode/plugins/synthorg-hooks.ts` also open `opencode.json` and verify the
plugin is listed (plugin entry referencing the same module); when
`.claude/rules/common/*.md` or `.claude/hookify.*.md` changes, ensure the
project's `opencode.json` `instructions[]` array includes the file path (treat
pattern matches as valid) rather than only checking for file presence; change
the CLAUDE/AGENTS check to run when either `CLAUDE.md` or `AGENTS.md` is edited
and accept any reference or link to CLAUDE.md (not only the exact string "See
CLAUDE.md"); finally, when `.claude/settings.json` permission rules change,
continue to flag `opencode.json` permission advisories but do not block merge.
In @.claude/hookify.enforce-parallel-tests.md:
- Line 8: The regex in the pattern variable only matches "run pytest" and
"python -m pytest" and therefore misses bare "pytest" invocations; update the
pattern to also accept standalone pytest commands (e.g., include an alternative
for "\bpytest\b" or collapse to something like allowing an optional "run "
prefix so both "pytest ..." and "run pytest ..." are matched) so the rule
catches any direct pytest invocation; modify the pattern variable in the file to
add that alternative (e.g., add a branch for pytest or make "run " optional) to
ensure commands like "pytest tests/ -m unit" are detected.
In @.claude/hookify.no-cd-prefix.md:
- Line 5: The regex pattern '^cd\s+' allows leading whitespace to bypass the
rule; update the pattern to allow optional leading whitespace (e.g. change the
pattern to '^\s*cd\s+') so lines like " cd cli && go test ./..." are also
matched and blocked; adjust the pattern string in the file where the current
pattern '^cd\s+' is defined so the hook disallows any use of cd regardless of
indentation.
In @.opencode/agents/comment-analyzer.md:
- Around line 17-68: Add a blank line between each "### ..." heading (e.g., "###
1. Docstring Completeness", "### 2. Docstring Accuracy", etc.) and the bulleted
lists that follow them so each H3 is followed by an empty line, and ensure the
sample fence near the bottom (the triple-backtick block shown around the sample
report format) is surrounded by blank lines and includes a language identifier
(e.g., ```text) to satisfy MD022/MD031/MD040; update those specific headings and
the fenced block accordingly.
In @.opencode/agents/conventions-enforcer.md:
- Around line 44-46: Remove the duplicate rule "from __future__ import
annotations" from Section 6 (Imports) so it only appears once as the CRITICAL
convention in Section 3; edit the "### 6. Imports (MEDIUM)" block to delete the
line mentioning that future import and, if needed, reflow or renumber the list
items in Section 6 to keep formatting consistent while leaving the original
CRITICAL rule in Section 3 unchanged.
- Around line 62-67: The fenced code block in the example report format lacks a
language identifier (triggers MD040); update the example block in
conventions-enforcer.md by adding a language tag (e.g., change the opening "```"
to "```text" or "```markdown") for the snippet that starts with "[SEVERITY]
file:line -- Convention violated" so the linter is satisfied and rendering is
improved.
In @.opencode/agents/design-token-audit.md:
- Line 22: Update the Storybook exclusions in the design-token-audit skip list
so it also excludes `.stories.ts` files (not just `.stories.tsx`); locate the
skip entry that currently lists `.stories.tsx` in the Skip section of
design-token-audit.md and change it to include `.stories.ts` as well (or replace
with a glob pattern like `.stories.ts{,x}`) so TypeScript-only stories are not
flagged as false positives.
- Line 66: The file currently references a non-canonical page
`docs/design/brand-and-ux.md`; replace that reference with the canonical
design-spec path used by DESIGN_SPEC.md (e.g., `docs/design/index.md`) so the
agent points to the canonical design pages set (index, agents, organization,
communication, engine, memory, operations); update the string
`docs/design/brand-and-ux.md` to the appropriate canonical entry from
DESIGN_SPEC.md to keep links consistent.
In @.opencode/agents/go-conventions-enforcer.md:
- Line 17: The markdown has heading/spacing and fenced-code-block language
issues: ensure there is a blank line before and after each heading like "1.
Error Handling Conventions (HIGH)" and the other headings, and add an explicit
language tag to every fenced code block (e.g., ```go or ```bash) to resolve
MD022/MD031/MD040; update the sample fences under the mentioned headings
(including the ones referenced around the file: the blocks near the headings at
lines noted in the review) so each fence starts with a language token and each
heading is separated from surrounding content by a blank line.
In @.opencode/agents/go-reviewer.md:
- Around line 17-68: Add a blank line after each section heading (e.g., "1.
Error Handling (HIGH)", "2. Concurrency Safety (HIGH)", "3. Idiomatic Go
(MEDIUM)", "4. Resource Management (HIGH)", "5. Testing (MEDIUM)", "6.
Performance (LOW)") so lists directly follow a blank line, and update the
report-format fence to be a language-tagged fenced code block with blank lines
around it (use ```text ... ``` with an empty line before and after the fence) to
resolve MD022/MD031/MD040 in the reviewer template.
In @.opencode/agents/go-security-reviewer.md:
- Around line 17-73: Add missing blank lines around each top-level heading
(e.g., "### 1. Command Injection (HIGH)", "### 2. Path Traversal (HIGH)", "###
3. Secrets in Logs (HIGH)", "### 4. TLS/Network (MEDIUM)", "### 5. Container
Security (MEDIUM)", "### 6. Cryptographic Safety (MEDIUM)", "### 7. Input
Validation (HIGH)") so there is a blank line before and after each heading, and
update the report-format fenced block to declare a language (change ``` to
```text) and ensure the fenced block also has blank lines separating it from
surrounding content; this will resolve MD022, MD031, and MD040.
In @.opencode/agents/infra-reviewer.md:
- Around line 17-57: Fix Markdown lint issues by ensuring there's a blank line
before and after each heading in .opencode/agents/infra-reviewer.md and by
specifying an explicit fenced-code language for the report block (e.g., replace
the bare ``` fence with ```text). Locate the report-format fenced block and add
the language tag and surrounding blank lines, and apply the same
blank-line-around-heading and fenced-block-language fixes to the similar section
referenced (lines 73-78) so all headings and code fences comply with
markdownlint.
- Around line 58-63: Update the security hardening checklist in
.opencode/agents/infra-reviewer.md to include this repo's concrete controls: add
checks that verify presence and gating of Trivy and Grype scans (CI workflows
reference the Trivy/Grype steps and enforce failure on high/critical findings),
require repo CVE triage config files (.github/.trivyignore.yaml and
.github/.grype.yaml) and validate their existence, require cosign signing
configuration and that container images are signed in the Docker workflow, and
require SLSA provenance generation via actions/attest-build-provenance ensuring
SLSA L3 attestation; ensure the agent’s validation rules explicitly look for
these filenames/workflow steps and fail if missing so policy is
repository-specific rather than generic.
In @.opencode/agents/issue-resolution-verifier.md:
- Line 17: Fix MD022 by adding a blank line before and after each top-level or
subsection heading flagged in the file: ensure there is an empty line above and
below headings such as the ones at lines referenced (the repeated section
headings around lines 17, 22, 28, 34, 40, 45) so that each heading is separated
from surrounding text; update the markdown so every heading token (e.g., "# ..."
or "## ...") has a blank line both preceding and following it to silence the
markdownlint MD022 warnings.
In @.opencode/agents/persistence-reviewer.md:
- Around line 70-75: The fenced code block in the "Report Format" section is
missing a language specifier and surrounding blank lines; update that block to
use a fenced language (e.g., "text") and ensure there is a blank line before the
opening ``` and after the closing ``` so the block follows MD040 and MD031
rules—find the fenced block shown as "[SEVERITY] file:line -- Category..." and
add the language token and spacing accordingly.
- Around line 17-55: The Markdown headings "1. SQL Injection (HIGH)", "2.
Transaction Safety (HIGH)", "3. Repository Protocol (MEDIUM)", "4. Schema Design
(MEDIUM)", "5. Connection Management (HIGH)", "6. Data Integrity (HIGH)", and
"7. Query Efficiency (MEDIUM)" each need a blank line inserted immediately after
the `###` heading and before the following list to satisfy MD022; update the
.opencode/agents/persistence-reviewer.md file by adding a single empty line
after each of those headings so the lists are separated from their headings.
In @.opencode/agents/pr-test-analyzer.md:
- Around line 60-64: Add a clear note under the "Severity Levels" section that a
quantitative 80% minimum test coverage threshold is enforced in CI; state:
"Maintain 80% minimum test coverage (enforced in CI) — applies to files matching
src/synthorg/**/*.py" so the agent knows both the numeric requirement and the
file pattern it applies to.
In @.opencode/agents/resilience-audit.md:
- Around line 62-66: The "Severity Levels" list is missing the CRITICAL level
referenced earlier in Section 1; update the "Severity Levels" section to add a
CRITICAL entry (e.g., "- **CRITICAL**: System-wide failures, immediate action
required, retry logic must not be applied or must be handled specially") so the
definitions align with the usages in Section 1 and maintain consistent severity
semantics across the document.
- Around line 51-52: Section 7 "Timeout Handling" is empty; add the missing
checklist items or merge them into the "Soft Rules" section: update the
resilience-audit.md to either (a) populate "### 7. Timeout Handling (MEDIUM)"
with concrete checks (e.g., verify request/operation timeouts are set, confirm
retry/backoff uses timeout bounds, ensure long-running tasks have
cancellation/abort handling, and that sensible defaults and overrides exist) or
(b) remove the empty header and move those timeout checklist items under the
existing "Soft Rules" section; reference the "### 7. Timeout Handling (MEDIUM)"
heading when making the change so the content is added or the header is
removed/merged accordingly.
In @.opencode/agents/security-reviewer.md:
- Around line 56-61: Insert a blank line immediately before the existing code
fence that contains the snippet starting with "[SEVERITY] file:line --
Vulnerability class" and change the opening fence from ``` to ```text so the
block becomes a fenced code block with a language identifier; this addresses the
missing blank line and language identifier for the code block.
In @.opencode/agents/silent-failure-hunter.md:
- Around line 17-71: The markdown linter complaints are due to missing blank
lines before subsection headings (e.g., the "1. Silent Except Blocks (HIGH)",
"2. Overly Broad Catches (HIGH)", "3. Lost Error Context (MEDIUM)", "4.
Suppressed Async Errors (HIGH)", "5. Conditional Swallowing (MEDIUM)", "6.
Logging Without Action (MEDIUM)" headings) and a missing blank line and language
identifier for the fenced code block at the end; fix by inserting a blank line
before each subsection heading and a blank line immediately before the fenced
code block, and add a language tag (use "text") to the opening backticks of the
code fence that contains the report template.
In @.opencode/agents/test-quality-reviewer.md:
- Line 17: The markdown headings (e.g., "### 1. Test Isolation (HIGH)") and
fenced code blocks violate lint rules MD022/MD031/MD040; fix by ensuring a blank
line before and after each heading, adding blank lines immediately above and
below every fenced code block, and specifying the language after each opening
``` fence (for example ```bash or ```js) so code-fence language is present;
apply these same fixes to the other reported headings/code blocks referenced
(the additional occurrences noted in the review) to make the document
lint-clean.
- Around line 56-59: The vendor name whitelist in the "Vendor Names (HIGH)" rule
currently lists "example-large-001" but tests use "test-large-001", causing
false positives; update the allowed identifiers in
.opencode/agents/test-quality-reviewer.md so the list includes "test-provider",
"test-small-001", and "test-large-001" (replacing "example-large-001"), and run
the test that references the "test-large-001" model identifier to confirm the
rule no longer flags it.
In @.opencode/agents/type-design-analyzer.md:
- Around line 64-70: The fenced code block in
.opencode/agents/type-design-analyzer.md currently uses an unspecified language
marker (```) which triggers MD040; update the block opener to include a language
identifier (for example change the opening fence to ```text) so it reads ```text
followed by the existing lines and then the closing ```; ensure the content
between the fences remains unchanged and the file saves with the updated code
block marker.
In @.opencode/commands/codebase-audit.md:
- Around line 13-22: The agent mapping table is incomplete and will cause the
adapter to fail when the skill spawns agents (e.g., missing-logging,
event-constants, silent-errors, test-coverage, flaky-tests, wiring-lifecycle,
dead-code, todo-fixme, spec-drift, api-consistency, async-patterns,
immutability, missing-validation, type-hints, vendor-names, observability-gaps,
react-dashboard, go-cli, docker-infra, ci-workflows, docs-consistency,
landing-site, security-gaps, dependency-issues, docstring-gaps); update the
mapping so every agent declared in the SKILL.md has a corresponding entry in the
mapping table (or implement a clear fallback rule) by adding entries that point
each agent to the correct OpenCode agent file or adopting the same
explicit-plus-custom-prompts approach used in the pre-pr-review mapping so the
adapter can resolve all 25 agents at runtime.
In @.opencode/commands/post-merge-cleanup.md:
- Around line 5-7: Add a top-level H1 heading to the markdown content so the
first non-frontmatter line is an H1 (to satisfy markdownlint MD041); edit
.opencode/commands/post-merge-cleanup.md (the content that references
@.claude/skills/post-merge-cleanup/SKILL.md and the line "Arguments:
$ARGUMENTS") and insert a suitable H1 (e.g., "# Post-merge cleanup") above the
existing content so the file's first content line is an H1.
In @.opencode/commands/pre-pr-review.md:
- Around line 30-32: The README contradicts hookify: it tells OpenCode to use
the shell command "gh pr create" while the hook rule in
.claude/hookify.block-pr-create.md blocks the regex "gh\s+pr\s+create"; update
the PR creation guidance so it does not invoke that blocked pattern — either
document the limitation and instruct contributors to skip PR creation in
OpenCode (or use the existing mcp__github__create_pull_request flow where
configured) and explicitly reference the hookify block so users know why "gh pr
create" is prohibited.
In @.opencode/commands/research-link.md:
- Around line 1-7: The doc currently triggers markdownlint MD041 because the
first content line (the YAML frontmatter or subsequent content) is not an H1;
fix this by adding a top-level heading as the first non-frontmatter line (e.g.,
add "# Research link" immediately after the YAML frontmatter) in
.opencode/commands/research-link.md so the first content line is an H1; keep the
existing description, the reference to @.claude/skills/research-link/SKILL.md
and the "Arguments: $ARGUMENTS" block unchanged.
In @.opencode/commands/review-dep-pr.md:
- Around line 1-7: Add a top-level H1 immediately after the YAML frontmatter to
satisfy markdownlint MD041; specifically, insert a line like "# Review
Dependency PR" right after the frontmatter block (which currently contains
"description: Review dependency update PRs") and before the existing include
line "@.claude/skills/review-dep-pr/SKILL.md".
In @.opencode/commands/worktree.md:
- Line 5: Change the second-line heading "## OpenCode Adapter (read this FIRST,
before the skill below)" to a top-level H1 by replacing the leading "##" with
"#" so the first heading after the frontmatter is an H1 and markdownlint rule
MD041 is satisfied; locate the heading string "OpenCode Adapter (read this
FIRST, before the skill below)" and update its prefix accordingly.
- Around line 11-21: Update documentation and copying behavior: replace any
literal `cd <path> && claude` with a cwd-aware invocation of the CLI (e.g., use
`opencode-cli --cwd <path>` or the CLI's equivalent option) to avoid triggering
SynthOrgHooks' `no-cd-prefix` rule; also change all occurrences of "Claude Code
prompts/instances" and the `claude` command to "OpenCode prompts/instances" and
`opencode-cli`. When describing config copying in the worktree logic, add
copying of `opencode.json` from the project root and the `.opencode/` directory
(commands, agents, plugins) to the worktree root while continuing to copy the
existing `.claude/` files as the shared source of truth.
In @.opencode/plugins/memory.ts:
- Around line 19-25: The MEMORY_DIR constant currently hardcodes
"C--Users-Aurelio-synthorg"; change it to build the path dynamically (e.g.,
join(homedir(), ".claude", "projects", projectId, "memory")) where projectId is
derived at runtime from a stable project identifier such as the git repo root
basename (use git rev-parse --show-toplevel or fallback to
path.basename(process.cwd())), or from package.json.name or an
OPENCODE_MEMORY_PROJECT env var; update the MEMORY_DIR declaration to compute
projectId with that fallback logic so each clone/worktree gets its own correct
memory path.
- Around line 48-58: The frontmatter produced by buildFrontmatter writes
free-form MemoryEntry fields (name, description) unquoted which breaks YAML if
they contain colons, quotes, or newlines; update buildFrontmatter to
YAML-quote/escape these values before joining (e.g., wrap name and description
in double quotes and escape inner double quotes, backslashes and newlines) so
the frontmatter lines `name: ...` and `description: ...` always produce valid
YAML while leaving type and content unchanged.
- Around line 110-118: The delete_memory branch currently joins user-controlled
filename onto MEMORY_DIR and calls unlinkSync, allowing path traversal; validate
filename before deleting by rejecting any input containing path separators or
traversal (e.g., if path.basename(filename) !== filename or
filename.includes(path.sep) or filename.includes("..")) and/or resolve the final
path and ensure it is inside MEMORY_DIR (use path.resolve(MEMORY_DIR, filename)
and confirm it startsWith(path.resolve(MEMORY_DIR))). Only when the check passes
should you call unlinkSync(resolvedPath) and then call updateIndex("remove",
filename, "", ""); otherwise throw an error indicating an invalid filename.
In @.opencode/plugins/synthorg-hooks.ts:
- Around line 124-139: The PostToolUse hook in the after: async (input, output)
function currently swallows failures from running
`scripts/check_web_design_system.py` (execSync) for edits/writes under web/src/,
turning a mandatory check into a warning; change this so the hook runs on every
(input.tool === "edit" || "write") with typeof output.args?.file_path ===
"string" && output.args.file_path.includes("web/src/") and if execSync throws,
rethrow or propagate the error (do not catch and suppress) so the process fails
and callers (e.g., Claude's hook) cannot proceed until design-system violations
are fixed.
- Around line 62-86: The regex used to detect pytest invocations only matches
"run pytest" or "python -m pytest" and misses plain "pytest" calls; update both
checks that reference /(?:run\s+pytest|python\s+-m\s+pytest)\b/i (the -n 8 guard
and the --cov guard) to a regex that also matches plain "pytest", e.g.
/\b(?:pytest|python\s+-m\s+pytest)\b/i (or
/\bpytest\b|python\s+-m\s+pytest\b/i), so the conditions that throw Errors based
on command (the checks against the variable `command`) will catch invocations
like `pytest -q` and `pytest --cov` as intended.
- Around line 23-34: The runHookScript function currently composes a shell
command with JSON which allows bash to interpret the payload; change
runHookScript to use child_process.spawnSync (or execFileSync) and pass the JSON
string via the input/stdin option while invoking the scriptPath directly (no
shell interpolation) so the script receives raw JSON; also update the pytest
command regex (the pattern currently at the pytest check) to
/(?>|?:pytest|run\s+pytest|python\s+-m\s+pytest)\b/i or equivalent to match
plain "pytest" as well as the other forms; register this plugin by adding a
plugins section to opencode.json that references this synthorg-hooks plugin so
it is executed; and adjust the design-system check (the block catching errors
between lines 124–139) to either throw/reject on error instead of just logging
so the edit/write is blocked (or make the intended blocking behavior explicit).
In `@AGENTS.md`:
- Around line 48-50: The AGENTS.md section claims "21 review agents" but the
listed entries under "Quality tier" and "Parallel tier" sum to 23 entries and
include a duplicate "frontend-reviewer"; update the content to make the roster
consistent by either removing the duplicate entry or renaming/reassigning it and
then adjusting the total count to match (e.g., remove the second
"frontend-reviewer" from the Parallel tier or consolidate it and change "21" to
the correct total), ensuring the "Quality tier" and "Parallel tier" bullet lists
and the numeric summary all agree.
- Around line 33-45: The guidance in AGENTS.md currently instructs authors to
prefer PowerShell-specific syntax (e.g., "Select-String" over "grep") for skills
that are stored in the shared `.claude/skills/` directory; update the text to
avoid prescribing PowerShell for shared skills and instead state that shared
skills should be shell-neutral or bash-first, and any PowerShell translations
must be handled in the OpenCode adapter layer (e.g., `.opencode/commands`), so
replace the PowerShell-specific bullet about using `Select-String` with a short
note that shared skills should use POSIX/bash-compatible commands or neutral
constructs and that OpenCode will translate commands to PowerShell in its
adapter.
In `@scripts/check_bash_no_write.sh`:
- Line 12: The script currently silences jq parse errors causing COMMAND to
default to empty; update the logic around the COMMAND assignment (the jq
invocation) to detect jq failures and fail-closed: run jq without redirecting
stderr, capture its exit status, and if jq returns non-zero or the output is
null/empty when the input was expected, print a clear error and exit non-zero.
Specifically modify the COMMAND assignment and add a check after the jq call
that verifies the command was parsed successfully and aborts (non-zero exit)
when jq fails or returns malformed JSON.
In `@scripts/check_git_c_cwd.sh`:
- Around line 16-18: The current GIT_C_PATH extraction uses a sed regex anchored
to "git" and fails when flags precede -C; replace the brittle sed with a robust
token-based parse: split COMMAND into shell tokens (e.g., via read -r -a TOKENS
or a loop over words) and iterate tokens to find a token equal to "-C" and take
the next token (strip surrounding quotes), or handle "-Cpath" by matching token
prefix; update references to GIT_C_PATH accordingly so complex invocations like
"git --no-pager -C /path" or "git -c key=val -C '/my path'" are handled
correctly.
- Around line 34-36: The block condition currently only echoes a JSON denial
when NORM_ARG equals NORM_PWD but does not terminate with a non-zero exit, so
the hook will allow the command; update the if block in
scripts/check_git_c_cwd.sh (the branch that checks if [[ "$NORM_ARG" ==
"$NORM_PWD" ]]) to call exit 2 immediately after echoing the denial JSON (use
exit 2 as the denial signal referenced by .opencode/plugins/synthorg-hooks.ts)
so the script returns the correct denial status.
🪄 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: 3d7bf82c-0ee2-494b-a738-295a2c37f9cb
📒 Files selected for processing (45)
.claude/agents/tool-parity-checker.md.claude/hookify.block-pr-create.md.claude/hookify.enforce-parallel-tests.md.claude/hookify.no-cd-prefix.md.claude/hookify.no-local-coverage.md.claude/skills/aurelio-review-pr/SKILL.md.claude/skills/pre-pr-review/SKILL.md.gitignore.opencode/agents/api-contract-drift.md.opencode/agents/async-concurrency-reviewer.md.opencode/agents/code-reviewer.md.opencode/agents/comment-analyzer.md.opencode/agents/conventions-enforcer.md.opencode/agents/design-token-audit.md.opencode/agents/docs-consistency.md.opencode/agents/frontend-reviewer.md.opencode/agents/go-conventions-enforcer.md.opencode/agents/go-reviewer.md.opencode/agents/go-security-reviewer.md.opencode/agents/infra-reviewer.md.opencode/agents/issue-resolution-verifier.md.opencode/agents/logging-audit.md.opencode/agents/persistence-reviewer.md.opencode/agents/pr-test-analyzer.md.opencode/agents/python-reviewer.md.opencode/agents/resilience-audit.md.opencode/agents/security-reviewer.md.opencode/agents/silent-failure-hunter.md.opencode/agents/test-quality-reviewer.md.opencode/agents/type-design-analyzer.md.opencode/commands/analyse-logs.md.opencode/commands/aurelio-review-pr.md.opencode/commands/codebase-audit.md.opencode/commands/post-merge-cleanup.md.opencode/commands/pre-pr-review.md.opencode/commands/research-link.md.opencode/commands/review-dep-pr.md.opencode/commands/worktree.md.opencode/plugins/memory.ts.opencode/plugins/synthorg-hooks.tsAGENTS.mdCLAUDE.mdopencode.jsonscripts/check_bash_no_write.shscripts/check_git_c_cwd.sh
scripts/check_git_c_cwd.sh
Outdated
| # Extract path after -C (handles quoted and unquoted) | ||
| GIT_C_PATH=$(echo "$COMMAND" | sed -E 's/.*git[[:space:]]+-C[[:space:]]+("([^"]+)"|([^[:space:]]+)).*/\2\3/') | ||
| GIT_C_PATH="${GIT_C_PATH//\"/}" |
There was a problem hiding this comment.
Regex may fail on complex quoting or flags before -C.
The sed pattern assumes -C immediately follows git with optional whitespace. Commands like git --no-pager -C path or git -c key=value -C path won't be parsed correctly. Consider a more flexible extraction or document the limitation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/check_git_c_cwd.sh` around lines 16 - 18, The current GIT_C_PATH
extraction uses a sed regex anchored to "git" and fails when flags precede -C;
replace the brittle sed with a robust token-based parse: split COMMAND into
shell tokens (e.g., via read -r -a TOKENS or a loop over words) and iterate
tokens to find a token equal to "-C" and take the next token (strip surrounding
quotes), or handle "-Cpath" by matching token prefix; update references to
GIT_C_PATH accordingly so complex invocations like "git --no-pager -C /path" or
"git -c key=val -C '/my path'" are handled correctly.
| function runHookScript( | ||
| scriptPath: string, | ||
| toolInput: Record<string, unknown>, | ||
| timeoutMs: number = 10000, | ||
| ): string | null { | ||
| try { | ||
| const input = JSON.stringify({ tool_input: toolInput }); | ||
| const result = execSync(`echo '${input.replace(/'/g, "\\'")}' | bash ${scriptPath}`, { | ||
| timeout: timeoutMs, | ||
| encoding: "utf-8", | ||
| stdio: ["pipe", "pipe", "pipe"], | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json
import subprocess
command = "python -c \"print('x')\""
payload = json.dumps({"tool_input": {"command": command}})
shell_cmd = f"echo '{payload.replace(\"'\", \"\\\\'\")}' | cat >/dev/null"
result = subprocess.run(
["bash", "-lc", shell_cmd],
capture_output=True,
text=True,
)
print("shell_cmd:", shell_cmd)
print("returncode:", result.returncode)
print("stderr:", result.stderr.strip())
PYRepository: Aureliolo/synthorg
Length of output: 310
🏁 Script executed:
#!/bin/bash
# First, check if the file exists and read the relevant lines
fd -t f "synthorg-hooks.ts" | head -1 | xargs -r head -40Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
#!/bin/bash
# Corrected reproduction script for the shell quoting issue
# Using proper Python syntax without backslashes in f-string expressions
python3 << 'EOF'
import json
import subprocess
# Input containing a single quote that would break the original shell command
command = "python -c \"print('x')\""
payload = json.dumps({"tool_input": {"command": command}})
# Mimic the original approach: echo ... | bash
# The shell_cmd escapes single quotes using '\'' pattern
escaped_payload = payload.replace("'", "'\\''")
shell_cmd = f"echo '{escaped_payload}' | cat >/dev/null"
print("=== Testing Original Approach ===")
print("Payload:", payload)
print("Escaped payload:", escaped_payload)
print("Shell command:", shell_cmd)
result = subprocess.run(
["bash", "-c", shell_cmd],
capture_output=True,
text=True,
)
print("Return code:", result.returncode)
print("Stderr:", result.stderr.strip() if result.stderr else "(none)")
print()
# Show that the issue is specifically with the quoting
print("=== Direct bash parse test ===")
simple_test = f"echo '{escaped_payload}' | bash"
print("Test command:", simple_test)
result2 = subprocess.run(
["bash", "-c", simple_test],
capture_output=True,
text=True,
)
print("Return code:", result2.returncode)
print("Stderr:", result2.stderr.strip() if result2.stderr else "(none)")
EOFRepository: Aureliolo/synthorg
Length of output: 563
🏁 Script executed:
#!/bin/bash
# Find the synthorg-hooks.ts file
fd -t f "synthorg-hooks.ts"Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
#!/bin/bash
# Read the file to see the actual code
find . -name "synthorg-hooks.ts" -type f -exec cat -n {} \;Repository: Aureliolo/synthorg
Length of output: 6692
🏁 Script executed:
#!/bin/bash
# Check if opencode.json exists and show its structure
fd -t f "opencode.json" -exec head -50 {} \;Repository: Aureliolo/synthorg
Length of output: 292
🏁 Script executed:
#!/bin/bash
# Search for opencode.json in the repository
find . -name "opencode.json" -type fRepository: Aureliolo/synthorg
Length of output: 77
🏁 Script executed:
#!/bin/bash
# If opencode.json exists, check its contents
find . -name "opencode.json" -type f -exec cat {} \;Repository: Aureliolo/synthorg
Length of output: 2415
Remove shell command composition; pass JSON directly to script stdin.
The runHookScript function pipes JSON to bash, which fails because bash tries to interpret the JSON as shell commands instead of passing it to the target script. For example, with input {"tool_input": {"command": "python -c \"print('x'\""}}, bash exits with "command not found". Use spawnSync with the input option to write JSON directly to the script's stdin.
Additionally, register this plugin in opencode.json by adding a plugins section; currently, the entire plugin is inert and never executes.
The pytest enforcement at line 64 only matches run pytest and python -m pytest, allowing the plain pytest command to bypass the -n 8 check. Update the regex to include it: /(?:pytest|run\s+pytest|python\s+-m\s+pytest)\b/i.
The design-system check at lines 124–139 catches and logs errors instead of blocking, making it advisory only; clarify whether errors should actually prevent the edit/write operation.
🔧 Suggested fix for shell quoting
-import { execSync } from "child_process";
+import { execSync, spawnSync } from "child_process";
@@
function runHookScript(
scriptPath: string,
toolInput: Record<string, unknown>,
timeoutMs: number = 10000,
): string | null {
- try {
- const input = JSON.stringify({ tool_input: toolInput });
- const result = execSync(`echo '${input.replace(/'/g, "\\'")}' | bash ${scriptPath}`, {
- timeout: timeoutMs,
- encoding: "utf-8",
- stdio: ["pipe", "pipe", "pipe"],
- });
- return result;
- } catch (error: unknown) {
- const err = error as { status?: number; stdout?: string };
- if (err.status === 2) {
- // Hook denied the action
- return err.stdout ?? "Hook denied this action";
- }
- return null;
- }
+ const input = JSON.stringify({ tool_input: toolInput });
+ const result = spawnSync("bash", [scriptPath], {
+ input,
+ timeout: timeoutMs,
+ encoding: "utf-8",
+ stdio: ["pipe", "pipe", "pipe"],
+ });
+
+ if (result.status === 0) {
+ return result.stdout;
+ }
+ if (result.status === 2) {
+ return result.stdout || "Hook denied this action";
+ }
+ return null;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/plugins/synthorg-hooks.ts around lines 23 - 34, The runHookScript
function currently composes a shell command with JSON which allows bash to
interpret the payload; change runHookScript to use child_process.spawnSync (or
execFileSync) and pass the JSON string via the input/stdin option while invoking
the scriptPath directly (no shell interpolation) so the script receives raw
JSON; also update the pytest command regex (the pattern currently at the pytest
check) to /(?>|?:pytest|run\s+pytest|python\s+-m\s+pytest)\b/i or equivalent to
match plain "pytest" as well as the other forms; register this plugin by adding
a plugins section to opencode.json that references this synthorg-hooks plugin so
it is executed; and adjust the design-system check (the block catching errors
between lines 124–139) to either throw/reject on error instead of just logging
so the edit/write is blocked (or make the intended blocking behavior explicit).
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (6)
scripts/check_git_c_cwd.sh (1)
16-18: 🧹 Nitpick | 🔵 TrivialSed pattern may miss complex git invocations (known limitation).
The sed extraction assumes
-Cimmediately followsgit, so commands likegit --no-pager -C pathwon't be parsed correctly. Since the script fails open (exits 0 when it can't parse), this just means some redundantgit -C <cwd>commands may slip through rather than causing false blocks. Consider documenting this limitation in the comment or using a token-based parser if stricter enforcement is needed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check_git_c_cwd.sh` around lines 16 - 18, The sed extraction for GIT_C_PATH (using the COMMAND variable and the sed pattern 's/.*git[[:space:]]+-C[[:space:]]+("([^"]+)"|([^[:space:]]+)).*/\2\3/') can miss cases where options appear between git and -C (e.g., git --no-pager -C path); either document this known limitation in a comment above the GIT_C_PATH extraction or replace the fragile sed with a token-based parse of COMMAND that scans tokens for "git" then for a later "-C" (handling quoted and unquoted paths) and assigns the result to GIT_C_PATH; update the comment to mention the behavior if parsing fails (script intentionally fails open) and reference GIT_C_PATH and COMMAND so reviewers can find the code to modify..opencode/plugins/synthorg-hooks.ts (3)
134-151:⚠️ Potential issue | 🟠 MajorDesign system check should throw on failure, not just log.
The PostToolUse hook catches errors and only logs them (lines 146-150), making the design system validation advisory only. Per learnings: "Run the PostToolUse hook (
scripts/check_web_design_system.py) on every Edit/Write toweb/src/files and fix all violations before proceeding." This should be mandatory enforcement.🧱 Proposed fix to enforce design system rules
if ( (input.tool === "edit" || input.tool === "write") && typeof output.args?.file_path === "string" && output.args.file_path.includes("web/src/") ) { - try { - execSync( - `python scripts/check_web_design_system.py`, - { timeout: 10000, encoding: "utf-8" }, - ); - } catch (error: unknown) { - const err = error as { message?: string; stderr?: string }; - // Log but don't block -- PostToolUse is advisory - console.error("Design system check failed for:", output.args.file_path, "-", err.message || err.stderr || "Unknown error"); - } + execSync( + `python scripts/check_web_design_system.py`, + { timeout: 10000, encoding: "utf-8" }, + ); + // If execSync throws, the error propagates and blocks the operation }Based on learnings: "Run the PostToolUse hook (
scripts/check_web_design_system.py) on every Edit/Write toweb/src/files and fix all violations before proceeding".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/plugins/synthorg-hooks.ts around lines 134 - 151, The PostToolUse handler after: async (input, output) => { ... } currently catches failures from execSync(`python scripts/check_web_design_system.py`) and only logs them (console.error), making the check advisory; change the catch to re-throw the error (or throw a new Error with err.message/err.stderr and context including output.args.file_path and script name) so the PostToolUse hook fails on violations, ensuring edits/writes to paths matching output.args.file_path.includes("web/src/") cannot proceed until the design system script succeeds.
62-70:⚠️ Potential issue | 🟠 MajorPytest detection regex inconsistent with hookify rule.
The regex
/(?:run\s+pytest|python\s+-m\s+pytest)\b/ionly matchesrun pytestandpython -m pytest, missing plainpytestinvocations. Meanwhile, the hookify rule in.claude/hookify.enforce-parallel-tests.mduses(?:^|\s)(?:pytest|run\s+pytest|python\s+-m\s+pytest)\bwhich correctly includes barepytest.🐛 Proposed fix to align with hookify pattern
+ // Detect any pytest invocation + const isPytest = /(?:^|\s)(?:pytest|run\s+pytest|python\s+-m\s+pytest)\b/i.test(command); + // enforce-parallel-tests: enforce -n 8 with pytest - if ( - /(?:run\s+pytest|python\s+-m\s+pytest)\b/i.test(command) && - !/-n 8/.test(command) - ) { + if (isPytest && !/-n 8/.test(command)) { throw new Error( "Always use `-n 8` with pytest for parallel execution. Add `-n 8` to your pytest command. Never run tests sequentially or with `-n auto` (32 workers causes crashes and is slower due to contention).", ); } @@ // no-local-coverage: block --cov flags locally - if ( - /(?:run\s+pytest|python\s+-m\s+pytest)\b/i.test(command) && - /--cov\b/.test(command) - ) { + if (isPytest && /--cov\b/.test(command)) { throw new Error( "Do not run pytest with coverage locally -- CI handles it. Coverage adds 20-40% overhead. Remove `--cov`, `--cov-report`, and `--cov-fail-under` from your command.", ); }Also applies to: 79-87
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/plugins/synthorg-hooks.ts around lines 62 - 70, The pytest-detection regex in the synthorg-hooks.ts hook currently only matches "run pytest" and "python -m pytest" (/(:?run\s+pytest|python\s+-m\s+pytest)\b/i) and should be expanded to include bare "pytest" to match the hookify rule; replace that regex with something like /(?:^|\s)(?:pytest|run\s+pytest|python\s+-m\s+pytest)\b/i in the pytest-checking code path (the regex literal in the block that throws the "Always use `-n 8` with pytest..." Error), and make the identical replacement in the other occurrence noted (the second similar block around the other check).
23-44: 🧹 Nitpick | 🔵 TrivialShell command composition is fragile with embedded quotes.
The
runHookScriptfunction usesecho '${input.replace(...)}' | bashwhich can fail with JSON containing single quotes or special characters. The escape\\'doesn't properly handle all quote scenarios in shell context. Consider usingspawnSyncwith theinputoption to pipe JSON directly to the script's stdin without shell interpolation.♻️ Proposed fix using spawnSync
-import { execSync } from "child_process"; +import { execSync, spawnSync } from "child_process"; function runHookScript( scriptPath: string, toolInput: Record<string, unknown>, timeoutMs: number = 10000, ): string | null { - try { - const input = JSON.stringify({ tool_input: toolInput }); - const result = execSync(`echo '${input.replace(/'/g, "\\'")}' | bash ${scriptPath}`, { - timeout: timeoutMs, - encoding: "utf-8", - stdio: ["pipe", "pipe", "pipe"], - }); - return result; - } catch (error: unknown) { - const err = error as { status?: number; stdout?: string }; - if (err.status === 2) { - // Hook denied the action - return err.stdout ?? "Hook denied this action"; - } - return null; + const input = JSON.stringify({ tool_input: toolInput }); + const result = spawnSync("bash", [scriptPath], { + input, + timeout: timeoutMs, + encoding: "utf-8", + stdio: ["pipe", "pipe", "pipe"], + }); + + if (result.status === 0) { + return result.stdout; + } + if (result.status === 2) { + return result.stdout || "Hook denied this action"; } + return null; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/plugins/synthorg-hooks.ts around lines 23 - 44, The runHookScript function builds a fragile shell pipeline using execSync and echo which breaks on embedded quotes; replace execSync(`echo ... | bash ${scriptPath}`) with a direct child process invocation (spawnSync or execFileSync) that passes the JSON stringified input to the script's stdin via the input option, keep timeoutMs, encoding and stdio settings, and preserve the existing error handling logic for exit status 2 by inspecting the child result.status and result.stdout (or captured error) to return the hook denial message or null as before; update references to execSync in runHookScript accordingly..opencode/plugins/memory.ts (1)
48-59:⚠️ Potential issue | 🟠 MajorQuote frontmatter fields to prevent YAML injection.
nameanddescriptionare user-provided and written directly into YAML frontmatter. Values containing colons, quotes, or newlines will produce invalid YAML, making saved memories unreadable.🛡️ Proposed fix to escape YAML values
function buildFrontmatter(entry: MemoryEntry): string { + // Escape values for safe YAML: wrap in quotes and escape internal quotes/newlines + const escapeYaml = (s: string): string => + JSON.stringify(s); // JSON escaping is valid YAML for strings + return [ "---", - `name: ${entry.name}`, - `description: ${entry.description}`, + `name: ${escapeYaml(entry.name)}`, + `description: ${escapeYaml(entry.description)}`, `type: ${entry.type}`, "---", "", entry.content, "", ].join("\n"); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/plugins/memory.ts around lines 48 - 59, The buildFrontmatter function is writing user-provided MemoryEntry.name and MemoryEntry.description directly into YAML frontmatter, risking YAML injection; fix buildFrontmatter by YAML-quoting and escaping those fields before embedding them: for both name and description replace backslashes and double quotes with escaped versions, convert literal newlines to "\n" (or otherwise escape them), then wrap the resulting string in double quotes when inserting `name:` and `description:`; keep entry.type as-is (or validate if user-provided) so the frontmatter always produces valid YAML.scripts/check_bash_no_write.sh (1)
12-12:⚠️ Potential issue | 🟡 MinorConsider fail-closed behavior for malformed JSON input.
The
2>/dev/nullon jq silently converts parse failures to an empty string, allowing the script to exit 0 (allow). Malformed JSON inputs would bypass all write checks. If strict enforcement is desired, fail closed on parse errors.🛡️ Proposed fix to fail-closed on parse errors
-COMMAND=$(jq -r '.tool_input.command // ""' 2>/dev/null) +COMMAND=$(jq -r '.tool_input.command // ""') || { + echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Failed to parse tool input JSON"}}' >&2 + exit 2 +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check_bash_no_write.sh` at line 12, The current assignment COMMAND=$(jq -r '.tool_input.command // ""' 2>/dev/null) masks jq parse errors; change it to let jq report failures and fail-closed: remove the "2>/dev/null" so jq writes errors to stderr, then immediately check jq's exit status (or test COMMAND) and if jq failed (non-zero) write an error message and exit 1; reference the COMMAND variable and the jq invocation in scripts/check_bash_no_write.sh so malformed JSON causes the script to exit non-zero instead of silently allowing writes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/skills/aurelio-review-pr/SKILL.md:
- Line 166: The MD038 lint failure is caused by a trailing space inside the
inline code span containing `class `; edit the SKILL.md content to remove the
trailing space so the code span reads `class` (no trailing space) to satisfy the
no-space-in-code rule (MD038) — update the exact occurrence of the `` `class `
`` code span in the document (around the existing diff entry) to `` `class` ``.
- Around line 155-182: The agent table in SKILL.md incorrectly uses only the
generic "explore" subagent_type for every entry (e.g., docs-consistency,
tool-parity-checker, code-reviewer, python-reviewer, logging-audit,
resilience-audit, etc.), which breaks the adapter contract that expects a
concrete agent definition per row; update each table row so the subagent_type
includes the concrete agent reference (for example replace plain "explore" with
a form that ties the base agent name to the explore type such as "explore
(code-reviewer)" or "explore + code-reviewer" for standard agents, and for the
custom-prompt agents (docs-consistency, logging-audit, resilience-audit, etc.)
indicate "explore (base-agent-name) + custom prompt" so the adapter can locate
the corresponding agent definition and append the inline prompt.
In @.opencode/agents/comment-analyzer.md:
- Line 4: Replace the invalid model identifier string
"ollama-cloud/glm-4.7:cloud" with the correct Ollama Cloud model name
"glm-4.7:cloud" in the agent config entries; update the occurrences in
.opencode/agents/comment-analyzer.md, .opencode/agents/pr-test-analyzer.md,
.opencode/agents/infra-reviewer.md, and .opencode/agents/docs-consistency.md so
each file's model: line reads model: glm-4.7:cloud exactly (no namespace
prefix).
In @.opencode/plugins/memory.ts:
- Around line 19-23: The path mangling in getMemoryDir uses underscores but
AGENTS.md expects hyphens; update getMemoryDir so the replacement uses "-"
instead of "_" (i.e., change the replace(/[:/\\]/g, "_") call to use "-" and
keep the trimming calls that strip leading/trailing separators to mirror the
documented format), ensuring the mangled variable produced by getMemoryDir
matches the documented pattern (so memory directories align between OpenCode and
Claude Code).
In @.opencode/plugins/synthorg-hooks.ts:
- Around line 121-131: The git -C check currently looks for the wrong marker and
will never block; update the check in the block where
runHookScript("scripts/check_git_c_cwd.sh", { command }, 5000) is called to test
gitResult for the string "deny" (e.g., gitResult.includes("deny")) instead of
"block" so the hook will throw new Error(gitResult) when the script returns a
deny decision; keep the existing variable names (gitResult) and function
runHookScript and preserve the thrown Error behavior.
In `@scripts/check_bash_no_write.sh`:
- Around line 36-43: The conditional using [[ ! "$REDIR" =~ ^&[0-9]+$ ]] is
causing a ShellCheck parse error because the regex contains unquoted $; fix by
quoting or moving the regex into a variable: define a variable e.g.
FD_RE='^&[0-9]+$' and then use [[ ! "$REDIR" =~ $FD_RE ]] (or [[ ! "$REDIR" =~
^'&'[0-9]+$ ]] with proper quoting) so the pattern is not treated as a shell
variable; update the check that references COMMAND, REDIR and the deny call
accordingly.
---
Duplicate comments:
In @.opencode/plugins/memory.ts:
- Around line 48-59: The buildFrontmatter function is writing user-provided
MemoryEntry.name and MemoryEntry.description directly into YAML frontmatter,
risking YAML injection; fix buildFrontmatter by YAML-quoting and escaping those
fields before embedding them: for both name and description replace backslashes
and double quotes with escaped versions, convert literal newlines to "\n" (or
otherwise escape them), then wrap the resulting string in double quotes when
inserting `name:` and `description:`; keep entry.type as-is (or validate if
user-provided) so the frontmatter always produces valid YAML.
In @.opencode/plugins/synthorg-hooks.ts:
- Around line 134-151: The PostToolUse handler after: async (input, output) => {
... } currently catches failures from execSync(`python
scripts/check_web_design_system.py`) and only logs them (console.error), making
the check advisory; change the catch to re-throw the error (or throw a new Error
with err.message/err.stderr and context including output.args.file_path and
script name) so the PostToolUse hook fails on violations, ensuring edits/writes
to paths matching output.args.file_path.includes("web/src/") cannot proceed
until the design system script succeeds.
- Around line 62-70: The pytest-detection regex in the synthorg-hooks.ts hook
currently only matches "run pytest" and "python -m pytest"
(/(:?run\s+pytest|python\s+-m\s+pytest)\b/i) and should be expanded to include
bare "pytest" to match the hookify rule; replace that regex with something like
/(?:^|\s)(?:pytest|run\s+pytest|python\s+-m\s+pytest)\b/i in the pytest-checking
code path (the regex literal in the block that throws the "Always use `-n 8`
with pytest..." Error), and make the identical replacement in the other
occurrence noted (the second similar block around the other check).
- Around line 23-44: The runHookScript function builds a fragile shell pipeline
using execSync and echo which breaks on embedded quotes; replace execSync(`echo
... | bash ${scriptPath}`) with a direct child process invocation (spawnSync or
execFileSync) that passes the JSON stringified input to the script's stdin via
the input option, keep timeoutMs, encoding and stdio settings, and preserve the
existing error handling logic for exit status 2 by inspecting the child
result.status and result.stdout (or captured error) to return the hook denial
message or null as before; update references to execSync in runHookScript
accordingly.
In `@scripts/check_bash_no_write.sh`:
- Line 12: The current assignment COMMAND=$(jq -r '.tool_input.command // ""'
2>/dev/null) masks jq parse errors; change it to let jq report failures and
fail-closed: remove the "2>/dev/null" so jq writes errors to stderr, then
immediately check jq's exit status (or test COMMAND) and if jq failed (non-zero)
write an error message and exit 1; reference the COMMAND variable and the jq
invocation in scripts/check_bash_no_write.sh so malformed JSON causes the script
to exit non-zero instead of silently allowing writes.
In `@scripts/check_git_c_cwd.sh`:
- Around line 16-18: The sed extraction for GIT_C_PATH (using the COMMAND
variable and the sed pattern
's/.*git[[:space:]]+-C[[:space:]]+("([^"]+)"|([^[:space:]]+)).*/\2\3/') can miss
cases where options appear between git and -C (e.g., git --no-pager -C path);
either document this known limitation in a comment above the GIT_C_PATH
extraction or replace the fragile sed with a token-based parse of COMMAND that
scans tokens for "git" then for a later "-C" (handling quoted and unquoted
paths) and assigns the result to GIT_C_PATH; update the comment to mention the
behavior if parsing fails (script intentionally fails open) and reference
GIT_C_PATH and COMMAND so reviewers can find the code to modify.
🪄 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: d0bd75e5-b3a0-4102-9cd2-c3ae170fcbe7
📒 Files selected for processing (9)
.claude/hookify.enforce-parallel-tests.md.claude/hookify.no-cd-prefix.md.claude/skills/aurelio-review-pr/SKILL.md.opencode/agents/comment-analyzer.md.opencode/plugins/memory.ts.opencode/plugins/synthorg-hooks.tsopencode.jsonscripts/check_bash_no_write.shscripts/check_git_c_cwd.sh
📜 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). (2)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
🧠 Learnings (55)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
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-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).
📚 Learning: 2026-04-06T13:43:45.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.381Z
Learning: Applies to .git* : Pre-push hooks: mypy type-check (affected modules) + pytest unit tests (affected modules) + golangci-lint + go vet + go test (CLI conditional) + eslint-web (web dashboard) (fast gate before push). Foundational module changes (core, config, observability) or conftest changes trigger full runs.
Applied to files:
.claude/hookify.enforce-parallel-tests.mdscripts/check_git_c_cwd.sh.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
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/hookify.enforce-parallel-tests.md
📚 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: Parallelism: pytest-xdist via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.
Applied to files:
.claude/hookify.enforce-parallel-tests.md.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to 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:
.claude/hookify.enforce-parallel-tests.md.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-06T13:43:45.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.381Z
Learning: Applies to tests/**/*.py : Use pytest markers: pytest.mark.unit, pytest.mark.integration, pytest.mark.e2e, pytest.mark.slow. Minimum coverage 80% (enforced in CI).
Applied to files:
.claude/hookify.enforce-parallel-tests.md.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-02T18:48:02.489Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T18:48:02.489Z
Learning: ALWAYS run pytest with `-n auto` for parallel execution with pytest-xdist; never run tests sequentially
Applied to files:
.claude/hookify.enforce-parallel-tests.md.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-06T13:43:45.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.381Z
Learning: Applies to tests/**/*.py : Async testing: asyncio_mode = 'auto' - no manual pytest.mark.asyncio needed. Default timeout: 30 seconds per test (do not add per-file markers; non-default overrides like timeout(60) allowed). Parallelism: ALWAYS include -n 8 when running pytest locally, never run tests sequentially.
Applied to files:
.claude/hookify.enforce-parallel-tests.md
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally; mock `time.monotonic()` and `asyncio.sleep()` for determinism; use `asyncio.Event().wait()` for indefinite blocking instead of `asyncio.sleep(large_number)`
Applied to files:
.claude/hookify.enforce-parallel-tests.md
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests; fix them fully and fundamentally by mocking `time.monotonic()` and `asyncio.sleep()` for determinism or using `asyncio.Event().wait()` for indefinite blocking
Applied to files:
.claude/hookify.enforce-parallel-tests.md
📚 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:
.claude/hookify.enforce-parallel-tests.md
📚 Learning: 2026-04-01T15:36:39.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T15:36:39.993Z
Learning: Applies to tests/**/*.py : Flaky tests: NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
.claude/hookify.enforce-parallel-tests.md
📚 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:
.claude/hookify.enforce-parallel-tests.md
📚 Learning: 2026-03-30T16:36:33.512Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: cli/CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:33.512Z
Learning: Applies to cli/**/*.{sh,bash} : Use `go -C cli` instead of `cd cli` to change directories for Go tooling - `go -C cli` changes directory internally without affecting the shell, whereas `cd cli` poisons the cwd for all subsequent Bash calls
Applied to files:
.claude/hookify.no-cd-prefix.mdscripts/check_git_c_cwd.sh
📚 Learning: 2026-03-15T20:45:14.430Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T20:45:14.430Z
Learning: Shell commands: for Go CLI work, cd cli is an exception because Go tooling requires working directory to be the module root. Go commands require `cd cli` for other work, never use `cd`.
Applied to files:
.claude/hookify.no-cd-prefix.md
📚 Learning: 2026-03-21T11:08:01.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T11:08:01.542Z
Learning: NEVER use `cd` in Bash commands — the working directory is already set to the project root. Use absolute paths or run commands directly.
Applied to files:
.claude/hookify.no-cd-prefix.md
📚 Learning: 2026-04-01T18:17:24.196Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T18:17:24.196Z
Learning: NEVER use `cd` in Bash commands — working directory is already set to project root; use absolute paths or run commands directly
Applied to files:
.claude/hookify.no-cd-prefix.md
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: CLI (Go binary): see `cli/CLAUDE.md` for commands, flags, and reference; use `go -C cli` (never `cd cli`)
Applied to files:
.claude/hookify.no-cd-prefix.md
📚 Learning: 2026-04-02T09:04:54.709Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T09:04:54.709Z
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/hookify.no-cd-prefix.md
📚 Learning: 2026-04-06T13:43:45.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.381Z
Learning: Applies to .git* : Branches: `<type>/<slug>` from main. 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, golangci-lint + go vet (CLI conditional), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard zero warnings, conditional).
Applied to files:
.claude/hookify.no-cd-prefix.mdscripts/check_git_c_cwd.sh
📚 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: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Applied to files:
.claude/hookify.no-cd-prefix.md
📚 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:
.claude/hookify.no-cd-prefix.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:
.claude/hookify.no-cd-prefix.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:
scripts/check_git_c_cwd.shscripts/check_bash_no_write.sh.opencode/plugins/synthorg-hooks.ts.opencode/agents/comment-analyzer.md
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
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:
scripts/check_git_c_cwd.shscripts/check_bash_no_write.sh.opencode/plugins/synthorg-hooks.ts.opencode/agents/comment-analyzer.md
📚 Learning: 2026-03-31T20:21:35.626Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:21:35.626Z
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/`
Applied to files:
scripts/check_bash_no_write.sh.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/memory/**/*.py : Memory package (memory/): pluggable MemoryBackend protocol, backends/ (Mem0 adapter), retrieval pipeline (ranking, RRF fusion, injection, formatting, non-inferable filtering), shared org memory (org/), consolidation/archival (density-aware: DensityClassifier, AbstractiveSummarizer, ExtractivePreserver, DualModeConsolidationStrategy)
Applied to files:
.opencode/plugins/memory.ts
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/tsconfig.json : In TypeScript 6, remove `baseUrl` from tsconfig (deprecated; will stop working in TS 7) -- `paths` entries are relative to the tsconfig directory
Applied to files:
.opencode/plugins/memory.ts
📚 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/memory/**/*.py : Use MemoryBackend protocol with pluggable backends (Mem0 adapter available at backends/mem0/) for persistent agent memory
Applied to files:
.opencode/plugins/memory.ts
📚 Learning: 2026-04-06T13:43:45.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.381Z
Learning: When review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes), fix them all. No deferring, no 'out of scope' skipping.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-04-01T05:46:17.064Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T05:46:17.064Z
Learning: Fix all valid issues found by review agents (including pre-existing issues in surrounding code, suggestions, and adjacent findings) — never skip or defer
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md.opencode/agents/comment-analyzer.md
📚 Learning: 2026-03-15T21:49:53.264Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:49:53.264Z
Learning: Fix everything valid — never skip when review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes). No deferring, no 'out of scope' skipping.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-04-06T13:43:45.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.381Z
Learning: NEVER create a PR directly - gh pr create is blocked by hookify. ALWAYS use /pre-pr-review to create PRs - it runs automated checks + review agents + fixes before creating the PR. For trivial/docs-only changes: /pre-pr-review quick skips agents but still runs automated checks. After PR exists, use /aurelio-review-pr for external reviewer feedback.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.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: NEVER create a PR directly with `gh pr create` — ALWAYS use `/pre-pr-review` to create PRs (runs automated checks + review agents + fixes before creating PR); use `/pre-pr-review quick` for trivial/docs-only changes
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-03-20T12:59:09.553Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T12:59:09.553Z
Learning: NEVER create a PR directly — `gh pr create` is blocked by hookify. ALWAYS use `/pre-pr-review` to create PRs — it runs automated checks + review agents + fixes before creating the PR. For trivial/docs-only changes: `/pre-pr-review quick` skips agents but still runs automated checks.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-04-06T13:43:45.380Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.380Z
Learning: Applies to src/**/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.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/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.
Applied to files:
.claude/skills/aurelio-review-pr/SKILL.md
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Run the PostToolUse hook (`scripts/check_web_design_system.py`) on every Edit/Write to `web/src/` files and fix all violations before proceeding
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Handle errors explicitly, never silently swallow them. All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
Learning: Run full pytest suite with coverage: `uv run python -m pytest tests/ -n auto --cov=synthorg --cov-fail-under=80`
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-06T13:43:45.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T13:43:45.381Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from web/src/components/ui/ before creating new ones. NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions - use design tokens and `@/lib/motion` presets. A PostToolUse hook (scripts/check_web_design_system.py) enforces these rules on every Edit/Write to web/src/.
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-02T21:38:30.127Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T21:38:30.127Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Handle errors explicitly; never silently swallow exceptions
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 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: Alert the user and explain why if implementation deviates from the design spec; do NOT silently diverge
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/**/*.{tsx,ts} : Do not use rgba() with hardcoded values -- use design token variables
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-01T20:43:51.878Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T20:43:51.878Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and `@/lib/motion` presets.
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{ts,tsx,css} : Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code — use design tokens and `@/lib/motion` presets
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and `@/lib/motion` presets
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : Always use `createLogger` from `@/lib/logger` -- never bare `console.warn`/`console.error`/`console.debug` in application code
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox
Applied to files:
.opencode/plugins/synthorg-hooks.ts
📚 Learning: 2026-04-03T07:50:58.367Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T07:50:58.367Z
Learning: Applies to **/*.py : Use Google-style docstrings, required on all public classes and functions (enforced by ruff D rules)
Applied to files:
.opencode/agents/comment-analyzer.md
📚 Learning: 2026-03-16T10:40:25.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T10:40:25.144Z
Learning: Applies to **/*.py : Docstrings: Google style, required on public classes and functions (enforced by ruff D rules).
Applied to files:
.opencode/agents/comment-analyzer.md
🪛 markdownlint-cli2 (0.22.0)
.claude/hookify.no-cd-prefix.md
[warning] 15-15: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
.claude/skills/aurelio-review-pr/SKILL.md
[warning] 166-166: Spaces inside code span elements
(MD038, no-space-in-code)
🪛 Shellcheck (0.11.0)
scripts/check_bash_no_write.sh
[info] 40-40: The mentioned syntax error was in this if expression.
(SC1009)
[error] 40-40: Couldn't parse this test expression. Fix to allow more checks.
(SC1073)
[error] 40-40: Expected test to end here (don't wrap commands in []/[[]]). Fix any mentioned problems and try again.
(SC1072)
🔇 Additional comments (9)
opencode.json (2)
3-54: Model/provider wiring looks consistent.
ollama-cloud/glm-4.7:cloudis defined here (Line 43) and matches the agent usage in.opencode/agents/comment-analyzer.md, so this part is correctly wired.
55-68: All instruction paths are present in the repository.The verification confirms that all 12 instruction file paths listed in the opencode.json instructions array (including
.claude/hookify.*.md,web/CLAUDE.md, andcli/CLAUDE.md) exist and resolve correctly..claude/skills/aurelio-review-pr/SKILL.md (1)
554-562: Good addition: explicit polling across both issue comments and review submissions.This reduces missed bot feedback and avoids premature triage.
.opencode/agents/comment-analyzer.md (1)
17-74: The markdownlint warnings cited in the previous review have been addressed.The current code shows blank lines after each H3 heading (lines 18, 25, 32, 38, 44) and blank lines surrounding the fenced code block (lines 69, 75), with a
textlanguage identifier at line 70. The MD022, MD031, and MD040 warnings no longer apply..claude/hookify.enforce-parallel-tests.md (1)
1-23: LGTM!The hookify rule correctly enforces
-n 8for pytest commands. The regex pattern appropriately matches barepytest,run pytest, andpython -m pytestinvocations, and thenot_containscondition properly blocks commands missing the required parallelization flag.scripts/check_git_c_cwd.sh (1)
34-45: Exit code 2 correctly added for denial.The block condition now properly exits with code 2, aligning with the OpenCode plugin's expectation that exit status 2 signals denial.
.claude/hookify.no-cd-prefix.md (1)
1-20: LGTM!The pattern
^\s*cd\s+correctly handles the leading whitespace bypass case flagged in earlier reviews. The guidance provides appropriate alternatives (go -C clifor Go commands,bash -c "cd <dir> && <cmd>"for tools without-Csupport) that align with repository conventions. Based on learnings: "Usego -C cliinstead ofcd clito change directories for Go tooling".scripts/check_bash_no_write.sh (1)
14-26: Well-structured deny function.The
deny()helper cleanly outputs structured JSON with the correcthookSpecificOutputschema and exits with code 2, consistent with the OpenCode plugin's denial detection..opencode/plugins/memory.ts (1)
110-130: Path traversal protection looks correct.The
delete_memoryhandler properly validates filenames by rejecting non-alphanumeric characters and..sequences, then verifies the resolved path stays withinMEMORY_DIR. This addresses the critical security concern from previous reviews.
| | **pr-test-analyzer** | `test_py` changed, OR `src_py` changed with no corresponding test changes | `explore` | | ||
| | **silent-failure-hunter** | Diff contains `try`, `except`, `raise`, error handling patterns | `explore` | | ||
| | **comment-analyzer** | Diff contains docstring changes (`"""`) or significant comment changes | `explore` | | ||
| | **type-design-analyzer** | Diff contains `class ` definitions, `BaseModel`, `TypedDict`, type aliases | `explore` | |
There was a problem hiding this comment.
Fix markdownlint MD038 at Line 166 (class code span spacing).
The code span has a trailing space, which triggers no-space-in-code.
Proposed fix
-| **type-design-analyzer** | Diff contains `class ` definitions, `BaseModel`, `TypedDict`, type aliases | `explore` |
+| **type-design-analyzer** | Diff contains `class` definitions, `BaseModel`, `TypedDict`, type aliases | `explore` |📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| | **type-design-analyzer** | Diff contains `class ` definitions, `BaseModel`, `TypedDict`, type aliases | `explore` | | |
| | **type-design-analyzer** | Diff contains `class` definitions, `BaseModel`, `TypedDict`, type aliases | `explore` | |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 166-166: Spaces inside code span elements
(MD038, no-space-in-code)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.claude/skills/aurelio-review-pr/SKILL.md at line 166, The MD038 lint
failure is caused by a trailing space inside the inline code span containing
`class `; edit the SKILL.md content to remove the trailing space so the code
span reads `class` (no trailing space) to satisfy the no-space-in-code rule
(MD038) — update the exact occurrence of the `` `class ` `` code span in the
document (around the existing diff entry) to `` `class` ``.
.opencode/agents/comment-analyzer.md
Outdated
| --- | ||
| description: "Comment and docstring analysis: accuracy, completeness, maintainability, Google style" | ||
| mode: subagent | ||
| model: ollama-cloud/glm-4.7:cloud |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify the Ollama Cloud model is configured and accessible
# Check if opencode.json references this model
if [ -f .opencode/opencode.json ]; then
echo "=== Checking .opencode/opencode.json for model configuration ==="
jq '.models // {}' .opencode/opencode.json
fi
# Search for other references to this model in agent configs
echo ""
echo "=== Other agent files using the same model ==="
rg -n "model:\s*ollama-cloud/glm-4\.7:cloud" .opencode/agents/Repository: Aureliolo/synthorg
Length of output: 399
🌐 Web query:
Is "ollama-cloud/glm-4.7:cloud" a valid Ollama Cloud model identifier?
💡 Result:
No. In Ollama Cloud, the model name is glm-4.7:cloud (as shown in Ollama’s model library and cloud-model usage examples), not a namespaced identifier like ollama-cloud/glm-4.7:cloud. [1], [2]
Use: glm-4.7:cloud (e.g., ollama run glm-4.7:cloud). [1]
Sources:
[1] Ollama model library page for glm-4.7:cloud (ollama.com)
[2] Ollama blog: Cloud models (examples use ...:*-cloud tags without an ollama-cloud/ prefix) (ollama.com)
Citations:
- 1: https://ollama.com/library/glm-4.7%3Acloud?utm_source=openai
- 2: https://ollama.com/blog/cloud-models?utm_source=openai
Replace the invalid model identifier with the correct Ollama Cloud model name.
The model identifier ollama-cloud/glm-4.7:cloud is incorrect. According to Ollama's official documentation, the valid model name is glm-4.7:cloud (without the ollama-cloud/ namespace prefix). This affects .opencode/agents/comment-analyzer.md, .opencode/agents/pr-test-analyzer.md, .opencode/agents/infra-reviewer.md, and .opencode/agents/docs-consistency.md. Update all four agent configuration files to use model: glm-4.7:cloud.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/comment-analyzer.md at line 4, Replace the invalid model
identifier string "ollama-cloud/glm-4.7:cloud" with the correct Ollama Cloud
model name "glm-4.7:cloud" in the agent config entries; update the occurrences
in .opencode/agents/comment-analyzer.md, .opencode/agents/pr-test-analyzer.md,
.opencode/agents/infra-reviewer.md, and .opencode/agents/docs-consistency.md so
each file's model: line reads model: glm-4.7:cloud exactly (no namespace
prefix).
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
There was a problem hiding this comment.
Actionable comments posted: 13
♻️ Duplicate comments (4)
.opencode/agents/design-token-audit.md (1)
54-59:⚠️ Potential issue | 🟡 MinorAlign Storybook exclusions with the updated skip list.
Line 58 still excludes only
.stories.tsx, but Line 22 already excludes both.stories.tsxand.stories.ts. This can reintroduce false positives for TS-only stories.Suggested fix
- - Storybook `.stories.tsx` demo wrappers + - Storybook `.stories.tsx` / `.stories.ts` demo wrappers🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/agents/design-token-audit.md around lines 54 - 59, The Storybook exclusion in the design-token-audit skip list only mentions ".stories.tsx" but must match the earlier exclusion that includes both ".stories.tsx" and ".stories.ts"; update the Storybook exclusion bullet (the "Storybook `.stories.tsx` demo wrappers" item) to include ".stories.ts" as well so TS-only stories are also skipped and avoid reintroducing false positives..opencode/agents/pr-test-analyzer.md (1)
66-70:⚠️ Potential issue | 🟡 MinorAdd the enforced 80% coverage gate to this agent spec.
Line 66-Line 70 defines severities but omits the hard CI threshold, so analysis can understate release-blocking coverage failures. Based on learnings: minimum 80% test coverage is enforced in CI for
src/synthorg/**/*.py.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/agents/pr-test-analyzer.md around lines 66 - 70, Update the agent spec to include the enforced CI coverage gate: add a rule stating that any files under src/synthorg/**/*.py must maintain at least 80% test coverage and that violations are treated as HIGH severity (release-blocking); modify the "Severity Levels" section (the list containing HIGH/MEDIUM/LOW) to mention this 80% threshold and link the path pattern src/synthorg/**/*.py to the HIGH category so the analyzer flags coverage below 80% as a HIGH issue.scripts/check_git_c_cwd.sh (1)
12-20:⚠️ Potential issue | 🟠 Major
git -Cblocking is bypassable due brittle parsing and non-canonical path comparison.Line 12-Line 20 only handles
git -Cin a narrow token order, and Line 36 compares strings rather than resolved paths.git --no-pager -C . status(and similar forms) can evade the block despite targeting the current directory.🐛 Proposed hardening patch
-if [[ -z "$COMMAND" ]] || ! echo "$COMMAND" | grep -qE 'git[[:space:]]+-C[[:space:]]+'; then +if [[ -z "$COMMAND" ]] || ! echo "$COMMAND" | grep -qE '(^|[[:space:]])git([[:space:]]|$)'; then exit 0 fi -# Extract path after -C (handles quoted and unquoted) -# Note: this simple sed may miss cases where options appear between git and -C -# (e.g., git --no-pager -C path). For such cases, the script intentionally fails open. -GIT_C_PATH=$(echo "$COMMAND" | sed -E 's/.*git[[:space:]]+-C[[:space:]]+("([^"]+)"|([^[:space:]]+)).*/\2\3/') -GIT_C_PATH="${GIT_C_PATH//\"/}" +# Extract -C argument robustly (supports flags before -C and quoted args) +GIT_C_PATH="$( +python - <<'PY' +import os, shlex +cmd = os.environ.get("COMMAND", "") +try: + toks = shlex.split(cmd) +except ValueError: + print("") + raise SystemExit(0) +path = "" +for i, t in enumerate(toks): + if t == "-C" and i + 1 < len(toks): + path = toks[i + 1] + break + if t.startswith("-C") and len(t) > 2: + path = t[2:] + break +print(path) +PY +)" +if [[ -z "$GIT_C_PATH" ]]; then + exit 0 +fi normalize() { local p="$1" @@ - echo "$p" + # Resolve relative paths like "." so equivalence checks are reliable + if [[ "$p" != /* ]]; then + p="$PWD/$p" + fi + p="$(cd "$p" 2>/dev/null && pwd -P || echo "$p")" + echo "${p%/}" }Also applies to: 33-36
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check_git_c_cwd.sh` around lines 12 - 20, The current check for git -C is brittle because COMMAND parsing (variable COMMAND and GIT_C_PATH extraction) only matches a narrow token order and then compares raw strings; update the logic to robustly locate a -C token anywhere in the command argument list by splitting COMMAND into tokens and scanning for the -C/--git-dir usage, capture the subsequent token (respecting quoted args), then canonicalize the extracted path and the repository cwd using realpath/readlink -f (or equivalent) and compare resolved paths instead of raw strings; ensure you handle the case of -C . and quoted paths and treat missing/invalid -C value as “no block” (fail open) as before..claude/skills/aurelio-review-pr/SKILL.md (1)
166-166:⚠️ Potential issue | 🟠 MajorKeep the
subagent_typeliteral consistent here.
explore (type-design-analyzer)is the only row that is not one of the documented working values. If this literal is copied into a Task call, the type-design-analyzer path still fails withUnknown agent typeand the type review never runs.♻️ Proposed fix
-| **type-design-analyzer** | Diff contains `class` definitions, `BaseModel`, `TypedDict`, type aliases | `explore (type-design-analyzer)` | +| **type-design-analyzer** | Diff contains `class` definitions, `BaseModel`, `TypedDict`, type aliases | `explore` |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.claude/skills/aurelio-review-pr/SKILL.md at line 166, The table row uses the nonstandard literal "explore (type-design-analyzer)" for subagent_type which breaks agent lookup; update the value to the documented working literal (e.g., "type-design-analyzer") wherever "subagent_type" or that table row appears so Task calls use the exact expected string; ensure any callers or tests referencing "explore (type-design-analyzer)" are updated to the canonical "type-design-analyzer" to restore the type review path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/skills/aurelio-review-pr/SKILL.md:
- Around line 183-184: The fallback behavior that currently restricts Task
failures to diff-only inspection must be expanded: update the Task failure path
so instead of only inspecting changed files it invokes the Read/Grep fallback on
both the changed files and the additional required sources (explicitly include
CLAUDE.md, README.md, and any referenced design pages) and also ensures the
issue-resolution-verifier fetches and reads the full linked issue content;
locate the Task dispatch/fallback logic handling "Unknown agent type" and modify
it to call the same Read/Grep routines used by docs-consistency and
issue-resolution-verifier with the expanded source set and issue fetch step.
In @.opencode/agents/conventions-enforcer.md:
- Around line 27-29: The vendor-name allowlist is duplicated (the "Real vendor
names (Anthropic, OpenAI, Claude, GPT, Gemini, etc.)..." block) and must be
centralized; pick the SKILL.md policy as canonical, remove the duplicate block
from conventions-enforcer.md, and update the conventions-enforcer.md to
reference the canonical SKILL.md policy (or import/include it) so scopes match
exactly; also ensure tests use the canonical test names like
`test-provider`/`test-small-001` and adjust any differing scope text in either
SKILL.md or conventions-enforcer.md so there is a single source of truth.
In @.opencode/agents/docs-consistency.md:
- Around line 23-32: The agent spec currently references non-existent section
IDs (SS15.3, SS3.1, SS15.4, SS15.5, SS10.2, SS11.1.1, SS15.2, SS9.2, SS9.3);
replace each SS* reference with the correct design document page and section
heading (e.g., map SS3.1 → design/agents.md “Agent Identity Card”, SS15.3 →
design/agents.md “Project Structure”, SS15.4/SS15.5 → architecture/tech-stack.md
or design/models sections as appropriate, SS10.2 → design/operations.md “Cost
Tracking / TokenUsage”, SS11.1.1 → design/engine.md “Tool Execution /
ToolInvoker”, SS15.2 → architecture/tech-stack.md “Technology Stack”,
SS9.2/SS9.3 → design/operations.md “Provider Configuration / LiteLLM
Integration”); update the list entries in .opencode/agents/docs-consistency.md
to point to those pages and exact section headers and adjust any labels from SS*
to the corresponding filenames and headings so the spec matches the actual docs.
In @.opencode/agents/issue-resolution-verifier.md:
- Line 21: The gh CLI invocation that fetches issue data currently uses "gh
issue view <number> --json title,body,labels" which omits comments needed for
Check `#5` ("Issue comments"); update the invocation to request the comments field
(add comments to the --json list) so the verifier can inspect issue comments
when running the "Issue comments" validation, and ensure any parsing logic that
consumes the fetched JSON (the code path handling the gh issue view response)
accesses the comments property for the check.
In @.opencode/agents/pr-test-analyzer.md:
- Around line 74-87: The markdown code fence before the "## Coverage Analysis"
section is unlabeled which triggers MD040; update the opening fenced block in
.opencode/agents/pr-test-analyzer.md (the fence that currently precedes "##
Coverage Analysis") to a labeled fence by adding the language identifier `text`
(i.e., change ``` to ```text) so the fenced block is explicitly typed and the
docs linter passes.
In @.opencode/agents/resilience-audit.md:
- Around line 63-69: Update the document’s Severity Levels and summary logic to
eliminate the ambiguous "(SUGGESTION)" label by either (A) adding "SUGGESTION"
as a first-class severity in the Severity Levels section and summary format, or
(B) mapping all occurrences of "(SUGGESTION)" under the "Soft Rules" heading to
the existing "LOW" level; search for every instance of the literal
"(SUGGESTION)" in the "Soft Rules" block (and the repeated blocks around the
noted ranges) and ensure the summary templates, examples, and any automated
report mapping reflect the chosen approach so generated reports are unambiguous.
In @.opencode/plugins/memory.ts:
- Around line 103-105: The slug generated from a memory name can be empty
(making filename ".md") which leads to collisions; after calling slugify(name)
in the memory save flow (where ensureMemoryDir(), filename, filepath, and
MEMORY_DIR are used), validate the slug result and reject the operation if it is
an empty string or only whitespace—e.g., return or throw a clear
error/validation message indicating the name is invalid and cannot be all
punctuation/non-ASCII—so no file is written when slugify(name) produces an empty
slug.
- Around line 117-135: The delete_memory handler currently allows deleting the
shared index file "MEMORY.md", which breaks all stored memories; modify the
delete flow in the block handling toolName === "delete_memory" (variables:
filename, safeName, filepath, MEMORY_DIR, updateIndex) to explicitly reject
attempts to delete "MEMORY.md" (or any case-insensitive variant) before
unlinkSync/updateIndex are executed—e.g., if safeName.toLowerCase() ===
"memory.md" throw a descriptive error and do not call unlinkSync or updateIndex
so the shared index cannot be removed or recreated empty.
- Around line 77-84: Replace substring checks against filename in the add/remove
logic with exact link-target matching: when deciding if an entry exists in index
use a regex to extract the link target from each markdown list line (e.g., parse
lines from index with /\[.*?\]\((.*?)\)/ and compare the captured group to
filename) instead of index.includes(filename); similarly, when removing, filter
out only lines whose captured link target === filename rather than
line.includes(filename). Update the blocks around the variables/action checks
(action, entry, title, filename, hook, index, lines) to use this exact-match
extraction.
In @.opencode/plugins/synthorg-hooks.ts:
- Around line 20-21: The after handler calls execSync but the module only
imports spawnSync; update the imports at the top of
.opencode/plugins/synthorg-hooks.ts to include execSync from "child_process"
(alongside or instead of spawnSync as appropriate) so the after(...) handler's
call to execSync(...) is defined and the design-system post-write hook can run.
- Around line 23-49: The runHookScript function currently treats any spawnSync
result other than status 2 as success; change the logic so non-zero, non-2 exit
codes are treated as failures (fail closed) instead of returning stdout.
Specifically, after calling spawnSync in runHookScript, check result.status: if
result.status === 2 return the denied message, if result.status === 0 return
result.stdout, and for any other non-zero status return null (or propagate an
error) to block the action; also mirror this logic in the catch branch that
inspects err.status to ensure status 1 or other failures do not silently pass.
In `@scripts/check_bash_no_write.sh`:
- Around line 37-48: The initial regex that checks COMMAND (grep -qE
'(^|[|&;])\s*>>?\s*"?[^-]') misses redirects that aren’t directly after a
pipe/sep (e.g., "cat > out" or "echo x > README"); update that grep to detect
redirects appearing anywhere (for example change it to grep -qE
'(^|[^|&;])\s*>>?\s*"?[^-]' or an equivalent pattern that matches '>'/ '>>' not
only after pipe/sep), keep the REDIR extraction logic (REDIR=$(... grep -oE
'>>?\s*"?[^|&;<>]+' ...)) and then continue validating against FD_RE='^&[0-9]+$'
and calling deny(...) when the target is not a file descriptor; ensure you
replace only the first grep pattern in the if that references COMMAND so other
logic (FD_RE, REDIR, deny) remains unchanged.
- Around line 32-35: The heredoc detection regex misses quoted and escaped
delimiters (e.g. <<"EOF" or <<\EOF) so commands like python - <<"PY" bypass the
check; update the grep pattern used on "$COMMAND" to also accept an optional
backslash before the delimiter and optional single or double quotes around the
delimiter (i.e. allow an optional \\ before the quote and match both ' and "),
then keep calling deny(...) when a match is found; the change should be applied
to the grep -qE line that references "$COMMAND" and leave the deny message and
function intact.
---
Duplicate comments:
In @.claude/skills/aurelio-review-pr/SKILL.md:
- Line 166: The table row uses the nonstandard literal "explore
(type-design-analyzer)" for subagent_type which breaks agent lookup; update the
value to the documented working literal (e.g., "type-design-analyzer") wherever
"subagent_type" or that table row appears so Task calls use the exact expected
string; ensure any callers or tests referencing "explore (type-design-analyzer)"
are updated to the canonical "type-design-analyzer" to restore the type review
path.
In @.opencode/agents/design-token-audit.md:
- Around line 54-59: The Storybook exclusion in the design-token-audit skip list
only mentions ".stories.tsx" but must match the earlier exclusion that includes
both ".stories.tsx" and ".stories.ts"; update the Storybook exclusion bullet
(the "Storybook `.stories.tsx` demo wrappers" item) to include ".stories.ts" as
well so TS-only stories are also skipped and avoid reintroducing false
positives.
In @.opencode/agents/pr-test-analyzer.md:
- Around line 66-70: Update the agent spec to include the enforced CI coverage
gate: add a rule stating that any files under src/synthorg/**/*.py must maintain
at least 80% test coverage and that violations are treated as HIGH severity
(release-blocking); modify the "Severity Levels" section (the list containing
HIGH/MEDIUM/LOW) to mention this 80% threshold and link the path pattern
src/synthorg/**/*.py to the HIGH category so the analyzer flags coverage below
80% as a HIGH issue.
In `@scripts/check_git_c_cwd.sh`:
- Around line 12-20: The current check for git -C is brittle because COMMAND
parsing (variable COMMAND and GIT_C_PATH extraction) only matches a narrow token
order and then compares raw strings; update the logic to robustly locate a -C
token anywhere in the command argument list by splitting COMMAND into tokens and
scanning for the -C/--git-dir usage, capture the subsequent token (respecting
quoted args), then canonicalize the extracted path and the repository cwd using
realpath/readlink -f (or equivalent) and compare resolved paths instead of raw
strings; ensure you handle the case of -C . and quoted paths and treat
missing/invalid -C value as “no block” (fail open) as before.
🪄 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: 39a4a950-4ac6-4e0c-a80e-b744e9e69907
📒 Files selected for processing (18)
.claude/skills/aurelio-review-pr/SKILL.md.opencode/agents/comment-analyzer.md.opencode/agents/conventions-enforcer.md.opencode/agents/design-token-audit.md.opencode/agents/docs-consistency.md.opencode/agents/go-conventions-enforcer.md.opencode/agents/go-reviewer.md.opencode/agents/go-security-reviewer.md.opencode/agents/infra-reviewer.md.opencode/agents/issue-resolution-verifier.md.opencode/agents/persistence-reviewer.md.opencode/agents/pr-test-analyzer.md.opencode/agents/resilience-audit.md.opencode/agents/security-reviewer.md.opencode/plugins/memory.ts.opencode/plugins/synthorg-hooks.tsscripts/check_bash_no_write.shscripts/check_git_c_cwd.sh
| - Real vendor names (Anthropic, OpenAI, Claude, GPT, Gemini, etc.) in code, docstrings, comments, tests, or config examples | ||
| - Allowed only in: `docs/design/operations.md`, `.claude/` files, third-party import paths, `providers/presets.py` | ||
| - Tests must use `test-provider`, `test-small-001`, etc. |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Unify vendor-name allowlisting to a single policy source.
Line 27-Line 29 duplicates policy that is also defined in .claude/skills/aurelio-review-pr/SKILL.md; the scopes are close but not identical, which can cause drift and inconsistent findings across agents.
♻️ Suggested adjustment
-- Allowed only in: `docs/design/operations.md`, `.claude/` files, third-party import paths, `providers/presets.py`
+- Allowed only in the canonical allowlist policy (single source of truth) used by pre-PR review skill/config.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/conventions-enforcer.md around lines 27 - 29, The
vendor-name allowlist is duplicated (the "Real vendor names (Anthropic, OpenAI,
Claude, GPT, Gemini, etc.)..." block) and must be centralized; pick the SKILL.md
policy as canonical, remove the duplicate block from conventions-enforcer.md,
and update the conventions-enforcer.md to reference the canonical SKILL.md
policy (or import/include it) so scopes match exactly; also ensure tests use the
canonical test names like `test-provider`/`test-small-001` and adjust any
differing scope text in either SKILL.md or conventions-enforcer.md so there is a
single source of truth.
.opencode/agents/docs-consistency.md
Outdated
| 1. SS15.3 Project Structure -- does it match actual files/directories under `src/synthorg/`? Any new modules missing? Any listed files that no longer exist? (CRITICAL) | ||
| 2. SS3.1 Agent Identity Card -- does the config/runtime split documentation match the actual model code? (MAJOR) | ||
| 3. SS15.4 Key Design Decisions -- are technology choices and rationale still accurate? (MAJOR) | ||
| 4. SS15.5 Pydantic Model Conventions -- do documented conventions match how models are actually written? Are "Adopted" vs "Planned" labels still accurate? (MAJOR) | ||
| 5. SS10.2 Cost Tracking -- does the implementation note match actual `TokenUsage` and spending summary models? (MAJOR) | ||
| 6. SS11.1.1 Tool Execution Model -- does it match actual `ToolInvoker` behavior? (MAJOR) | ||
| 7. SS15.2 Technology Stack -- are versions, libraries, and rationale current? (MEDIUM) | ||
| 8. SS9.2 Provider Configuration -- are model IDs, provider capability examples, and config/runtime mapping still representative? (MEDIUM) | ||
| 9. SS9.3 LiteLLM Integration -- does the integration status match reality? (MEDIUM) | ||
| 10. Any other section that describes behavior, structure, or patterns that have changed (MAJOR) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify that all SS* section references mentioned in the agent spec exist in design docs.
echo "=== Checking for design spec index ==="
if [ -f "docs/DESIGN_SPEC.md" ]; then
echo "✓ docs/DESIGN_SPEC.md exists"
cat docs/DESIGN_SPEC.md
else
echo "✗ docs/DESIGN_SPEC.md not found"
fi
echo -e "\n=== Searching for section references in design docs ==="
# Search for each section reference mentioned in the agent spec
for section in "SS15.3" "SS3.1" "SS15.4" "SS15.5" "SS10.2" "SS11.1.1" "SS15.2" "SS9.2" "SS9.3"; do
echo -e "\n--- Searching for $section ---"
rg -n "$section" docs/design/ || echo "✗ $section not found"
done
echo -e "\n=== Listing all design docs ==="
fd -t f . docs/design/Repository: Aureliolo/synthorg
Length of output: 3886
Update agent spec to reference actual design documentation structure.
The agent references non-existent section identifiers (SS15.3, SS3.1, SS15.4, SS15.5, SS10.2, SS11.1.1, SS15.2, SS9.2, SS9.3) that do not appear in the design documentation. The design spec uses simple page filenames and section headers, not SS* numbering.
Update the agent to reference the actual design pages at docs/design/:
design/agents.md(Agent identity, roles, hiring, performance)design/organization.md(Company structure, templates)design/engine.md(Task lifecycle, execution, workflows)design/operations.md(Providers, cost management, tools, security)design/communication.md(Message bus, delegation, meetings)design/memory.md(Memory types, backends, retrieval)architecture/tech-stack.md(Technology choices and versions)
Map each item to the correct design page and specific section headings within that page.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/docs-consistency.md around lines 23 - 32, The agent spec
currently references non-existent section IDs (SS15.3, SS3.1, SS15.4, SS15.5,
SS10.2, SS11.1.1, SS15.2, SS9.2, SS9.3); replace each SS* reference with the
correct design document page and section heading (e.g., map SS3.1 →
design/agents.md “Agent Identity Card”, SS15.3 → design/agents.md “Project
Structure”, SS15.4/SS15.5 → architecture/tech-stack.md or design/models sections
as appropriate, SS10.2 → design/operations.md “Cost Tracking / TokenUsage”,
SS11.1.1 → design/engine.md “Tool Execution / ToolInvoker”, SS15.2 →
architecture/tech-stack.md “Technology Stack”, SS9.2/SS9.3 →
design/operations.md “Provider Configuration / LiteLLM Integration”); update the
list entries in .opencode/agents/docs-consistency.md to point to those pages and
exact section headers and adjust any labels from SS* to the corresponding
filenames and headings so the spec matches the actual docs.
.opencode/agents/pr-test-analyzer.md
Outdated
| ``` | ||
| ## Coverage Analysis | ||
|
|
||
| ### New/Modified Source Files | ||
| - src/synthorg/module/file.py (N new public functions) | ||
| - function_a: COVERED in tests/module/test_file.py:42 | ||
| - function_b: NOT COVERED [HIGH] | ||
| - function_c: PARTIAL -- happy path only [MEDIUM] | ||
|
|
||
| ### Missing Test Scenarios | ||
| [SEVERITY] Description of untested scenario | ||
| Source: file:line | ||
| Needed: What test should verify | ||
| ``` |
There was a problem hiding this comment.
Specify a language for the report-format fenced block.
Line 74 starts an unlabeled fence; add text to avoid MD040 and keep docs lint-clean.
🧹 Suggested fix
-```
+```text
## Coverage Analysis🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 74-74: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/pr-test-analyzer.md around lines 74 - 87, The markdown code
fence before the "## Coverage Analysis" section is unlabeled which triggers
MD040; update the opening fenced block in .opencode/agents/pr-test-analyzer.md
(the fence that currently precedes "## Coverage Analysis") to a labeled fence by
adding the language identifier `text` (i.e., change ``` to ```text) so the
fenced block is explicitly typed and the docs linter passes.
- Move check_bash_no_write.sh from ~/.claude/hooks/ to scripts/ - Move check_git_c_cwd.sh from ~/bin/ to scripts/ - Promote 4 hookify rules from .local to committed: block-pr-create, enforce-parallel-tests, no-cd-prefix, no-local-coverage - Genericize absolute paths in no-cd-prefix rule - Add .opencode/plugins/node_modules/ to .gitignore
- Add opencode.json with Ollama Cloud provider (10 models, MiniMax M2.5 default) - Add AGENTS.md pointing to CLAUDE.md (both tools read both) - Add .opencode/commands/ wrappers for all 8 project skills (post-merge-cleanup, analyse-logs, research-link, review-dep-pr, worktree, pre-pr-review, aurelio-review-pr, codebase-audit) - Add .opencode/agents/design-token-audit.md (ported from .claude/agents/) - Add .opencode/plugins/synthorg-hooks.ts (calls same shell scripts) - Add .opencode/plugins/memory.ts (read/write shared MEMORY.md files) - Add .claude/agents/tool-parity-checker.md (verifies dual-tool parity) - Add tool-parity-checker to pre-pr-review and aurelio-review-pr agent rosters
… from opencode.json - Add Memory Directory section to AGENTS.md with generic path derivation - Add Shell Compatibility section to AGENTS.md (PowerShell vs bash) - Remove misplaced research-log.md (OpenCode wrote to wrong location) - Memory path reference moved to global config (never committed)
…pter commands - Add 21 agent definitions in .opencode/agents/ with model tiers: Tier 1 (minimax-m2.5): code-reviewer, security-reviewer, issue-resolution-verifier, silent-failure-hunter, async-concurrency Tier 2 (qwen3-coder-next): python, conventions, logging, resilience, frontend, persistence, test-quality, api-contract, type-design, go-reviewer, go-security, go-conventions Tier 3 (glm-4.7): docs-consistency, comment-analyzer, pr-test, infra - Add adapter headers to 4 commands (pre-pr-review, aurelio-review-pr, codebase-audit, worktree) mapping Claude Code subagent_type refs to .opencode/agents/ definitions - Fix 7 agent prompts with content gaps: security-reviewer: add frontend checks with correct severity docs-consistency: add 19-point DESIGN_SPEC checklist issue-resolution-verifier: add CRITICAL override for NOT_RESOLVED logging-audit: add coverage suggestion section resilience-audit: add provider-layer hard rules + manual retry detect test-quality-reviewer: add web/Vitest checks conventions-enforcer: elevate PEP 758 and __future__ severity - Add CLAUDE.md, web/CLAUDE.md, cli/CLAUDE.md to opencode.json instructions
- Document 4 hookify rules in CLAUDE.md (block-pr-create, enforce-parallel-tests, no-cd-prefix, no-local-coverage) - Document validation hook scripts in Scripts section (check_push_rebased.sh, check_bash_no_write.sh, check_git_c_cwd.sh, check_web_design_system.py) - Add OpenCode agents, commands, and plugins documentation to AGENTS.md - Implement 4 new hookify rules in synthorg-hooks.ts plugin (block-pr-create, enforce-parallel-tests, no-cd-prefix, no-local-coverage) Pre-reviewed by 2 agents, 7 findings addressed
…pilot - Hookify: fix regex patterns for pytest (bare pytest) and cd (whitespace bypass) - OpenCode: remove non-existent .claude/rules/common/ from instructions - Memory plugin: fix hardcoded path (derive from cwd), add path traversal protection - Hooks plugin: fix JSON parsing for check_push_rebased, fix error logging - Shell scripts: fix exit code alignment (check_git_c_cwd exits 2 on deny) - Block redirects to files (more comprehensive check_bash_no_write) - Skill: add auto-detection for CodeRabbit processing status - Agent docs: fix MD022 (blank lines around headings)
- SKILL.md: fix MD038 trailing space in code span - OpenCode agents: fix model identifiers, MD022/031/040 violations - memory.ts: fix path mangling (hyphens not underscores), YAML injection - synthorg-hooks.ts: fix git -C check (deny), pytest regex, PostToolUse throws, shell pipeline - check_bash_no_write.sh: fix ShellCheck regex, remove error masking - check_git_c_cwd.sh: document sed limitation
…s, plugins, scripts
d8536ab to
0d94de6
Compare
There was a problem hiding this comment.
Actionable comments posted: 20
♻️ Duplicate comments (14)
.opencode/commands/post-merge-cleanup.md (1)
1-7:⚠️ Potential issue | 🟡 MinorAdd an H1 after the frontmatter.
Line 5 is still the first content line, so this file continues to trigger MD041.
🛠️ Minimal fix
--- description: Post-merge branch cleanup --- +# Post-merge Cleanup + @.claude/skills/post-merge-cleanup/SKILL.md🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/commands/post-merge-cleanup.md around lines 1 - 7, The file has YAML frontmatter but no top-level heading after it, causing MD041; add an H1 immediately after the closing '---' (before the existing content line "@.claude/skills/post-merge-cleanup/SKILL.md") such as "# Post-merge branch cleanup" so the first content line is a proper H1 and the linter warning is resolved..opencode/commands/review-dep-pr.md (1)
1-7:⚠️ Potential issue | 🟡 MinorAdd an H1 after the frontmatter.
Line 5 is still the first content line, so this file continues to trigger MD041.
🛠️ Minimal fix
--- description: Review dependency update PRs --- +# Review Dependency PR + @.claude/skills/review-dep-pr/SKILL.md🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/commands/review-dep-pr.md around lines 1 - 7, Add a top-level H1 immediately after the YAML frontmatter so the file doesn't trigger MD041; e.g., insert a heading like "# Review dependency update PRs" on the first content line after the frontmatter block (currently where "Arguments: $ARGUMENTS" appears) so the document has an explicit H1 following the frontmatter..opencode/commands/research-link.md (1)
1-7:⚠️ Potential issue | 🟡 MinorAdd an H1 after the frontmatter.
Line 5 is still the first content line, so this file continues to trigger MD041.
🛠️ Minimal fix
--- description: Research any link, tool, or concept for this project --- +# Research Link + @.claude/skills/research-link/SKILL.md🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.opencode/commands/research-link.md around lines 1 - 7, The file's YAML frontmatter ends but no H1 follows, causing MD041; add a top-level heading immediately after the frontmatter (for example "# Research Link" or another appropriate title) so the first content line is an H1; place it before the existing reference line "@.claude/skills/research-link/SKILL.md" to ensure the H1 is the first content element after the frontmatter.scripts/check_git_c_cwd.sh (1)
16-48:⚠️ Potential issue | 🟠 MajorThe
git -Cdetector is still bypassable.Line 17 only matches
git -C ..., so forms likegit --no-pager -C "$PWD" statusandgit -c key=val -C "$PWD" statuspass straight through. Then Line 24 uses plain word-splitting, so quoted paths and$PWDare treated as literal text instead of a single resolved path. The deny rule ends up covering only baregit -C /literal/pathcommands.💡 Suggested direction
-# Not a git -C command -- no opinion -if [[ -z "$COMMAND" ]] || ! echo "$COMMAND" | grep -qE 'git[[:space:]]+-C[[:space:]]+'; then +if [[ -z "$COMMAND" ]]; then exit 0 fi -# Robust token-based parsing: split command into tokens, find -C, capture next token -GIT_C_PATH="" -in_git=false -for token in $COMMAND; do - if [[ "$token" == "git" ]]; then - in_git=true - elif [[ "$in_git" == true ]]; then - if [[ "$token" == "-C" ]] || [[ "$token" == "--git-dir" ]]; then - continue - elif [[ "$token" == -* ]]; then - # Skip other git options - continue - else - # This is the path after -C - GIT_C_PATH="$token" - break - fi - fi -done - -# Handle quoted paths -GIT_C_PATH="${GIT_C_PATH//\"/}" +GIT_C_PATH="$( +python3 - "$COMMAND" <<'PY' +import shlex, sys + +try: + tokens = shlex.split(sys.argv[1], posix=True) +except ValueError: + print("") + raise SystemExit(0) + +in_git = False +want_c_path = False +for token in tokens: + if not in_git: + in_git = token == "git" + continue + if want_c_path: + print(token) + break + if token == "-C": + want_c_path = True +PY +)" + +case "$GIT_C_PATH" in + '$PWD'|'${PWD}'|'$(pwd)'|'`pwd`') + GIT_C_PATH="$PWD" + ;; +esacRun the following script to verify the bypasses. Expected: the flag-before-
-Ccommands should still be detected, and the quoted path should stay a single token. Actual with the current implementation: the regex gate returnsFalsefor the first two commands and naive splitting breaks the quoted path.#!/bin/bash set -euo pipefail sed -n '16,48p' scripts/check_git_c_cwd.sh python3 - <<'PY' import re import shlex tests = [ 'git --no-pager -C "$PWD" status', 'git -c color.ui=always -C "$PWD" status', 'git -C "/tmp/my repo" status', ] pattern = re.compile(r'git[ \t]+-C[ \t]+') for command in tests: print(f"\nCOMMAND: {command}") print("regex_gate =", bool(pattern.search(command))) print("repo_word_split =", command.split()) print("shlex_tokens =", shlex.split(command)) PYscripts/check_bash_no_write.sh (1)
13-18:⚠️ Potential issue | 🟠 MajorJSON parse failures result in fail-open behavior.
If stdin contains malformed JSON,
jqexits non-zero with stderr suppressed by2>/dev/null, causing the script to exit 0 (allow). This is fail-open, which could let malformed inputs bypass the hook. The past review comment indicated this was addressed, but the current code still shows the same pattern.🛡️ Proposed fix for fail-closed on parse errors
# Try to extract command from JSON stdin (OpenCode mode), or skip check if no stdin -if ! COMMAND=$(jq -r '.tool_input.command // ""' 2>/dev/null); then - exit 0 +if ! COMMAND=$(jq -r '.tool_input.command // ""' 2>&1); then + echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Failed to parse tool input JSON"}}' >&2 + exit 2 fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check_bash_no_write.sh` around lines 13 - 18, The script currently treats jq parse failures as success (fail-open) because the error branch in "if ! COMMAND=$(jq -r '.tool_input.command // \"\"' 2>/dev/null); then exit 0 fi" exits 0; change this to fail-closed by detecting jq errors and exiting non-zero (e.g., exit 1) or printing an error before exit; ensure you still suppress jq stderr if desired but do not swallow the failure—update the conditional around the COMMAND assignment (the COMMAND variable and the jq invocation) so that a jq failure causes the hook to abort with a non-zero exit status rather than returning 0..claude/hookify.no-local-coverage.md (1)
8-8:⚠️ Potential issue | 🟠 MajorCatch direct
pytestinvocations in this coverage rule.Line 8 misses bare
pytest, so commands likepytest tests/ --cov=synthorgbypass the block.Proposed fix
- pattern: (?:run\s+pytest|python\s+-m\s+pytest)\b + pattern: (?:^|\s)(?:pytest|run\s+pytest|python\s+-m\s+pytest)\b🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.claude/hookify.no-local-coverage.md at line 8, The current regex pattern "(?:run\s+pytest|python\s+-m\s+pytest)\b" misses bare "pytest" invocations; update the pattern used (the "pattern" value) to also match plain "pytest" — e.g. replace with "(?:run\s+|python\s+-m\s+)?pytest\b" or "(?:pytest|run\s+pytest|python\s+-m\s+pytest)\b" so commands like "pytest tests/ --cov=..." are caught..opencode/agents/code-reviewer.md (1)
17-17:⚠️ Potential issue | 🟡 MinorFix markdownlint violations in headings and report fence.
Lines 17, 24, 30, 36, 43, and 49 need blank-line separation per MD022; Line 64 needs a fenced language and surrounding blank lines (MD031/MD040).
Proposed fix pattern
### 1. Correctness (HIGH) + - Logic errors, off-by-one, wrong comparisons ... ## Report Format For each finding: -``` + +```text [SEVERITY] file:line -- Brief description Problem: What the code does wrong Fix: What it should do insteadApply the same blank-line adjustment to each `###` subsection listed above. </details> Also applies to: 24-24, 30-30, 36-36, 43-43, 49-49, 64-64 <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/code-reviewer.md at line 17, Add required blank lines
around the Markdown headings and the report fence: insert a blank line before
and after each "###" subsection (e.g., the heading "### 1. Correctness (HIGH)"
and the other listed ### headings) to satisfy MD022, and convert the report
fence at the end (the triple-backtick block) to a fenced code block with
language "text" and ensure it has a blank line above and below (MD031/MD040)
using the proposed pattern; update the same changes for the other occurrences
referenced in the comment.</details> </blockquote></details> <details> <summary>.opencode/agents/type-design-analyzer.md (1)</summary><blockquote> `65-70`: _⚠️ Potential issue_ | _🟡 Minor_ **Add a language identifier to the report-format code fence.** Use a typed fence (e.g., ` ```text `) for the example block. <details> <summary>Suggested fix</summary> ```diff -``` +```text [SEVERITY] file:line -- Category Type: The type/model in question Problem: What design issue exists Fix: Better type design approach</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/type-design-analyzer.md around lines 65 - 70, Update the
example code fence for the report format to include a language identifier (e.g.,
change the plainfence totext) so the block is rendered as plain text;
locate the example block that currently shows the "[SEVERITY] file:line --
Category" template in the type/design analyzer doc (the triple-backtick fenced
example) and add the "text" language tag to the opening fence and keep the
matching closing fence.</details> </blockquote></details> <details> <summary>.opencode/commands/worktree.md (2)</summary><blockquote> `11-13`: _⚠️ Potential issue_ | _🟠 Major_ **Avoid documenting a `cd`-prefixed launch command that hooks block.** Line 12 still tells users to run `cd <path> && opencode-cli`, which conflicts with the no-cd-prefix rule introduced in this PR context. <details> <summary>Suggested fix</summary> ```diff -Wherever the skill says `claude` as a command to run, use `opencode-cli` instead: -- `cd <path> && claude` becomes `cd <path> && opencode-cli` +Wherever the skill says `claude` as a command to run, use `opencode-cli` instead: +- `cd <path> && claude` becomes `opencode-cli --cwd <path>` ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In @.opencode/commands/worktree.md around lines 11 - 13, The doc still instructs using a cd-prefixed launch command; remove the "cd <path> &&" prefix everywhere and replace instances of the old command name "claude" with "opencode-cli" (e.g. change "cd <path> && opencode-cli" to just "opencode-cli"), and rename any occurrences of the phrase "Claude Code prompts" to "OpenCode prompts" to match the new naming and the no-cd-prefix rule. ``` </details> --- `5-5`: _⚠️ Potential issue_ | _🟡 Minor_ **First heading should be H1 (MD041).** Line 5 still starts with `##`; make it `#` to clear markdownlint. <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In @.opencode/commands/worktree.md at line 5, The Markdown heading "OpenCode Adapter (read this FIRST, before the skill below)" is using a level-2 heading (##) and should be an H1 to satisfy MD041; update the heading token from "## OpenCode Adapter (read this FIRST, before the skill below)" to a single "#" at the start (i.e., "# OpenCode Adapter (read this FIRST, before the skill below)") so the heading becomes H1. ``` </details> </blockquote></details> <details> <summary>.claude/agents/tool-parity-checker.md (1)</summary><blockquote> `34-50`: _⚠️ Potential issue_ | _🟠 Major_ **Parity checks are still too narrow and can miss real drift.** Current logic only checks script references/file presence and requires an exact `"See CLAUDE.md"` phrase, but doesn’t ensure plugin registration in `opencode.json` and doesn’t trigger CLAUDE/AGENTS parity when `AGENTS.md` alone changes. </blockquote></details> <details> <summary>.opencode/agents/silent-failure-hunter.md (1)</summary><blockquote> `17-69`: _⚠️ Potential issue_ | _🟡 Minor_ **Fix markdown formatting issues flagged by linter.** The markdown formatting issues from the previous review have not been addressed: 1. Missing blank lines after subsection headings (lines 17, 23, 28, 33, 38, 43) 2. Code block at line 64 missing language specifier and blank line before it <details> <summary>📝 Proposed fix</summary> ```diff ### 1. Silent Except Blocks (HIGH) + - `except:` or `except Exception:` with only `pass` ... ### 2. Overly Broad Catches (HIGH) + - `except Exception:` catching too wide when specific exceptions are known ... For each finding: -``` + +```text [SEVERITY] file:line -- Pattern type ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/silent-failure-hunter.md around lines 17 - 69, Markdown
headings in .opencode/agents/silent-failure-hunter.md lack required blank lines
and the code block near the end is missing a language specifier and a preceding
blank line; fix by inserting a single blank line after each subsection heading
(e.g., after "### 1. Silent Except Blocks", "### 2. Overly Broad Catches", "###
3. Lost Error Context", "### 4. Suppressed Async Errors", "### 5. Conditional
Swallowing", "### 6. Logging Without Action") and ensure the code block
beginning with the report example has a blank line before the triple backticks
and a language tag (use "text") like ```text to satisfy the linter.</details> </blockquote></details> <details> <summary>AGENTS.md (2)</summary><blockquote> `48-51`: _⚠️ Potential issue_ | _🟡 Minor_ **Agent roster count doesn't match the list.** The section claims "21 review agents" but: - Quality tier lists 8 agents - Parallel tier lists 15 agents - `frontend-reviewer` appears in both tiers (duplicate) Total unique agents: 22 (or 23 entries). Update the count or deduplicate the list. <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` around lines 48 - 51, The AGENTS.md roster count is incorrect: the "Quality tier" and "Parallel tier" agent lists include a duplicate ("frontend-reviewer") and the stated "21 review agents" doesn't match the actual unique agents; update AGENTS.md so the total count equals the number of unique agent names, remove the duplicate "frontend-reviewer" from one tier (or intentionally note it if it must appear in both), and adjust the header number accordingly; verify the lists "Quality tier" and "Parallel tier" and the final agent count are consistent. ``` </details> --- `33-36`: _⚠️ Potential issue_ | _🟠 Major_ **Don't recommend PowerShell-specific syntax for shared skills.** Shared skills in `.claude/skills/` should remain shell-neutral or bash-based. Recommending `Select-String` over `grep` creates drift between Claude Code and OpenCode. Translation to PowerShell should happen in `.opencode/commands` adapters, not in shared skill content. <details> <summary>📝 Proposed fix</summary> ```diff -This project runs on Windows. OpenCode uses PowerShell, Claude Code uses bash. When writing shell commands in skills: -- Use PowerShell-compatible syntax (e.g., `ls` not `ls -la`, `Select-String` not `grep`) +This project runs on Windows. OpenCode uses PowerShell, Claude Code uses bash. When writing shell commands in shared skills (`.claude/skills/`): +- Use POSIX/bash-compatible or shell-neutral commands; OpenCode adapters (`.opencode/commands/`) handle PowerShell translation - Git commands work the same in both shells -- For bash-specific constructs (pipes, format strings), the model will self-correct to PowerShell equivalents +- For bash-specific constructs, the model or adapter will translate to PowerShell equivalents ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` around lines 33 - 36, The AGENTS.md guidance is pushing PowerShell-specific syntax into shared skill guidance (e.g., recommending Select-String instead of grep and advising PowerShell-compatible `ls` usage); update the document to keep shared skill instructions shell-neutral or bash-first by removing PowerShell-specific examples and replacing them with neutral/baseline bash examples (use `ls -la`, `grep`, pipes/format strings) and add a note that PowerShell translation must be handled in the OpenCode adapter layer (.opencode/commands) while .claude/skills remain shell-neutral; ensure references to "PowerShell-compatible syntax" and the Select-String example are removed or replaced and explicitly call out .claude/skills as bash/shared and .opencode/commands as the place for PowerShell conversions. ``` </details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.opencode/agents/api-contract-drift.md:
- Around line 17-52: Add a blank line after each subsection heading to satisfy
markdownlint MD022: insert an empty line after the headings "### 1. Endpoint
Consistency (HIGH)", "### 2. Type/Field Consistency (HIGH)", "### 3.
Request/Response Shape (HIGH)", "### 4. Auth Contract (MEDIUM)", "### 5. Error
Handling (MEDIUM)", and "### 6. Query Parameters (MEDIUM)" so each heading is
followed by one blank line before the list content.- Around line 69-75: Add a blank line immediately before the fenced code block
that starts with the snippet "[SEVERITY] Backend: file:line <-> Frontend:
file:line" and add a language specifier (use "text") after the opening backticks
(i.e., changetotext) so the block passes MD031 and MD040 checks; update
the markdown in .opencode/agents/api-contract-drift.md accordingly.In @.opencode/agents/async-concurrency-reviewer.md:
- Around line 17-72: Add missing blank lines after each heading (e.g., "### 1.
Race Conditions (HIGH)", "### 2. Resource Leaks (HIGH)", "### 3. TaskGroup
Patterns (MEDIUM)", "### 4. Blocking Calls in Async (HIGH)", "### 5.
Cancellation Safety (HIGH)", "### 6. Event Loop Misuse (MEDIUM)", "### 7.
Deadlocks (HIGH)") to satisfy MD022, and update the code fence shown at the end
(the triple backticks block that contains the example report) to include a
language identifier (use "text" or "markdown") and ensure there is a blank line
before and after the fenced block to satisfy MD031 and MD040.In @.opencode/agents/design-token-audit.md:
- Around line 1-9: The frontmatter in the design-token-audit agent spec is
missing a model field; add a line like "model: glm-4.7:cloud" to the YAML
frontmatter for consistency with other agent specs (or add a brief comment
indicating you intentionally rely on the default model), ensuring it appears
alongside existing keys such as description, mode, and permission so the parser
and reviewers see the chosen model explicitly.In @.opencode/agents/docs-consistency.md:
- Around line 21-56: Several Markdown headings in
.opencode/agents/docs-consistency.md (for example the "### Design pages in
docs/design/(CRITICAL -- project source of truth)" heading and other
third-level headings such as sections listing CLAUDE.md, README.md items) are
immediately followed by list items and trigger MD022; add a single blank line
after each heading to separate it from the subsequent list content. Update all
headings that precede bullet/numbered lists (e.g., the Design pages, CLAUDE.md,
and README.md section headings shown in the diff) so there's one blank line
between the heading line and the first list item to satisfy MD022.In @.opencode/agents/frontend-reviewer.md:
- Around line 17-77: Add missing blank lines after each heading (e.g., "### 1.
React 19 Patterns", "### 2. Component Design", etc.) and update the example code
fence at the end to include a language specifier (use ```text) and ensure there
is a blank line before and after the fenced block; modify the markdown around
the final example (the three-line severity example) to match the pattern used in
go-reviewer.md so MD022, MD031 and MD040 are resolved.In @.opencode/agents/go-conventions-enforcer.md:
- Line 4: Replace the model identifier string that currently includes the vendor
prefix (the line containing "model: ollama-cloud/qwen3-coder-next:cloud") with
the unprefixed official tag "qwen3-coder-next:cloud" so the value becomes
"model: qwen3-coder-next:cloud".In @.opencode/agents/issue-resolution-verifier.md:
- Around line 55-60: The fenced code block that currently begins with
and contains the four lines "Requirement:..." etc. is unlabeled and causing MD040; update that fence to include a language identifier (e.g., change the opening fence totext or ```txt) so the block becomes a labeled code fence; locate
the unlabeled fence in .opencode/agents/issue-resolution-verifier.md around the
snippet starting with the triple backticks and modify only the opening fence to
include the chosen language identifier.- Line 4: The model identifier is incorrectly prefixed as
"ollama-cloud/minimax-m2.5:cloud"; update the value used for the model spec (the
"model:" entry) to the correct Ollama format "minimax-m2.5:cloud" by removing
the "ollama-cloud/" prefix so the model line reads exactly "model:
minimax-m2.5:cloud".In @.opencode/agents/logging-audit.md:
- Around line 72-76: The fenced code block containing the sample log format
starting with "[SEVERITY] file:line -- Violation type" is missing a language
specifier; update that opening fence from "" to include a language tag (e.g., "text") so it reads "text" to ensure consistent syntax highlighting and
linting for the block.In @.opencode/agents/resilience-audit.md:
- Around line 75-76: Remove the duplicate "LOW" severity definition in the
resilience-audit.md list: locate the two consecutive bullets starting with
"LOW" (one reading "Soft rule violations, optimization opportunities" and
the other "Optimization opportunities, minor pattern improvements") and
consolidate them into a single, non-duplicated entry (either by keeping one and
merging the descriptions or removing the redundant line) so the LOW severity
appears only once.In @.opencode/agents/type-design-analyzer.md:
- Around line 17-50: The markdown violates MD022 because each section heading
(e.g., "1. Domain Modeling (HIGH)", "2. Invariant Enforcement (HIGH)", "3.
Encapsulation (MEDIUM)", "4. Pydantic Model Quality (HIGH)", "5. Type Safety
(MEDIUM)", "6. Inheritance Design (MEDIUM)") is not followed by a blank line;
fix by inserting a single blank line immediately after each of these headings so
that the subsequent list items start on a new paragraph line, ensuring MD022
compliance.In @.opencode/commands/analyse-logs.md:
- Around line 1-7: The file is missing a top-level heading after the YAML
frontmatter which triggers MD041; add an H1 line immediately after the closing
frontmatter marker (---) that reflects the page title (e.g., "Analyse Docker
container logs") so the content begins with a proper H1 before the reference
line '@.claude/skills/analyse-logs/SKILL.md' and the "Arguments: $ARGUMENTS"
line.In @.opencode/commands/aurelio-review-pr.md:
- Line 5: The first heading in the file is currently a level-2 heading ("##
OpenCode Adapter (read this FIRST, before the skill below)"); change it to a
level-1 heading by replacing the "##" with a single "#" so the line reads "#
OpenCode Adapter (read this FIRST, before the skill below)" to satisfy MD041;
ensure this is the first heading after the frontmatter and that no other H1s
precede it.In @.opencode/plugins/memory.ts:
- Around line 119-124: The code is casting the incoming type into
MemoryEntry["type"] without validation; add a validation step (e.g.,
isValidMemoryType or validateMemoryType) before constructing the entry to ensure
type is one of "user" | "feedback" | "project" | "reference" and either
reject/throw or map to a safe default if not; perform this check where the entry
is created (the const entry: MemoryEntry = { name, description, type: type as
MemoryEntry["type"], content }) and update callers to handle the validation
error or fallback behavior.In @.opencode/plugins/synthorg-hooks.ts:
- Around line 112-117: The catch block that currently checks for
result.includes("block") is too fragile; update the catch in synthorg-hooks.ts
(the try/catch around JSON.parse where the variable result is checked) to (1)
capture the parse error (catch (err)) and log or propagate as needed, and (2)
replace the brittle "block" substring check with the same "deny" detection used
by the other hook logic — e.g., test for the explicit deny pattern used
elsewhere (such as checking for '"deny"' or the exact deny token/pattern your
hooks use) and only throw when that deny pattern is present; do not treat any
occurrence of "block" as a block decision. Ensure you reference and use the same
deny detection routine or string/pattern the other hook handlers use so behavior
is consistent.- Around line 40-43: runHookScript currently returns null on non-zero status
which callers (they use "if (result)") treat the null as “allowed” and silently
proceed; change runHookScript to return a discriminated object (e.g., { allowed:
boolean, reason?: string } or { error: Error }) instead of null so callers can
distinguish execution failure from "disallowed", and update all callers that
invoked runHookScript (references: runHookScript, and scripts like
check_bash_no_write.sh / check_git_c_cwd.sh) to first check for an "error" or
execution-failed sentinel and throw or fail-closed, then check allowed and
reason to decide further action.In
@AGENTS.md:
- Around line 19-21: Add a language specifier to the fenced code block
containing "/.claude/projects//memory/" so markdownlint MD040 is/.claude/projects//memory/") to start with
satisfied; edit the fenced block in AGENTS.md (the block that currently shows
"text instead of
and keep the same content and closing backticks.In
@scripts/check_bash_no_write.sh:
- Around line 52-55: The grep pattern in the echo/printf redirect check only
matches filenames containing a dot, so commands like echo "data" > myfile slip
past it; update the pattern used on COMMAND (the
'\b(echo|printf)\b.>\s\S+.\S+' regex) to detect any non-whitespace target
(e.g. '\b(echo|printf)\b.>\s\S+') or otherwise remove the “extension required”
part, and ensure you still avoid false positives by accounting for
append/descriptor redirects (handle '>>' and '>&' cases) and keep the deny(...)
call intact so the deny function is triggered for bare filenames as well;
alternatively, add a short comment documenting the overlap with the general
redirect check for clarity if you choose not to change the regex.- Around line 34-37: The heredoc detection regex only matches delimiters
starting with letters/underscores and misses numeric or mixed-quoted delimiters;
update the grep pattern used on COMMAND (the grep -qE call) to allow digits and
optional surrounding quote characters so tokens like <<123 or <<-"'MIXED'" are
caught. Replace the existing pattern (used in the if that calls deny) with one
that accepts digits and optional repeated quote chars around the delimiter, e.g.
use something like <<-?\s*\?['"][A-Za-z0-9_]+['"] in the grep -qE invocation
so the check still matches heredoc forms while continuing to call deny "Do not
use heredocs..." when found.
Duplicate comments:
In @.claude/hookify.no-local-coverage.md:
- Line 8: The current regex pattern "(?:run\s+pytest|python\s+-m\s+pytest)\b"
misses bare "pytest" invocations; update the pattern used (the "pattern" value)
to also match plain "pytest" — e.g. replace with
"(?:run\s+|python\s+-m\s+)?pytest\b" or
"(?:pytest|run\s+pytest|python\s+-m\s+pytest)\b" so commands like "pytest tests/
--cov=..." are caught.In @.opencode/agents/code-reviewer.md:
- Line 17: Add required blank lines around the Markdown headings and the report
fence: insert a blank line before and after each "###" subsection (e.g., the
heading "### 1. Correctness (HIGH)" and the other listed ### headings) to
satisfy MD022, and convert the report fence at the end (the triple-backtick
block) to a fenced code block with language "text" and ensure it has a blank
line above and below (MD031/MD040) using the proposed pattern; update the same
changes for the other occurrences referenced in the comment.In @.opencode/agents/silent-failure-hunter.md:
- Around line 17-69: Markdown headings in
.opencode/agents/silent-failure-hunter.md lack required blank lines and the code
block near the end is missing a language specifier and a preceding blank line;
fix by inserting a single blank line after each subsection heading (e.g., after
"### 1. Silent Except Blocks", "### 2. Overly Broad Catches", "### 3. Lost Error
Context", "### 4. Suppressed Async Errors", "### 5. Conditional Swallowing",
"### 6. Logging Without Action") and ensure the code block beginning with the
report example has a blank line before the triple backticks and a language tag
(use "text") like ```text to satisfy the linter.In @.opencode/agents/type-design-analyzer.md:
- Around line 65-70: Update the example code fence for the report format to
include a language identifier (e.g., change the plainfence totext) so
the block is rendered as plain text; locate the example block that currently
shows the "[SEVERITY] file:line -- Category" template in the type/design
analyzer doc (the triple-backtick fenced example) and add the "text" language
tag to the opening fence and keep the matching closing fence.In @.opencode/commands/post-merge-cleanup.md:
- Around line 1-7: The file has YAML frontmatter but no top-level heading after
it, causing MD041; add an H1 immediately after the closing '---' (before the
existing content line "@.claude/skills/post-merge-cleanup/SKILL.md") such as "#
Post-merge branch cleanup" so the first content line is a proper H1 and the
linter warning is resolved.In @.opencode/commands/research-link.md:
- Around line 1-7: The file's YAML frontmatter ends but no H1 follows, causing
MD041; add a top-level heading immediately after the frontmatter (for example "#
Research Link" or another appropriate title) so the first content line is an H1;
place it before the existing reference line
"@.claude/skills/research-link/SKILL.md" to ensure the H1 is the first content
element after the frontmatter.In @.opencode/commands/review-dep-pr.md:
- Around line 1-7: Add a top-level H1 immediately after the YAML frontmatter so
the file doesn't trigger MD041; e.g., insert a heading like "# Review dependency
update PRs" on the first content line after the frontmatter block (currently
where "Arguments: $ARGUMENTS" appears) so the document has an explicit H1
following the frontmatter.In @.opencode/commands/worktree.md:
- Around line 11-13: The doc still instructs using a cd-prefixed launch command;
remove the "cd &&" prefix everywhere and replace instances of the old
command name "claude" with "opencode-cli" (e.g. change "cd &&
opencode-cli" to just "opencode-cli"), and rename any occurrences of the phrase
"Claude Code prompts" to "OpenCode prompts" to match the new naming and the
no-cd-prefix rule.- Line 5: The Markdown heading "OpenCode Adapter (read this FIRST, before the
skill below)" is using a level-2 heading (##) and should be an H1 to satisfy
MD041; update the heading token from "## OpenCode Adapter (read this FIRST,
before the skill below)" to a single "#" at the start (i.e., "# OpenCode Adapter
(read this FIRST, before the skill below)") so the heading becomes H1.In
@AGENTS.md:
- Around line 48-51: The AGENTS.md roster count is incorrect: the "Quality tier"
and "Parallel tier" agent lists include a duplicate ("frontend-reviewer") and
the stated "21 review agents" doesn't match the actual unique agents; update
AGENTS.md so the total count equals the number of unique agent names, remove the
duplicate "frontend-reviewer" from one tier (or intentionally note it if it must
appear in both), and adjust the header number accordingly; verify the lists
"Quality tier" and "Parallel tier" and the final agent count are consistent.- Around line 33-36: The AGENTS.md guidance is pushing PowerShell-specific
syntax into shared skill guidance (e.g., recommending Select-String instead of
grep and advising PowerShell-compatiblelsusage); update the document to keep
shared skill instructions shell-neutral or bash-first by removing
PowerShell-specific examples and replacing them with neutral/baseline bash
examples (usels -la,grep, pipes/format strings) and add a note that
PowerShell translation must be handled in the OpenCode adapter layer
(.opencode/commands) while .claude/skills remain shell-neutral; ensure
references to "PowerShell-compatible syntax" and the Select-String example are
removed or replaced and explicitly call out .claude/skills as bash/shared and
.opencode/commands as the place for PowerShell conversions.In
@scripts/check_bash_no_write.sh:
- Around line 13-18: The script currently treats jq parse failures as success
(fail-open) because the error branch in "if ! COMMAND=$(jq -r
'.tool_input.command // ""' 2>/dev/null); then exit 0 fi" exits 0; change this
to fail-closed by detecting jq errors and exiting non-zero (e.g., exit 1) or
printing an error before exit; ensure you still suppress jq stderr if desired
but do not swallow the failure—update the conditional around the COMMAND
assignment (the COMMAND variable and the jq invocation) so that a jq failure
causes the hook to abort with a non-zero exit status rather than returning 0.</details> <details> <summary>🪄 Autofix (Beta)</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: Repository UI **Review profile**: ASSERTIVE **Plan**: Pro **Run ID**: `1c5e4098-a7e1-45e7-b562-0c11b6279247` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between c4cb4538139eafbb0cdc821b9685480bcebd6c33 and 0d94de6e0b7606cd3248d1190e999ce763ab6ca6. </details> <details> <summary>📒 Files selected for processing (47)</summary> * `.claude/agents/tool-parity-checker.md` * `.claude/hookify.block-pr-create.md` * `.claude/hookify.enforce-parallel-tests.md` * `.claude/hookify.no-cd-prefix.md` * `.claude/hookify.no-local-coverage.md` * `.claude/skills/aurelio-review-pr/SKILL.md` * `.claude/skills/pre-pr-review/SKILL.md` * `.gitignore` * `.opencode/agents/api-contract-drift.md` * `.opencode/agents/async-concurrency-reviewer.md` * `.opencode/agents/code-reviewer.md` * `.opencode/agents/comment-analyzer.md` * `.opencode/agents/conventions-enforcer.md` * `.opencode/agents/design-token-audit.md` * `.opencode/agents/docs-consistency.md` * `.opencode/agents/frontend-reviewer.md` * `.opencode/agents/go-conventions-enforcer.md` * `.opencode/agents/go-reviewer.md` * `.opencode/agents/go-security-reviewer.md` * `.opencode/agents/infra-reviewer.md` * `.opencode/agents/issue-resolution-verifier.md` * `.opencode/agents/logging-audit.md` * `.opencode/agents/persistence-reviewer.md` * `.opencode/agents/pr-test-analyzer.md` * `.opencode/agents/python-reviewer.md` * `.opencode/agents/resilience-audit.md` * `.opencode/agents/security-reviewer.md` * `.opencode/agents/silent-failure-hunter.md` * `.opencode/agents/test-quality-reviewer.md` * `.opencode/agents/type-design-analyzer.md` * `.opencode/commands/analyse-logs.md` * `.opencode/commands/aurelio-review-pr.md` * `.opencode/commands/codebase-audit.md` * `.opencode/commands/post-merge-cleanup.md` * `.opencode/commands/pre-pr-review.md` * `.opencode/commands/research-link.md` * `.opencode/commands/review-dep-pr.md` * `.opencode/commands/worktree.md` * `.opencode/plugins/memory.ts` * `.opencode/plugins/synthorg-hooks.ts` * `.pre-commit-config.yaml` * `AGENTS.md` * `CLAUDE.md` * `opencode.json` * `scripts/check_bash_no_write.sh` * `scripts/check_git_c_cwd.sh` * `scripts/check_push_rebased.sh` </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| ### 1. Endpoint Consistency (HIGH) | ||
| - Frontend calling endpoints that don't exist in backend route definitions | ||
| - URL path mismatches (e.g., `/api/v1/agents` vs `/api/v1/agent`) | ||
| - HTTP method mismatches (frontend sends POST, backend expects PUT) | ||
| - Missing API version prefix in frontend calls | ||
|
|
||
| ### 2. Type/Field Consistency (HIGH) | ||
| - Frontend TypeScript types not matching backend Pydantic models | ||
| - Field name mismatches (e.g., `created_at` vs `createdAt` -- check serialization config) | ||
| - Missing fields in frontend types that backend returns | ||
| - Extra fields in frontend types that backend doesn't send | ||
| - Enum value mismatches between Python and TypeScript | ||
|
|
||
| ### 3. Request/Response Shape (HIGH) | ||
| - Frontend sending fields backend doesn't accept | ||
| - Backend returning nested objects frontend expects flat (or vice versa) | ||
| - Pagination parameter mismatches (offset/limit vs page/size) | ||
| - Missing envelope wrapper (backend returns `{data, error}`, frontend expects raw) | ||
|
|
||
| ### 4. Auth Contract (MEDIUM) | ||
| - Frontend not sending auth headers backend requires | ||
| - Token format mismatches (Bearer vs custom) | ||
| - Missing role checks frontend assumes backend enforces | ||
| - CSRF token handling inconsistencies | ||
|
|
||
| ### 5. Error Handling (MEDIUM) | ||
| - Frontend not handling error response format (RFC 9457) | ||
| - Missing error status code handling | ||
| - Frontend showing wrong error messages for specific status codes | ||
| - Validation error format mismatches | ||
|
|
||
| ### 6. Query Parameters (MEDIUM) | ||
| - Filter/sort parameters frontend sends that backend ignores | ||
| - Pagination defaults differing between frontend and backend | ||
| - Array parameter encoding mismatches | ||
|
|
There was a problem hiding this comment.
Add blank lines after subsection headings.
Markdownlint MD022 requires blank lines around headings. Add a blank line after each ### N. heading (lines 17, 23, 30, 36, 42, 48).
📝 Proposed fix (showing pattern for first two)
### 1. Endpoint Consistency (HIGH)
+
- Frontend calling endpoints that don't exist in backend route definitions
...
### 2. Type/Field Consistency (HIGH)
+
- Frontend TypeScript types not matching backend Pydantic models📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ### 1. Endpoint Consistency (HIGH) | |
| - Frontend calling endpoints that don't exist in backend route definitions | |
| - URL path mismatches (e.g., `/api/v1/agents` vs `/api/v1/agent`) | |
| - HTTP method mismatches (frontend sends POST, backend expects PUT) | |
| - Missing API version prefix in frontend calls | |
| ### 2. Type/Field Consistency (HIGH) | |
| - Frontend TypeScript types not matching backend Pydantic models | |
| - Field name mismatches (e.g., `created_at` vs `createdAt` -- check serialization config) | |
| - Missing fields in frontend types that backend returns | |
| - Extra fields in frontend types that backend doesn't send | |
| - Enum value mismatches between Python and TypeScript | |
| ### 3. Request/Response Shape (HIGH) | |
| - Frontend sending fields backend doesn't accept | |
| - Backend returning nested objects frontend expects flat (or vice versa) | |
| - Pagination parameter mismatches (offset/limit vs page/size) | |
| - Missing envelope wrapper (backend returns `{data, error}`, frontend expects raw) | |
| ### 4. Auth Contract (MEDIUM) | |
| - Frontend not sending auth headers backend requires | |
| - Token format mismatches (Bearer vs custom) | |
| - Missing role checks frontend assumes backend enforces | |
| - CSRF token handling inconsistencies | |
| ### 5. Error Handling (MEDIUM) | |
| - Frontend not handling error response format (RFC 9457) | |
| - Missing error status code handling | |
| - Frontend showing wrong error messages for specific status codes | |
| - Validation error format mismatches | |
| ### 6. Query Parameters (MEDIUM) | |
| - Filter/sort parameters frontend sends that backend ignores | |
| - Pagination defaults differing between frontend and backend | |
| - Array parameter encoding mismatches | |
| ### 1. Endpoint Consistency (HIGH) | |
| - Frontend calling endpoints that don't exist in backend route definitions | |
| - URL path mismatches (e.g., `/api/v1/agents` vs `/api/v1/agent`) | |
| - HTTP method mismatches (frontend sends POST, backend expects PUT) | |
| - Missing API version prefix in frontend calls | |
| ### 2. Type/Field Consistency (HIGH) | |
| - Frontend TypeScript types not matching backend Pydantic models | |
| - Field name mismatches (e.g., `created_at` vs `createdAt` -- check serialization config) | |
| - Missing fields in frontend types that backend returns | |
| - Extra fields in frontend types that backend doesn't send | |
| - Enum value mismatches between Python and TypeScript | |
| ### 3. Request/Response Shape (HIGH) | |
| - Frontend sending fields backend doesn't accept | |
| - Backend returning nested objects frontend expects flat (or vice versa) | |
| - Pagination parameter mismatches (offset/limit vs page/size) | |
| - Missing envelope wrapper (backend returns `{data, error}`, frontend expects raw) | |
| ### 4. Auth Contract (MEDIUM) | |
| - Frontend not sending auth headers backend requires | |
| - Token format mismatches (Bearer vs custom) | |
| - Missing role checks frontend assumes backend enforces | |
| - CSRF token handling inconsistencies | |
| ### 5. Error Handling (MEDIUM) | |
| - Frontend not handling error response format (RFC 9457) | |
| - Missing error status code handling | |
| - Frontend showing wrong error messages for specific status codes | |
| - Validation error format mismatches | |
| ### 6. Query Parameters (MEDIUM) | |
| - Filter/sort parameters frontend sends that backend ignores | |
| - Pagination defaults differing between frontend and backend | |
| - Array parameter encoding mismatches |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 17-17: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 23-23: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 30-30: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 36-36: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 42-42: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 48-48: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/api-contract-drift.md around lines 17 - 52, Add a blank
line after each subsection heading to satisfy markdownlint MD022: insert an
empty line after the headings "### 1. Endpoint Consistency (HIGH)", "### 2.
Type/Field Consistency (HIGH)", "### 3. Request/Response Shape (HIGH)", "### 4.
Auth Contract (MEDIUM)", "### 5. Error Handling (MEDIUM)", and "### 6. Query
Parameters (MEDIUM)" so each heading is followed by one blank line before the
list content.
| ``` | ||
| [SEVERITY] Backend: file:line <-> Frontend: file:line | ||
| Drift: Description of the inconsistency | ||
| Backend expects: X | ||
| Frontend sends/expects: Y | ||
| Fix: Which side to update | ||
| ``` |
There was a problem hiding this comment.
Add language specifier and blank line before code block.
The fenced code block is missing a language identifier (MD040) and should have a blank line before it (MD031).
📝 Proposed fix
For each finding:
-```
+
+```text
[SEVERITY] Backend: file:line <-> Frontend: file:line📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| [SEVERITY] Backend: file:line <-> Frontend: file:line | |
| Drift: Description of the inconsistency | |
| Backend expects: X | |
| Frontend sends/expects: Y | |
| Fix: Which side to update | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 69-69: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 69-69: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/api-contract-drift.md around lines 69 - 75, Add a blank
line immediately before the fenced code block that starts with the snippet
"[SEVERITY] Backend: file:line <-> Frontend: file:line" and add a language
specifier (use "text") after the opening backticks (i.e., change ``` to ```text)
so the block passes MD031 and MD040 checks; update the markdown in
.opencode/agents/api-contract-drift.md accordingly.
| ### 1. Race Conditions (HIGH) | ||
| - Shared mutable state accessed from multiple coroutines without locks | ||
| - Check-then-act patterns without atomicity (`if key not in dict: dict[key] = ...`) | ||
| - TOCTOU (time-of-check-time-of-use) on async resources | ||
| - Unprotected counters or accumulators in concurrent code | ||
|
|
||
| ### 2. Resource Leaks (HIGH) | ||
| - `aiohttp.ClientSession` created but not closed (missing `async with`) | ||
| - Database connections not returned to pool | ||
| - File handles opened in async context without `async with` | ||
| - Tasks created but never awaited or cancelled on shutdown | ||
|
|
||
| ### 3. TaskGroup Patterns (MEDIUM) | ||
| - Bare `create_task()` instead of `async with TaskGroup()` for fan-out | ||
| - Missing error handling when TaskGroup child tasks fail | ||
| - TaskGroup used where sequential execution was intended | ||
| - Exceptions from TaskGroup not properly propagated | ||
|
|
||
| ### 4. Blocking Calls in Async (HIGH) | ||
| - `time.sleep()` instead of `asyncio.sleep()` | ||
| - Synchronous I/O (file reads, `requests.get`) in async functions | ||
| - CPU-bound computation without `run_in_executor()` | ||
| - Blocking database calls in async context | ||
|
|
||
| ### 5. Cancellation Safety (HIGH) | ||
| - Catching `asyncio.CancelledError` without re-raising | ||
| - Missing cleanup in cancelled coroutines | ||
| - `shield()` used without understanding its semantics | ||
| - `wait_for()` timeout not handling cancellation of inner task | ||
|
|
||
| ### 6. Event Loop Misuse (MEDIUM) | ||
| - `asyncio.run()` called from within a running loop | ||
| - `loop.run_until_complete()` in async context | ||
| - Getting event loop with `get_event_loop()` instead of `get_running_loop()` | ||
| - Mixing sync and async APIs incorrectly | ||
|
|
||
| ### 7. Deadlocks (HIGH) | ||
| - Nested lock acquisition in different orders | ||
| - `await` inside a lock that calls back to code needing the same lock | ||
| - Unbounded queue producers with bounded queue consumers | ||
|
|
||
| ## Severity Levels | ||
|
|
||
| - **HIGH**: Race condition, deadlock, resource leak, blocking async | ||
| - **MEDIUM**: Suboptimal pattern, missing TaskGroup, minor safety issue | ||
| - **LOW**: Style preference, could-be-improved patterns | ||
|
|
||
| ## Report Format | ||
|
|
||
| For each finding: | ||
| ``` | ||
| [SEVERITY] file:line -- Concurrency issue type | ||
| Problem: What can go wrong under concurrency | ||
| Scenario: Concrete sequence of events causing the bug | ||
| Fix: Specific remediation | ||
| ``` |
There was a problem hiding this comment.
Fix markdownlint violations: add blank lines after headings and specify code fence language.
Static analysis reports MD022 (missing blank lines below headings at lines 17, 23, 29, 35, 41, 47, 53), MD031 (missing blank lines around code fence at line 67), and MD040 (missing code fence language at line 67).
Proposed fix
### 1. Race Conditions (HIGH)
+
- Shared mutable state accessed from multiple coroutines without locks
...
### 2. Resource Leaks (HIGH)
+
- `aiohttp.ClientSession` created but not closed (missing `async with`)
...
### 3. TaskGroup Patterns (MEDIUM)
+
- Bare `create_task()` instead of `async with TaskGroup()` for fan-out
...
### 4. Blocking Calls in Async (HIGH)
+
- `time.sleep()` instead of `asyncio.sleep()`
...
### 5. Cancellation Safety (HIGH)
+
- Catching `asyncio.CancelledError` without re-raising
...
### 6. Event Loop Misuse (MEDIUM)
+
- `asyncio.run()` called from within a running loop
...
### 7. Deadlocks (HIGH)
+
- Nested lock acquisition in different orders
...
For each finding:
-```
+
+```text
[SEVERITY] file:line -- Concurrency issue type
Problem: What can go wrong under concurrency
Scenario: Concrete sequence of events causing the bug
Fix: Specific remediation</details>
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.22.0)</summary>
[warning] 17-17: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
---
[warning] 23-23: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
---
[warning] 29-29: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
---
[warning] 35-35: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
---
[warning] 41-41: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
---
[warning] 47-47: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
---
[warning] 53-53: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
---
[warning] 67-67: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
---
[warning] 67-67: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/async-concurrency-reviewer.md around lines 17 - 72, Add
missing blank lines after each heading (e.g., "### 1. Race Conditions (HIGH)",
"### 2. Resource Leaks (HIGH)", "### 3. TaskGroup Patterns (MEDIUM)", "### 4.
Blocking Calls in Async (HIGH)", "### 5. Cancellation Safety (HIGH)", "### 6.
Event Loop Misuse (MEDIUM)", "### 7. Deadlocks (HIGH)") to satisfy MD022, and
update the code fence shown at the end (the triple backticks block that contains
the example report) to include a language identifier (use "text" or "markdown")
and ensure there is a blank line before and after the fenced block to satisfy
MD031 and MD040.
</details>
<!-- fingerprinting:phantom:poseidon:ocelot:d90fed7a-c1a1-4d01-b63c-8e951a8a8242 -->
<!-- This is an auto-generated comment by CodeRabbit -->
| --- | ||
| description: Audits web dashboard files for design token violations in animation (Framer Motion transitions) and density/spacing (card padding, section gaps, grid gaps, banner padding) | ||
| mode: subagent | ||
| permission: | ||
| Read: allow | ||
| Grep: allow | ||
| Glob: allow | ||
| --- | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding a model: field for consistency.
Unlike other agent specs in this PR (e.g., pr-test-analyzer.md, comment-analyzer.md), this file lacks a model: field in the frontmatter. If this is intentional (to use a default model), consider adding a comment. Otherwise, add model: glm-4.7:cloud for consistency.
♻️ Add model field for consistency
---
description: Audits web dashboard files for design token violations in animation (Framer Motion transitions) and density/spacing (card padding, section gaps, grid gaps, banner padding)
mode: subagent
+model: glm-4.7:cloud
permission:
Read: allow
Grep: allow
Glob: allow
---📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| --- | |
| description: Audits web dashboard files for design token violations in animation (Framer Motion transitions) and density/spacing (card padding, section gaps, grid gaps, banner padding) | |
| mode: subagent | |
| permission: | |
| Read: allow | |
| Grep: allow | |
| Glob: allow | |
| --- | |
| --- | |
| description: Audits web dashboard files for design token violations in animation (Framer Motion transitions) and density/spacing (card padding, section gaps, grid gaps, banner padding) | |
| mode: subagent | |
| model: glm-4.7:cloud | |
| permission: | |
| Read: allow | |
| Grep: allow | |
| Glob: allow | |
| --- |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/design-token-audit.md around lines 1 - 9, The frontmatter
in the design-token-audit agent spec is missing a model field; add a line like
"model: glm-4.7:cloud" to the YAML frontmatter for consistency with other agent
specs (or add a brief comment indicating you intentionally rely on the default
model), ensuring it appears alongside existing keys such as description, mode,
and permission so the parser and reviewers see the chosen model explicitly.
| ### Design pages in `docs/design/` (CRITICAL -- project source of truth) | ||
|
|
||
| 1. `design/agents.md` "Project Structure" -- does it match actual files/directories under `src/synthorg/`? Any new modules missing? Any listed files that no longer exist? (CRITICAL) | ||
| 2. `design/agents.md` "Agent Identity Card" -- does the config/runtime split documentation match the actual model code? (MAJOR) | ||
| 3. `design/agents.md` "Key Design Decisions" -- are technology choices and rationale still accurate? (MAJOR) | ||
| 4. `design/agents.md` "Pydantic Model Conventions" -- do documented conventions match how models are actually written? Are "Adopted" vs "Planned" labels still accurate? (MAJOR) | ||
| 5. `design/operations.md` "Cost Tracking" -- does the implementation note match actual `TokenUsage` and spending summary models? (MAJOR) | ||
| 6. `design/engine.md` "Tool Execution Model" -- does it match actual `ToolInvoker` behavior? (MAJOR) | ||
| 7. `docs/architecture/tech-stack.md` "Technology Stack" -- are versions, libraries, and rationale current? (MEDIUM) | ||
| 8. `design/operations.md` "Provider Configuration" -- are model IDs, provider capability examples, and config/runtime mapping still representative? (MEDIUM) | ||
| 9. `design/operations.md` "LiteLLM Integration" -- does the integration status match reality? (MEDIUM) | ||
| 10. Any other section that describes behavior, structure, or patterns that have changed (MAJOR) | ||
|
|
||
| ### CLAUDE.md (CRITICAL -- guides all future development) | ||
|
|
||
| 11. Code Conventions -- do documented patterns match what's actually in the code? New patterns used but not documented? Documented patterns no longer followed? (CRITICAL) | ||
| 12. Logging section -- are event import paths, logger patterns, and rules accurate? (CRITICAL) | ||
| 13. Resilience section -- does it match actual retry/rate-limit implementation? (MAJOR) | ||
| 14. Package Structure -- does it match actual directory layout? (MAJOR) | ||
| 15. Testing section -- are markers, commands, and conventions current? (MEDIUM) | ||
| 16. Any other section that gives instructions that don't match reality (CRITICAL) | ||
|
|
||
| ### README.md | ||
|
|
||
| 17. Installation, usage, and getting-started instructions -- still accurate? (MAJOR) | ||
| 18. Feature descriptions -- do they match what's actually built? (MEDIUM) | ||
| 19. Links -- any dead links or references to things that moved? (MINOR) | ||
|
|
||
| ## Severity Levels | ||
|
|
||
| - **CRITICAL**: Documentation actively misleading about project conventions or architecture | ||
| - **MAJOR**: Documentation incomplete, stale, or describes removed features | ||
| - **MEDIUM**: Minor inaccuracies, outdated versions, formatting | ||
| - **MINOR**: Wording improvements, dead links | ||
|
|
||
| ## Report Format |
There was a problem hiding this comment.
Fix heading spacing to clear MD022 warnings.
Several headings are immediately followed by list content; insert a blank line after each heading (e.g., around Line 21, Line 34, Line 43, Line 49, Line 56).
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 25-25: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 33-33: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 39-39: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 47-47: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 53-53: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/agents/docs-consistency.md around lines 21 - 56, Several Markdown
headings in .opencode/agents/docs-consistency.md (for example the "### Design
pages in `docs/design/` (CRITICAL -- project source of truth)" heading and other
third-level headings such as sections listing CLAUDE.md, README.md items) are
immediately followed by list items and trigger MD022; add a single blank line
after each heading to separate it from the subsequent list content. Update all
headings that precede bullet/numbered lists (e.g., the Design pages, CLAUDE.md,
and README.md section headings shown in the diff) so there's one blank line
between the heading line and the first list item to satisfy MD022.
| if (result.status !== 0) { | ||
| // Fail closed: non-zero, non-2 exit codes are errors | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Script execution errors fail open — non-zero exits are silently ignored.
When runHookScript returns null (lines 42, 52), callers like lines 106, 127, and 138 check if (result) which evaluates to false for null, causing them to silently proceed. This means if check_bash_no_write.sh or check_git_c_cwd.sh crashes or returns exit code 1, the action is allowed.
Consider throwing an error or returning a distinct sentinel to differentiate "script allowed action" from "script failed to execute."
🛡️ Proposed fix to fail closed on script errors
function runHookScript(
scriptPath: string,
toolInput: Record<string, unknown>,
timeoutMs: number = 10000,
-): string | null {
+): { allowed: true; stdout: string } | { allowed: false; reason: string } | { error: true } {
try {
const input = JSON.stringify({ tool_input: toolInput });
const result = spawnSync("bash", [scriptPath], {
input,
timeout: timeoutMs,
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
});
if (result.status === 2) {
// Hook denied the action
- return result.stdout ?? "Hook denied this action";
+ return { allowed: false, reason: result.stdout || "Hook denied this action" };
}
if (result.status !== 0) {
// Fail closed: non-zero, non-2 exit codes are errors
- return null;
+ return { error: true };
}
- return result.stdout;
+ return { allowed: true, stdout: result.stdout || "" };
} catch (error: unknown) {
- const err = error as { status?: number; stdout?: string };
- if (err.status === 2) {
- // Hook denied the action
- return err.stdout ?? "Hook denied this action";
- }
// Fail closed on catch errors too
- return null;
+ return { error: true };
}
}Then callers would check:
const result = runHookScript(...);
if ("error" in result) {
throw new Error(`Hook script failed: ${scriptPath}`);
}
if (!result.allowed) {
throw new Error(result.reason);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/plugins/synthorg-hooks.ts around lines 40 - 43, runHookScript
currently returns null on non-zero status which callers (they use "if (result)")
treat the null as “allowed” and silently proceed; change runHookScript to return
a discriminated object (e.g., { allowed: boolean, reason?: string } or { error:
Error }) instead of null so callers can distinguish execution failure from
"disallowed", and update all callers that invoked runHookScript (references:
runHookScript, and scripts like check_bash_no_write.sh / check_git_c_cwd.sh) to
first check for an "error" or execution-failed sentinel and throw or
fail-closed, then check allowed and reason to decide further action.
| } catch { | ||
| // Not JSON or parse error - check for legacy "block" pattern | ||
| if (result.includes("block")) { | ||
| throw new Error(result); | ||
| } | ||
| } |
There was a problem hiding this comment.
Legacy "block" fallback in JSON parse catch is fragile.
When JSON.parse fails, the code falls back to checking if the raw output contains "block". This is unreliable — if the script output happens to contain the word "block" in any context (like an error message), it would incorrectly block the push. The script should output valid JSON consistently, or this fallback should use the same "deny" check pattern as other hooks.
📝 Proposed fix
} catch {
// Not JSON or parse error - check for legacy "block" pattern
- if (result.includes("block")) {
+ if (result.includes("deny")) {
throw new Error(result);
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.opencode/plugins/synthorg-hooks.ts around lines 112 - 117, The catch block
that currently checks for result.includes("block") is too fragile; update the
catch in synthorg-hooks.ts (the try/catch around JSON.parse where the variable
result is checked) to (1) capture the parse error (catch (err)) and log or
propagate as needed, and (2) replace the brittle "block" substring check with
the same "deny" detection used by the other hook logic — e.g., test for the
explicit deny pattern used elsewhere (such as checking for '"deny"' or the exact
deny token/pattern your hooks use) and only throw when that deny pattern is
present; do not treat any occurrence of "block" as a block decision. Ensure you
reference and use the same deny detection routine or string/pattern the other
hook handlers use so behavior is consistent.
| ``` | ||
| ~/.claude/projects/<mangled-cwd>/memory/ | ||
| ``` |
There was a problem hiding this comment.
Add language specifier to fenced code block.
The code block is missing a language identifier, which triggers markdownlint MD040.
📝 Proposed fix
-```
+```text
~/.claude/projects/<mangled-cwd>/memory/</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 19-19: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` around lines 19 - 21, Add a language specifier to the fenced code
block containing "~/.claude/projects/<mangled-cwd>/memory/" so markdownlint
MD040 is satisfied; edit the fenced block in AGENTS.md (the block that currently
shows "~/.claude/projects/<mangled-cwd>/memory/") to start with ```text instead
of ``` and keep the same content and closing backticks.
| # Heredocs anywhere in command: << EOF, << 'EOF', <<"EOF", <<\EOF, <<-EOF, <<-'PLAN_EOF' | ||
| if printf '%s\n' "$COMMAND" | grep -qE '<<-?\s*\\?'"'"'?"?[A-Za-z_]'; then | ||
| deny "Do not use heredocs (<< EOF) to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification." | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Test the heredoc regex against various patterns
PATTERN='<<-?\s*\\?'"'"'?"?[A-Za-z_]'
test_cases=(
'cat <<EOF'
'cat <<"EOF"'
"cat <<'EOF'"
'cat <<\EOF'
'cat <<-EOF'
'cat <<123'
'python - <<"PY"'
)
for tc in "${test_cases[@]}"; do
if printf '%s\n' "$tc" | grep -qE "$PATTERN"; then
echo "MATCH: $tc"
else
echo "MISS: $tc"
fi
doneRepository: Aureliolo/synthorg
Length of output: 192
🏁 Script executed:
#!/bin/bash
# Test additional edge cases for the heredoc regex
PATTERN='<<-?\s*\\?'"'"'?"?[A-Za-z_]'
test_cases=(
'cat <<-"'"'"'MIXED'"'"'"' # Mixed quotes - single inside double'
'cat <<-'"'"'"'"MIXED'"'"' # Nested quotes - double inside single'
'cat <<456' # Numeric delimiter
'cat <<_VAR' # Starting with underscore (allowed)'
'cat <<9VAR' # Starting with number (invalid delim)'
)
for tc in "${test_cases[@]}"; do
# Extract just the command part (before the # if present)
cmd="${tc%% #*}"
if printf '%s\n' "$cmd" | grep -qE "$PATTERN"; then
echo "MATCH: $cmd"
else
echo "MISS: $cmd"
fi
doneRepository: Aureliolo/synthorg
Length of output: 162
Heredoc detection misses numeric and mixed-quote delimiters.
The regex <<-?\s*\\?'"'"'?"?[A-Za-z_] only matches delimiters starting with letters or underscores. It fails to catch:
- Numeric delimiters:
cat <<123 - Mixed-quote delimiters:
cat <<-"'MIXED'"(valid bash syntax where the delimiter includes the quote characters themselves)
These are uncommon edge cases, but valid heredoc syntax that would bypass the check.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/check_bash_no_write.sh` around lines 34 - 37, The heredoc detection
regex only matches delimiters starting with letters/underscores and misses
numeric or mixed-quoted delimiters; update the grep pattern used on COMMAND (the
grep -qE call) to allow digits and optional surrounding quote characters so
tokens like <<123 or <<-"'MIXED'" are caught. Replace the existing pattern (used
in the if that calls deny) with one that accepts digits and optional repeated
quote chars around the delimiter, e.g. use something like
<<-?\s*\\?['"]*[A-Za-z0-9_]+['"]* in the grep -qE invocation so the check still
matches heredoc forms while continuing to call deny "Do not use heredocs..."
when found.
| # echo/printf > filename.ext (catches echo "text" > file.txt) | ||
| if printf '%s\n' "$COMMAND" | grep -qE '\b(echo|printf)\b.*>\s*\S+\.\S+'; then | ||
| deny "Do not use echo/printf with redirects to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification." | ||
| fi |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
The echo/printf redirect check only catches files with extensions.
The pattern \b(echo|printf)\b.*>\s*\S+\.\S+ requires a dot in the filename, so echo "data" > myfile (no extension) bypasses this specific check. However, this is likely caught by the general redirect check on lines 42-50, so this is a defense-in-depth gap rather than a full bypass.
♻️ Consider removing the extension requirement or documenting the overlap
# echo/printf > filename.ext (catches echo "text" > file.txt)
-if printf '%s\n' "$COMMAND" | grep -qE '\b(echo|printf)\b.*>\s*\S+\.\S+'; then
+if printf '%s\n' "$COMMAND" | grep -qE '\b(echo|printf)\b.*>\s*\S+'; then
deny "Do not use echo/printf with redirects to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification."
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # echo/printf > filename.ext (catches echo "text" > file.txt) | |
| if printf '%s\n' "$COMMAND" | grep -qE '\b(echo|printf)\b.*>\s*\S+\.\S+'; then | |
| deny "Do not use echo/printf with redirects to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification." | |
| fi | |
| # echo/printf > filename.ext (catches echo "text" > file.txt) | |
| if printf '%s\n' "$COMMAND" | grep -qE '\b(echo|printf)\b.*>\s*\S+'; then | |
| deny "Do not use echo/printf with redirects to write files. Use the Write tool to create new files or the Edit tool to modify existing files. Never use Bash for file creation or modification." | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/check_bash_no_write.sh` around lines 52 - 55, The grep pattern in the
echo/printf redirect check only matches filenames containing a dot, so commands
like echo "data" > myfile slip past it; update the pattern used on COMMAND (the
'\b(echo|printf)\b.*>\s*\S+\.\S+' regex) to detect any non-whitespace target
(e.g. '\b(echo|printf)\b.*>\s*\S+') or otherwise remove the “extension required”
part, and ensure you still avoid false positives by accounting for
append/descriptor redirects (handle '>>' and '>&' cases) and keep the deny(...)
call intact so the deny function is triggered for bare filenames as well;
alternatively, add a short comment documenting the overlap with the general
redirect check for clarity if you choose not to change the regex.
- scripts/check_bash_no_write.sh: fail-closed on jq parse errors (exit 2) - hookify.no-local-coverage: catch bare pytest invocations - worktree.md: use --cwd flag instead of cd prefix, H1 heading - AGENTS.md: shell-neutral guidance, correct model names, fixed agent count - .opencode/commands/*.md: add H1 after frontmatter
🤖 I have created a release *beep* *boop* --- ## [0.6.3](v0.6.2...v0.6.3) (2026-04-06) ### Features * backend CRUD + multi-user permissions ([#1081](#1081), [#1082](#1082)) ([#1094](#1094)) ([93e469b](93e469b)) * in-dashboard team editing + budget rebalance on pack apply ([#1093](#1093)) ([35977c0](35977c0)), closes [#1079](#1079) [#1080](#1080) * tiered rate limiting, NotificationSink protocol, in-dashboard notifications ([#1092](#1092)) ([df2142c](df2142c)), closes [#1077](#1077) [#1078](#1078) [#849](#849) * two-stage safety classifier and cross-provider uncertainty check for approval gates ([#1090](#1090)) ([0b2edee](0b2edee)), closes [#847](#847) [#701](#701) ### Refactoring * memory pipeline improvements ([#1075](#1075), [#997](#997)) ([#1091](#1091)) ([a048a4c](a048a4c)) ### Documentation * add OpenCode parity setup and hookify rule documentation ([#1095](#1095)) ([52e877a](52e877a)) ### Maintenance * bump vite from 8.0.3 to 8.0.4 in /web in the all group across 1 directory ([#1088](#1088)) ([1e86ca6](1e86ca6)) * tune ZAP DAST scan -- auth, timeouts, rules, report artifacts ([#1097](#1097)) ([82bf0e1](82bf0e1)), closes [#1096](#1096) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Summary
Test Plan
Review Coverage
Pre-reviewed by 2 agents (docs-consistency, tool-parity-checker), 7 findings addressed (4 MAJOR, 2 MEDIUM, 1 MINOR).