Skip to content

Copier update: claude config#16

Merged
ejfine merged 2 commits intomainfrom
cop-claude
Mar 24, 2026
Merged

Copier update: claude config#16
ejfine merged 2 commits intomainfrom
cop-claude

Conversation

@ejfine
Copy link
Contributor

@ejfine ejfine commented Mar 24, 2026

Pull in upstream template changes

Summary by CodeRabbit

  • New Features

    • Claude Code integration with repo-level configuration and command templates
    • Beads task-tracking with a Dolt-backed backend and local ignore rules
    • Added merge-claude-settings pre-commit hook to consolidate Claude settings
  • Documentation

    • Added AGENTS.md and many Claude command templates (TDD, red/green/refactor, issues, ADRs, etc.)
  • Infrastructure

    • DevContainer updated for Claude/Beads and Dolt service; CI/workflow action versions bumped; permission configs added; updated .gitignore
  • Tests

    • Added unit tests for configuration tooling

@coderabbitai
Copy link

coderabbitai bot commented Mar 24, 2026

Warning

Ignoring CodeRabbit configuration file changes. For security, only the configuration from the base branch is applied for open source repositories.

📝 Walkthrough

Walkthrough

This PR adds Claude/Beads integration (Dolt-backed issue tracking), a large set of Claude command workflow specs (TDD, issue creation, polish, research, etc.), project-wide Claude settings/permissions and merge tooling, devcontainer support with a Dolt service and conditional initialization, CI updates (unit tests, action versions), pre-commit hook changes, and many template updates.

Changes

Cohort / File(s) Summary
Beads / Dolt config & ignore
.\.claude/.beads/.*, template/.claude/.beads/.*
Add Dolt/Beads metadata/config (metadata.json, config.yaml) and .gitignore for local beads/dolt artifacts and JSONL dump files.
Claude command specs
.claude/commands/*.md, template/.claude/commands/*.md
Add ~18 command definition docs (red/green/refactor/tdd/spike/research/create-issues/create-adr/issue/gap/polish/simplify/summarize/tdd-review/etc.) with strict rules (no plan.md, TDD wording constraints) and usage guidance.
Claude settings & permissions
.claude/settings/*, template/.claude/settings/*
Add JSONC settings for basics and permission profiles (bash/read/write) controlling allowed/ask/deny command patterns.
Merge helper & package
.claude/helpers/merge-claude-settings.sh, .claude/package.json, template/.claude/helpers/merge-claude-settings.sh, template/.claude/package.json
Add script to merge JSON/JSONC settings using json5/jq with deduplication and sorting; add package.json declaring json5 dependency.
Devcontainer & Dolt service
.devcontainer/*, template/.devcontainer/*, .devcontainer/docker-compose.yml
Add Claude devcontainer feature, VS Code extensions bumps, add beads-dolt Dolt SQL server service and BEADS_* env vars, npm install step and bd CLI install, and conditional Dolt init/restore in post-start.
Pre-commit hook & copier tasks
.pre-commit-config.yaml, copier.yml, src/copier_tasks/remove_precommit_hooks.py
Add merge-claude-settings hook and replace specialized pre-commit-hook-removal with a generalized regex-driven remover; update copier tasks to conditionally remove hooks.
CI / GitHub Actions
.github/workflows/ci.yaml, .github/workflows/pre-commit.yaml, .github/reusable_workflows/build-docker-image.yaml, .github/actions/install_deps/action.yml, template/.github/actions/*
Add unit-test job (pytest matrix), tighten required-check dependencies, update action versions, introduce full-image-tag output and ecr-auth composite action, and change Pulumi preview artifact behavior.
Tooling pins & context
.devcontainer/install-ci-tooling.py, extensions/context.py, pyproject.toml, .copier-answers.yml
Bump pinned tooling versions (uv, pnpm, copier, pytest-cov, ty), update context template versions, bump copier template revision and enable install_claude_cli.
Coverage & tests
.coveragerc, tests/unit/copier_tasks/test_remove_precommit_hooks.py
Add coverage config (branch=true, fail_under=100, exclusions) and unit tests for the pre-commit-hook removal script.
Repository docs & ignores
AGENTS.md, CLAUDE.md, .gitignore, template/AGENTS.md, template/CLAUDE.md, template/.gitignore
Add AGENTS.md and CLAUDE.md, expand template docs, and update gitignore to exclude .claude settings and Dolt artifacts.
Other template updates
many template/... files (devcontainer, actions, command templates)
Mirror repository changes into template artifacts (devcontainer, actions, command templates, helper scripts, pre-commit template).

Sequence Diagram(s)

sequenceDiagram
    participant Devcontainer
    participant PreCommit as "pre-commit"
    participant BD as "bd CLI"
    participant Dolt as "Dolt Server"
    participant Git as "git"

    Devcontainer->>PreCommit: run merge-claude-settings hook
    Devcontainer->>BD: bd ready
    alt bd ready success
        Devcontainer->>Git: proceed (safe.directory configured)
    else bd ready fails
        Devcontainer->>BD: bd init --server-host $BEADS_DOLT_SERVER_HOST --database $BEADS_DOLT_SERVER_DATABASE --skip-hooks --stealth --prefix=work
        BD->>Dolt: initialize/create database
        Devcontainer->>Git: git -c core.hooksPath=/dev/null restore .claude/.beads (restore from HEAD)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description 'Pull in upstream template changes' is minimal and generic, providing no detail about which changes, why they are needed, testing approach, or side effects as required by the template. Expand the description to include: specific changes being pulled, rationale for the update, testing performed, and any side effects. Use all sections from the template: Link to Issue, Why, How, Side effects, Testing, and Other.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Copier update: claude config' accurately and concisely describes the main change—updating Claude-related configuration as part of a Copier template refresh.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 34

Caution

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

⚠️ Outside diff range comments (1)
template/.devcontainer/docker-compose.yml.jinja (1)

24-43: ⚠️ Potential issue | 🔴 Critical

Unnest postgres from install_claude_cli.

The current Jinja layout opens {% if has_backend %} while {% if install_claude_cli %} is still active, so postgres only renders when both flags are true. Backend projects with Claude CLI disabled will lose their database service, even though the matching backend volumes still render.

Also applies to: 55-58

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

In `@template/.devcontainer/docker-compose.yml.jinja` around lines 24 - 43, The
postgres service block is accidentally nested inside the install_claude_cli
conditional so it only renders when both install_claude_cli and has_backend are
true; move the has_backend conditional so that the postgres service (the
"postgres" service, its environment vars and the "postgres_data" volume) is
guarded only by has_backend and not by install_claude_cli, and apply the same
un-nesting to the other duplicate postgres block later in the file to ensure
backend projects get the DB service even when install_claude_cli is false.
🤖 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/commands/commit.md:
- Line 6: The file .claude/commands/commit.md triggers MD041 because the first
non-frontmatter line is a level-2 heading; add a top-level level-1 heading as
the very first line of the file (above the frontmatter or replace the existing
first heading) so the document begins with an H1 (e.g., "# Commit Commands") to
satisfy the markdown lint rule.

In @.claude/commands/create-adr.md:
- Around line 38-59: The ADR detection loop (for dir in doc/adr docs/adr
decisions doc/architecture/decisions) treats the first existing directory as
valid even if it contains zero ADR files; update the logic so after finding a
directory you also verify it contains ADR markdown files (e.g., check for *.md
or a numbered ADR like 0001.md) and only break/accept that directory when files
exist—if the dir is empty, continue the loop (or trigger the initialization
flow) so empty ADR directories are treated as uninitialized; reference the
existing detection loop and the directory list when making the change.
- Around line 223-235: The documentation mixes the enum status value
`superseded` with free-text instructions that write `superseded by ADR-NNNN`
into the same `status` field; change the workflow to keep `status` as an enum
(one of `proposed`, `accepted`, `deprecated`, `superseded`) and add a separate
field for the reference (e.g., `superseded_by` or `replaced_by`) so tooling
still sees a fixed enum; update the "Status Values" table and the two-step "To
supersede an existing ADR" instructions to say “Update old ADR's status to
`superseded` and set `superseded_by: ADR-NNNN`” and ensure the new field name is
consistently used in examples and guidance.

In @.claude/commands/create-issues.md:
- Around line 61-64: The fenced code block containing the error message "GitHub
CLI not available or not authenticated! Run: gh auth login" lacks a language
specifier; update the triple-backtick fence to include a plaintext language
(e.g., ```text) so the block becomes ```text ... ``` to satisfy linting and
ensure consistent rendering.

In @.claude/commands/gap.md:
- Line 6: MD041 is raised because the document's first heading is level 2 ("##
General Guidelines"); update the file so the top-level heading is level 1 by
changing "## General Guidelines" to "# General Guidelines" (or add a level-1
heading above the existing content) to ensure a single top-level heading and
satisfy markdown heading hierarchy rules.

In @.claude/commands/green.md:
- Line 6: Add a top-level heading at the start of the document (an H1, e.g., "#
Green") and update the ordered list referenced (the list at Line 62) to use the
configured "1/1/1" numbering style (use "1." for every item instead of
incremental numbers) so the file conforms to MD041 and MD029; modify the
markdown in green.md by inserting the H1 at the top and replacing the list item
numbers in that ordered list with "1." for each entry.

In @.claude/commands/red.md:
- Line 6: The file currently starts with a level-2 heading "**User arguments:**"
which fails MD041; add a top-level H1 heading above the existing content (for
example a descriptive title for this command file) so the document begins with
`# ...` and keep the existing "**User arguments:**" as-is below it; update the
file that contains the "**User arguments:**" header to insert the H1 at the very
top.

In @.claude/commands/refactor.md:
- Line 6: Add a top-level H1 heading line at the very start of the document
(before the first existing "##" section) to satisfy MD041; edit the file's
content so the first non-blank line is an H1 (e.g., a descriptive title like
"Refactor Commands") and keep the existing "##" sections unchanged.

In @.claude/commands/spike.md:
- Around line 33-103: The "TDD Fundamentals" block duplicated in this file
should be extracted into a single shared doc (suggested name:
.claude/commands/_tdd-fundamentals.md) and replaced here and in other files
(e.g., tdd.md, spike.md) with an include/import reference; locate the "TDD
Fundamentals" heading and its entire content in spike.md, move that content into
the new _tdd-fundamentals.md, then update spike.md (and the other command files
containing the same block) to reference the shared file using the project's
include/import mechanism so future edits are single-source.

In @.claude/commands/tdd-review.md:
- Around line 76-96: The fenced code block containing the "## Test Quality
Report" example lacks a language specifier, so add a language identifier (e.g.,
markdown or md) immediately after the opening triple backticks (the "```" that
precedes the "## Test Quality Report" block) so the block becomes "```markdown"
to enable proper syntax highlighting in renderers; update only that opening
fence in the block shown in the diff.

In @.claude/helpers/merge-claude-settings.sh:
- Around line 44-53: The current inline npx/node JSON5 parsing call assigned to
parsed_json can silently produce "{}" on failures and the try/catch masks
npx/node errors; update the logic around the parsed_json assignment (the
variable name parsed_json and the inline node invocation) to detect and handle
npx/node failures and invalid output: run the npx/node command and check its
exit status and/or stderr, ensure parsed_json is non-empty and valid JSON (e.g.,
non-empty and not just "{}" if that indicates failure in your flow), and if
validation fails print a clear error like "Error: Failed to parse any settings
files" and exit with non-zero status so the merge step does not proceed. Ensure
you reference the inline node/json5 invocation when adding the checks so
failures to install or run json5 are caught.
- Line 54: The variable expansion for settings_files is unquoted which can split
filenames with spaces; change the handling to treat settings_files as an array
and use quoted expansion instead — populate and use an array variable
(settings_files or settings_files[@]) when building the list and replace the
unquoted expansion (the occurrence of " $settings_files)" in the script) with a
quoted array expansion ("${settings_files[@]}") so filenames with spaces or
special characters are preserved.

In @.claude/settings/permissions/bash.jsonc:
- Around line 91-92: The current deny entries "Bash(rm -rf / *)" and "Bash(rm
-rf ~ *)" only match when there is a space after / or ~; update the deny
patterns in .claude/settings/permissions/bash.jsonc to also block common
no-space forms (e.g., "Bash(rm -rf /*)", "Bash(rm -rf ~/*)", and variants
without trailing wildcards) and consider adding regex-style or broader patterns
that cover paths like /tmp and ~/projects so commands such as rm -rf /tmp and rm
-rf ~/projects are caught by the deny list.

In @.claude/settings/permissions/write.jsonc:
- Line 6: Replace the broad write permission string "Write(/tmp/**)" with a
tool-specific subtree (for example "Write(/tmp/<tool-name>/**)") in the JSON
permission entry so writes are restricted to a dedicated temp directory; update
any tooling/config that expects the old path to use the new subdirectory and
ensure the code that creates temp files (references to /tmp in your codebase)
creates and uses that dedicated /tmp/<tool-name> directory with the correct
ownership/permissions.

In @.devcontainer/docker-compose.yml:
- Around line 29-35: Add a Docker healthcheck to the beads-dolt service and make
the devcontainer depend on service_healthy so startup waits for a healthy Dolt
instance: update the beads-dolt service definition to include a healthcheck
(using an appropriate check such as running dolt or a simple SQL/status probe)
and set the devcontainer's depends_on to refer to beads-dolt: condition:
service_healthy (or uses the newer depends_on: beads-dolt: condition:
service_healthy pattern) so post-start scripts like post-start-command.sh run
only after beads-dolt is healthy; reference the beads-dolt service name and the
healthcheck/depends_on keys when making the changes.

In @.devcontainer/post-start-command.sh:
- Around line 8-14: The script .devcontainer/post-start-command.sh mixes tabs
and spaces (normalize to spaces) and contains a hard-to-debug chained command
using bd init && git restore; update indentation to use spaces consistently
(replace the tab on the echo line), then refactor the long one-liner that
invokes bd init --server-host="$BEADS_DOLT_SERVER_HOST"
--database="$BEADS_DOLT_SERVER_DATABASE" --skip-hooks --stealth --prefix=work
and the subsequent git -c core.hooksPath=/dev/null restore --source=HEAD
--staged --worktree .claude/.beads into a clearer multi-line form: either split
into sequential commands with explicit error handling (run git restore only on
success of bd init) or keep the original short-circuit semantics by using
line-continuation backslashes with the && preserved; ensure the conditional
behavior (run restore only if bd init succeeds) remains correct depending on
intent.

In `@CLAUDE.md`:
- Line 1: Add a top-level H1 heading to the top of CLAUDE.md to satisfy
markdownlint rule MD041 (e.g., insert a single line beginning with "# " and a
concise title such as "AGENTS" or "Claude" at the very start of the file) so the
file begins with an H1 heading.

In `@template/.claude/.beads/.gitignore`:
- Around line 53-54: The comment above the ignore patterns is stale: update the
explanatory comment to state that interactions.jsonl, issues.jsonl,
issues-dump.jsonl, and the backup/ directory are explicitly ignored (rather than
“tracked by git by default”), so replace the existing lines describing
JSONL/config tracking with a brief note that those JSONL files and backup/ are
intentionally ignored to match the patterns (references: interactions.jsonl,
issues.jsonl, issues-dump.jsonl, backup/).

In `@template/.claude/.beads/metadata.json`:
- Around line 1-8: The metadata.json currently has redundant fields "database"
and "backend" both set to "dolt"; decide whether both are required and either
remove the unnecessary field or document the duplication for clarity. Update the
"database" or "backend" key in template/.claude/.beads/metadata.json accordingly
(remove one of the keys if unused, or add a short comment or inline README note
explaining that both are kept intentionally for backward compatibility/schema
requirements) and ensure references elsewhere (any code reading "database" or
"backend") are updated to use the remaining canonical key.

In `@template/.claude/commands/create-adr.md`:
- Around line 223-235: The Status field currently mixes an enum value
(`superseded`) with an instruction to write free text (`superseded by
ADR-NNNN`), which will break tools expecting the enum; update the guidance so
the Status column remains one of the enumerated values (e.g., keep `superseded`)
and move the free-text instruction into a separate field or into the ADR body
(e.g., add a note like “set status to `superseded` and record the superseding
ADR number in the ADR body or a new `supersedes` field”); ensure the table row
for `superseded` and the numbered steps reference the same approach so tooling
sees only the enum value in `status`.
- Around line 38-59: The directory-detection loop (for dir in doc/adr docs/adr
decisions doc/architecture/decisions; do ...) currently treats the first
existing directory as populated even when it has zero ADR files; update that
loop to verify the directory actually contains ADR markdown files before
breaking: use a file-presence check such as compgen -G "$dir"/*.md >/dev/null
2>&1 (or a find "$dir" -maxdepth 1 -type f -name '*.md' check) and only
echo/list and break when that check succeeds, otherwise continue so the
initialization flow (Step 1b) runs for empty ADR directories.

In `@template/.claude/commands/create-issues.md`:
- Around line 45-57: The gh command in Step 2 omits repository context and
doesn't request linked-issue data, so update the gh invocation used to fetch the
issue (the "gh issue view [ISSUE_NUMBER]" call) to include the extracted
owner/repo via --repo owner/repo and also request linked-issue information;
e.g., change to use gh issue view [ISSUE_NUMBER] --repo owner/repo --json
title,body,labels,comments,state,linkedIssues, and if your gh version doesn’t
support linkedIssues then fetch linked issues separately with gh api (e.g., GET
/repos/{owner}/{repo}/issues/{number}/timeline or the appropriate linked-issues
endpoint) so Step 3 ("Linked issues → Dependencies") has the required data.

In `@template/.claude/commands/issue.md`:
- Around line 44-49: Update the fenced code block that displays the CLI error
message by adding a language specifier so it becomes a plain-text block; replace
the opening ``` with ```text for the block containing "GitHub CLI not available
or not authenticated!" and "Run: gh auth login" so markdown linting passes.
- Around line 22-51: The markdown uses three separate numbered lists (each
starting with "1.") under the headings "Get Issue Number", "Fetch Issue", and
"Analyze and Plan", which breaks sequential numbering; update the template so
the steps form a single continuous ordered list (1. Get Issue Number → 2. Fetch
Issue → 3. Analyze and Plan) or convert the step titles into headers and keep a
single ordered list under "Process", editing the section text around the "Get
Issue Number", the gh CLI example, and "Analyze and Plan" to preserve flow and
correct numbering.

In `@template/.claude/commands/refactor.md`:
- Around line 125-127: The numbered list item "1. **Consistency check**" is
orphaned and incomplete; either finish the list or remove/convert this line into
a complete standalone sentence or section header. Locate the line containing the
numbered list entry "1. **Consistency check**" and: if it belongs in "Watch for
Brittle Tests," expand it with the intended explanation or subsequent list
items; otherwise delete the numbering and make it a full sentence (e.g.,
"Consistency check — look for inconsistent patterns, naming conventions, or
structure") or remove the line entirely to avoid a dangling list item.

In `@template/.claude/commands/tdd-review.md`:
- Around line 74-96: The markdown example block starting with "## Test Quality
Report" should include a language specifier; replace the opening triple
backticks in the example with "```markdown" so the block is explicitly marked as
markdown (update the code block that begins before "## Test Quality Report" in
template/.claude/commands/tdd-review.md).

In `@template/.claude/commands/tdd.md`:
- Around line 96-102: Replace the explicit "TDD: $ARGUMENTS" label with a
neutral label such as "User input: $ARGUMENTS" in the template block containing
"**User arguments:**" and the subsequent continuation line ("Please continue
with the user input above, applying TDD approach."); update any occurrences of
the literal "TDD: $ARGUMENTS" so forwarding uses "User input:" to avoid
injecting the banned term while preserving the same argument variable and the
continuation instruction.

In `@template/.claude/helpers/merge-claude-settings.sh`:
- Around line 44-54: The parsed_json assignment in merge-claude-settings.sh uses
an unquoted $settings_files which can split filenames with spaces; change how
settings files are passed to the node invocation by populating settings_files as
a Bash array (e.g., with mapfile/readarray or explicit array building) and then
supply the entries safely (e.g., "${settings_files[@]}" or via process
substitution) to the node command that produces parsed_json so each filename is
treated as a single argument; ensure you update the parsed_json line to
reference the array form instead of the unquoted $settings_files.

In `@template/.devcontainer/post-start-command.sh.jinja`:
- Around line 6-14: Normalize indentation in the Jinja template block around the
bd-related logic (remove mixed tabs/spaces around the bd ready check and the rm
-rf/.beads bd init/git restore sequence) and refactor the complex chained
command on the bd init line into explicit sequential steps: first run `bd ready`
and test its exit code, then run `rm -rf .claude/.beads`, then run `bd init`
with the explicit flags (pass "$BEADS_DOLT_SERVER_HOST" and
"$BEADS_DOLT_SERVER_DATABASE"), check its exit status, and finally run `git -c
core.hooksPath=/dev/null restore --source=HEAD --staged --worktree
.claude/.beads`; ensure each step uses clear indentation and explicit if/else or
&& checks instead of a long one-liner so errors are easier to handle and the
template remains consistent with the non-template
`.devcontainer/post-start-command.sh` refactor.

In `@template/.github/actions/pulumi_ephemeral_deploy/action.yml`:
- Around line 88-104: The heredoc used to write /tmp/pulumi-preview-comment.md
preserves the indentation and causes leading spaces in the generated markdown;
update the two cat heredocs that write $header and the fenced code block (the
blocks using cat > /tmp/pulumi-preview-comment.md << EOF) to either use the
indented-strip form <<-EOF or remove the leading indentation from the heredoc
body so $header and the fenced block (with $output) are written without leading
whitespace; ensure both the long-output fallback heredoc is updated the same way
so markdown code fences render correctly.
- Line 76: The workflow currently references the action
"aws-actions/configure-aws-credentials@v6.0.0" which requires runners with
Node.js 24 (GitHub Actions runner v2.327.1+); either update your runner
environments to v2.327.1 or later (e.g., ensure GitHub-hosted ubuntu-latest or
self-hosted runners are upgraded) or change the action reference to a
pre-Node24-compatible tag such as "aws-actions/configure-aws-credentials@v5" to
retain compatibility—make the change where the uses string
"aws-actions/configure-aws-credentials@v6.0.0" appears in the action definition.

In `@template/CLAUDE.md`:
- Line 1: Add a top-level H1 heading as the very first line of the file so the
reference token "@AGENTS.md" is no longer the first token and markdownlint MD041
passes; insert a line like "# CLAUDE" (or another appropriate H1 title) above
the existing "@AGENTS.md" token in template/CLAUDE.md.

In `@tests/unit/copier_tasks/test_remove_precommit_hooks.py`:
- Around line 29-33: The test currently copies the repo's live pre-commit config
via source_config/_PROJECT_ROOT and shutil.copyfile which couples assertions to
the checked-in file; instead, create small, explicit YAML fixture(s) directly in
tmp_path by writing known content to config_path (use
config_path.write_text(..., encoding="utf-8")), then read it into original and
assert against the controlled fixture values; apply the same replacement for the
other similar blocks (the ones currently using shutil.copyfile/source_config) so
all assertions target inline fixtures rather than the repository file.

---

Outside diff comments:
In `@template/.devcontainer/docker-compose.yml.jinja`:
- Around line 24-43: The postgres service block is accidentally nested inside
the install_claude_cli conditional so it only renders when both
install_claude_cli and has_backend are true; move the has_backend conditional so
that the postgres service (the "postgres" service, its environment vars and the
"postgres_data" volume) is guarded only by has_backend and not by
install_claude_cli, and apply the same un-nesting to the other duplicate
postgres block later in the file to ensure backend projects get the DB service
even when install_claude_cli is false.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fd08c22d-5096-44a4-ab82-5dbde6ed9eb2

📥 Commits

Reviewing files that changed from the base of the PR and between 6eb2eb7 and b96b384.

⛔ Files ignored due to path filters (3)
  • .claude/package-lock.json is excluded by !**/package-lock.json
  • template/.claude/package-lock.json is excluded by !**/package-lock.json
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (87)
  • .claude/.beads/.gitignore
  • .claude/.beads/config.yaml
  • .claude/.beads/metadata.json
  • .claude/commands/add-command.md
  • .claude/commands/commit.md
  • .claude/commands/create-adr.md
  • .claude/commands/create-issues.md
  • .claude/commands/gap.md
  • .claude/commands/green.md
  • .claude/commands/issue.md
  • .claude/commands/polish.md
  • .claude/commands/red.md
  • .claude/commands/refactor.md
  • .claude/commands/research.md
  • .claude/commands/simplify.md
  • .claude/commands/spike.md
  • .claude/commands/summarize.md
  • .claude/commands/tdd-review.md
  • .claude/commands/tdd.md
  • .claude/helpers/merge-claude-settings.sh
  • .claude/package.json
  • .claude/settings/basics.jsonc
  • .claude/settings/permissions/bash.jsonc
  • .claude/settings/permissions/read.jsonc
  • .claude/settings/permissions/write.jsonc
  • .coderabbit.yaml
  • .copier-answers.yml
  • .coveragerc
  • .devcontainer/devcontainer.json
  • .devcontainer/docker-compose.yml
  • .devcontainer/install-ci-tooling.py
  • .devcontainer/on-create-command.sh
  • .devcontainer/post-start-command.sh
  • .github/actions/install_deps/action.yml
  • .github/reusable_workflows/build-docker-image.yaml
  • .github/workflows/ci.yaml
  • .github/workflows/pre-commit.yaml
  • .gitignore
  • .pre-commit-config.yaml
  • AGENTS.md
  • CLAUDE.md
  • copier.yml
  • extensions/context.py
  • pyproject.toml
  • src/copier_tasks/__init__.py
  • src/copier_tasks/remove_precommit_hooks.py
  • template/.claude/.beads/.gitignore
  • template/.claude/.beads/config.yaml
  • template/.claude/.beads/metadata.json
  • template/.claude/commands/add-command.md
  • template/.claude/commands/commit.md
  • template/.claude/commands/create-adr.md
  • template/.claude/commands/create-issues.md
  • template/.claude/commands/gap.md
  • template/.claude/commands/green.md
  • template/.claude/commands/issue.md
  • template/.claude/commands/polish.md
  • template/.claude/commands/red.md
  • template/.claude/commands/refactor.md
  • template/.claude/commands/research.md
  • template/.claude/commands/simplify.md
  • template/.claude/commands/spike.md
  • template/.claude/commands/summarize.md
  • template/.claude/commands/tdd-review.md
  • template/.claude/commands/tdd.md
  • template/.claude/helpers/merge-claude-settings.sh
  • template/.claude/package.json
  • template/.claude/settings/basics.jsonc
  • template/.claude/settings/permissions/bash.jsonc
  • template/.claude/settings/permissions/read.jsonc
  • template/.claude/settings/permissions/write.jsonc
  • template/.coderabbit.yaml
  • template/.devcontainer/devcontainer.json.jinja
  • template/.devcontainer/docker-compose.yml.jinja
  • template/.devcontainer/on-create-command.sh.jinja
  • template/.devcontainer/post-start-command.sh.jinja
  • template/.github/actions/ecr-auth/action.yml.jinja
  • template/.github/actions/install_deps/action.yml
  • template/.github/actions/pulumi_ephemeral_deploy/action.yml
  • template/.github/workflows/pre-commit.yaml
  • template/.github/workflows/pulumi-aws.yml
  • template/.gitignore
  • template/.pre-commit-config.yaml
  • template/AGENTS.md
  • template/CLAUDE.md
  • tests/unit/copier_tasks/__init__.py
  • tests/unit/copier_tasks/test_remove_precommit_hooks.py

argument-hint: [optional-commit-description]
---

## General Guidelines
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a top-level heading to satisfy markdown lint.

The file starts with frontmatter and then a level-2 heading, which triggers MD041 (first-line-heading) in your lint config.

Proposed fix
 ---
 description: Create a git commit following project standards
 argument-hint: [optional-commit-description]
 ---
 
+# Commit
+
 ## General Guidelines
📝 Committable suggestion

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

Suggested change
## General Guidelines
---
description: Create a git commit following project standards
argument-hint: [optional-commit-description]
---
# Commit
## General Guidelines
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 6-6: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

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

In @.claude/commands/commit.md at line 6, The file .claude/commands/commit.md
triggers MD041 because the first non-frontmatter line is a level-2 heading; add
a top-level level-1 heading as the very first line of the file (above the
frontmatter or replace the existing first heading) so the document begins with
an H1 (e.g., "# Commit Commands") to satisfy the markdown lint rule.

Comment on lines +38 to +59
Check for existing ADR directory and structure:

```bash
# Check common ADR directories (in order of preference)
for dir in doc/adr docs/adr decisions doc/architecture/decisions; do
if [ -d "$dir" ]; then
echo "Found: $dir"
ls "$dir"/*.md 2>/dev/null
break
fi
done
```

**If ADRs exist:** Read the first few ADRs (especially 0001 if present) to understand:

- The template/structure this project uses
- Any project-specific sections or frontmatter
- Naming conventions and style

Adapt the new ADR to match the existing pattern.

**If no ADR directory exists:** Run the initialization flow (Step 1b).
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Treat empty ADR directories as uninitialized.

The detection loop stops on the first existing directory even when it contains no ADR files. In that case the workflow skips the initialization path but still has no template or numbering baseline to follow.

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

In @.claude/commands/create-adr.md around lines 38 - 59, The ADR detection loop
(for dir in doc/adr docs/adr decisions doc/architecture/decisions) treats the
first existing directory as valid even if it contains zero ADR files; update the
logic so after finding a directory you also verify it contains ADR markdown
files (e.g., check for *.md or a numbered ADR like 0001.md) and only
break/accept that directory when files exist—if the dir is empty, continue the
loop (or trigger the initialization flow) so empty ADR directories are treated
as uninitialized; reference the existing detection loop and the directory list
when making the change.

Comment on lines +223 to +235
## Status Values

| Status | When to Use |
|--------|-------------|
| `proposed` | Under discussion, not yet agreed |
| `accepted` | Agreed upon and should be followed |
| `deprecated` | No longer relevant (context changed) |
| `superseded` | Replaced by another ADR (link to it) |

To supersede an existing ADR:

1. Create new ADR with the updated decision
2. Update old ADR's status to `superseded by ADR-NNNN`
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep status enumerable when superseding an ADR.

This file defines superseded as the valid status value, but the follow-up instruction writes superseded by ADR-NNNN into the same field. That turns status into free text and will break any tooling that relies on the documented enum.

🧰 Tools
🪛 LanguageTool

[style] ~227-~227: ‘Under discussion’ might be wordy. Consider a shorter alternative.
Context: ...|--------|-------------| | proposed | Under discussion, not yet agreed | | accepted | Agreed...

(EN_WORDINESS_PREMIUM_UNDER_DISCUSSION)

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

In @.claude/commands/create-adr.md around lines 223 - 235, The documentation
mixes the enum status value `superseded` with free-text instructions that write
`superseded by ADR-NNNN` into the same `status` field; change the workflow to
keep `status` as an enum (one of `proposed`, `accepted`, `deprecated`,
`superseded`) and add a separate field for the reference (e.g., `superseded_by`
or `replaced_by`) so tooling still sees a fixed enum; update the "Status Values"
table and the two-step "To supersede an existing ADR" instructions to say
“Update old ADR's status to `superseded` and set `superseded_by: ADR-NNNN`” and
ensure the new field name is consistently used in examples and guidance.

Comment on lines +61 to +64
```
GitHub CLI not available or not authenticated!
Run: gh auth login
```
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add language specifier to fenced code block.

The error message code block lacks a language specifier. Since this is plain text output, use text or plaintext to satisfy linting and improve rendering consistency.

📝 Proposed fix
-```
+```text
 GitHub CLI not available or not authenticated!
 Run: gh auth login
</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.21.0)

[warning] 61-61: 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 @.claude/commands/create-issues.md around lines 61 - 64, The fenced code
block containing the error message "GitHub CLI not available or not
authenticated! Run: gh auth login" lacks a language specifier; update the
triple-backtick fence to include a plaintext language (e.g., ```text) so the
block becomes ```text ... ``` to satisfy linting and ensure consistent
rendering.

argument-hint: [optional additional info]
---

## General Guidelines
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a top-level heading for markdown compliance.

MD041 is triggered because the first heading is level 2.

Proposed fix
 ---
 description: Analyze conversation context for unaddressed items and gaps
 argument-hint: [optional additional info]
 ---
 
+# Gap Analysis
+
 ## General Guidelines
📝 Committable suggestion

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

Suggested change
## General Guidelines
---
description: Analyze conversation context for unaddressed items and gaps
argument-hint: [optional additional info]
---
# Gap Analysis
## General Guidelines
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 6-6: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

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

In @.claude/commands/gap.md at line 6, MD041 is raised because the document's
first heading is level 2 ("## General Guidelines"); update the file so the
top-level heading is level 1 by changing "## General Guidelines" to "# General
Guidelines" (or add a level-1 heading above the existing content) to ensure a
single top-level heading and satisfy markdown heading hierarchy rules.

Comment on lines +44 to +54
parsed_json=$(npx -y -p json5 node -e "
const fs = require('fs');
const JSON5 = require('json5');
process.argv.slice(1).forEach(file => {
try {
console.log(JSON.stringify(JSON5.parse(fs.readFileSync(file, 'utf8'))));
} catch {
console.log('{}');
}
});
" $settings_files)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unquoted variable risks word splitting on filenames with spaces.

Line 54 passes $settings_files unquoted, which breaks if any filename contains spaces or glob characters. Consider using a bash array or process substitution for robustness.

🛠️ Proposed fix using mapfile/readarray
-# Find all .json and .jsonc files in settings/ directory (excluding settings.json)
-# Files are sorted alphabetically to ensure consistent merge order
-settings_files=$(find settings/ -type f \( -name '*.json' -o -name '*.jsonc' \) ! -name 'settings.json' | sort)
+# Find all .json and .jsonc files in settings/ directory (excluding settings.json)
+# Files are sorted alphabetically to ensure consistent merge order
+mapfile -t settings_files < <(find settings/ -type f \( -name '*.json' -o -name '*.jsonc' \) ! -name 'settings.json' | sort)

-if [ -z "$settings_files" ]; then
+if [ ${`#settings_files`[@]} -eq 0 ]; then
     echo "No settings files found in settings/ directory"
     exit 0
 fi

Then update line 54:

-" $settings_files)
+" "${settings_files[@]}")
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 54-54: Double quote to prevent globbing and word splitting.

(SC2086)

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

In `@template/.claude/helpers/merge-claude-settings.sh` around lines 44 - 54, The
parsed_json assignment in merge-claude-settings.sh uses an unquoted
$settings_files which can split filenames with spaces; change how settings files
are passed to the node invocation by populating settings_files as a Bash array
(e.g., with mapfile/readarray or explicit array building) and then supply the
entries safely (e.g., "${settings_files[@]}" or via process substitution) to the
node command that produces parsed_json so each filename is treated as a single
argument; ensure you update the parsed_json line to reference the array form
instead of the unquoted $settings_files.

Comment on lines +6 to +14
git config --global --add safe.directory /workspaces/{% endraw %}{{ repo_name }}{% if install_claude_cli %}{% raw %}
pre-commit run merge-claude-settings -a
if ! bd ready; then
echo "It's likely the Dolt server has not yet been initialized to support beads, running that now" # TODO: figure out a better way to match this specific scenario than just a non-zero exit code...but beads still seems like in high flux right now so not sure what to tie it to
# the 'stealth' flag is just the only way I could figure out how to stop it from modifying AGENTS.md...if there's another way to avoid that, then fine. Even without the stealth flag though, files inside the .claude/beads directory get modified, so restoring them at the end to what was set in git...these shouldn't really need to change regularly
# trying to set 'prefix' to nothing doesn't seem to work (it just acts like the prefix flag wasn't there), so just setting to 'work' as an arbitrary name
# for some reason, the envvar for the server host isn't being picked up normally, so just passing it explicitly here
rm -rf .claude/.beads && bd init --server-host="$BEADS_DOLT_SERVER_HOST" --database="$BEADS_DOLT_SERVER_DATABASE" --skip-hooks --stealth --prefix=work </dev/null && git -c core.hooksPath=/dev/null restore --source=HEAD --staged --worktree .claude/.beads
fi{% endraw %}{% endif %}{% if python_package_registry is defined and python_package_registry != "PyPI" %}{% raw %}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Same indentation and complexity concerns as the non-template version.

This template has the same issues noted in .devcontainer/post-start-command.sh:

  • Mixed tab/space indentation on Line 9
  • Complex command chain on Line 13

Apply the same refactoring suggested for the non-template version to maintain consistency between the two files.

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

In `@template/.devcontainer/post-start-command.sh.jinja` around lines 6 - 14,
Normalize indentation in the Jinja template block around the bd-related logic
(remove mixed tabs/spaces around the bd ready check and the rm -rf/.beads bd
init/git restore sequence) and refactor the complex chained command on the bd
init line into explicit sequential steps: first run `bd ready` and test its exit
code, then run `rm -rf .claude/.beads`, then run `bd init` with the explicit
flags (pass "$BEADS_DOLT_SERVER_HOST" and "$BEADS_DOLT_SERVER_DATABASE"), check
its exit status, and finally run `git -c core.hooksPath=/dev/null restore
--source=HEAD --staged --worktree .claude/.beads`; ensure each step uses clear
indentation and explicit if/else or && checks instead of a long one-liner so
errors are easier to handle and the template remains consistent with the
non-template `.devcontainer/post-start-command.sh` refactor.

Comment on lines +88 to +104
cat > /tmp/pulumi-preview-comment.md << EOF
$header

\`\`\`bash
$output
\`\`\`
EOF

if [ $(wc -c < /tmp/pulumi-preview-comment.md) -gt 65535 ]; then
cat > /tmp/pulumi-preview-comment.md << EOF
$header

Output was too long to display here.

[View the full output in the Actions run.](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
EOF
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Heredoc content will include leading whitespace, causing malformed markdown.

The heredoc content is indented relative to the script, but cat > file << EOF preserves that indentation in the output. This will cause the markdown to have leading spaces on each line, potentially breaking the code fence rendering.

Use <<-EOF (with hyphen) to strip leading tabs, or remove the indentation from the heredoc content.

🐛 Proposed fix using unindented heredoc content
       cat > /tmp/pulumi-preview-comment.md << EOF
-        $header
+$header

-        \`\`\`bash
-        $output
-        \`\`\`
-        EOF
+\`\`\`bash
+$output
+\`\`\`
+EOF

        if [ $(wc -c < /tmp/pulumi-preview-comment.md) -gt 65535 ]; then
          cat > /tmp/pulumi-preview-comment.md << EOF
-        $header
+$header

-        Output was too long to display here.
+Output was too long to display here.

-        [View the full output in the Actions run.](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
-        EOF
+[View the full output in the Actions run.](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
+EOF
        fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@template/.github/actions/pulumi_ephemeral_deploy/action.yml` around lines 88
- 104, The heredoc used to write /tmp/pulumi-preview-comment.md preserves the
indentation and causes leading spaces in the generated markdown; update the two
cat heredocs that write $header and the fenced code block (the blocks using cat
> /tmp/pulumi-preview-comment.md << EOF) to either use the indented-strip form
<<-EOF or remove the leading indentation from the heredoc body so $header and
the fenced block (with $output) are written without leading whitespace; ensure
both the long-output fallback heredoc is updated the same way so markdown code
fences render correctly.

@@ -0,0 +1 @@
@AGENTS.md
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a top-level heading to satisfy markdownlint MD041.

Line 1 starts with a reference token, so this file fails the configured markdown rule and may break lint-gated checks.

Suggested fix
+ # Claude agent entrypoint
+
 `@AGENTS.md`
📝 Committable suggestion

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

Suggested change
@AGENTS.md
# Claude agent entrypoint
`@AGENTS.md`
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

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

In `@template/CLAUDE.md` at line 1, Add a top-level H1 heading as the very first
line of the file so the reference token "@AGENTS.md" is no longer the first
token and markdownlint MD041 passes; insert a line like "# CLAUDE" (or another
appropriate H1 title) above the existing "@AGENTS.md" token in
template/CLAUDE.md.

Comment on lines +29 to +33
source_config = _PROJECT_ROOT / ".pre-commit-config.yaml"
config_path = tmp_path / ".pre-commit-config.yaml"
_ = shutil.copyfile(source_config, config_path)
original = config_path.read_text(encoding="utf-8")
assert "id: check-json5" in original
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't couple these tests to the repo's live .pre-commit-config.yaml.

Copying the checked-in config makes the assertions depend on whatever hook IDs and counts happen to exist today. Unrelated pre-commit changes will start breaking this unit test even if remove_precommit_hooks.py still behaves correctly. Prefer small inline YAML fixtures written into tmp_path.

Also applies to: 46-50, 74-81

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

In `@tests/unit/copier_tasks/test_remove_precommit_hooks.py` around lines 29 - 33,
The test currently copies the repo's live pre-commit config via
source_config/_PROJECT_ROOT and shutil.copyfile which couples assertions to the
checked-in file; instead, create small, explicit YAML fixture(s) directly in
tmp_path by writing known content to config_path (use
config_path.write_text(..., encoding="utf-8")), then read it into original and
assert against the controlled fixture values; apply the same replacement for the
other similar blocks (the ones currently using shutil.copyfile/source_config) so
all assertions target inline fixtures rather than the repository file.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 23

♻️ Duplicate comments (18)
.claude/helpers/merge-claude-settings.sh (1)

44-54: ⚠️ Potential issue | 🟠 Major

Fail fast on invalid settings fragments.

If one JSON/JSONC file is malformed, the catch { console.log('{}'); } fallback still rewrites settings.json and silently drops that file's merged config. That can remove permissions.* entries without surfacing the broken source file.

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

In @.claude/helpers/merge-claude-settings.sh around lines 44 - 54, The script
merge-claude-settings.sh currently swallows JSON/JSONC parse errors in the
parsed_json block (JSON5.parse over $settings_files) by printing '{}' in the
catch, causing silent loss of settings; change the try/catch to report the
filename and the parse error to stderr and immediately exit non‑zero (e.g.,
console.error(...) and process.exit(1)) so malformed fragments fail fast and
prevent rewriting settings.json, and ensure the error mentions the specific file
being parsed and the JSON5.parse error.
template/.claude/.beads/metadata.json (1)

2-4: 🧹 Nitpick | 🔵 Trivial

Clarify whether both database and backend fields are required.

Both fields are set to "dolt". If both are required for schema compliance or backward compatibility, consider adding a comment explaining the duplication. Otherwise, one field may be unnecessary.

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

In `@template/.claude/.beads/metadata.json` around lines 2 - 4, The metadata
contains duplicate fields "database" and "backend" both set to "dolt"; clarify
whether both are required by either (1) keeping both and adding an inline
comment in metadata.json explaining why both fields exist for backward
compatibility/schema reasons, or (2) removing the redundant field and updating
any reader code or docs that reference it to use the single canonical field
("database" or "backend"). Ensure you update any codepaths that parse
metadata.json to read the chosen field name (or accept either) so nothing
breaks.
.claude/commands/tdd.md (1)

96-102: ⚠️ Potential issue | 🟡 Minor

Use a neutral argument label to avoid injecting the banned term.

Line 98 forwards TDD: $ARGUMENTS, which conflicts with the file’s own output-style restriction and makes accidental echoing more likely.

Suggested patch
 **User arguments:**
 
-TDD: $ARGUMENTS
+User input: $ARGUMENTS
 
 **End of user arguments**
 
-Please continue with the user input above, applying TDD approach.
+Please continue with the user input above, applying the workflow in this document.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/commands/tdd.md around lines 96 - 102, The "TDD: $ARGUMENTS" label
forwards user input using a banned term pattern and risks echoing restricted
content; replace that specific label with a neutral placeholder (e.g., "User
input: $ARGUMENTS" or "Arguments: $ARGUMENTS") where it appears between the
"**User arguments:**" and "**End of user arguments**" blocks so the template no
longer injects the banned term and still forwards the variable, updating any
references to "TDD: $ARGUMENTS" accordingly.
template/.claude/commands/tdd.md (1)

96-102: ⚠️ Potential issue | 🟡 Minor

Avoid forwarding user input under a TDD: label.

Line 98 injects the exact term that this template asks agents not to repeat in generated artifacts.

Suggested patch
 **User arguments:**
 
-TDD: $ARGUMENTS
+User input: $ARGUMENTS
 
 **End of user arguments**
 
-Please continue with the user input above, applying TDD approach.
+Please continue with the user input above, applying the workflow in this document.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@template/.claude/commands/tdd.md` around lines 96 - 102, Remove the literal
"TDD: $ARGUMENTS" injection under the "**User arguments:**" block and stop
forwarding user input with the exact "TDD:" label; instead, preserve the user's
arguments without the "TDD:" prefix (i.e., replace the "TDD: $ARGUMENTS" line
with a neutral placeholder or directly inject $ARGUMENTS), update the template
area that contains "**User arguments:**" so generated artifacts do not include
the string "TDD:", and ensure any template variable or placeholder referencing
$ARGUMENTS is used without prepending "TDD:".
.claude/commands/polish.md (1)

6-6: ⚠️ Potential issue | 🟡 Minor

Resolve remaining markdownlint issues in this command doc.

Line 6 still triggers MD041, and fences at Lines 48, 86, and 162 still need explicit language tags (MD040).

Suggested patch
 ---
 description: Review and address issues in existing code - fix problems or justify skipping
 argument-hint: [branch, PR#, file, or area to polish]
 ---
 
-## General Guidelines
+# Polish
+
+## General Guidelines
@@
-# Polish
-
 Take another pass at existing work to address issues. Unlike `/code-review` which only identifies problems, `/polish` resolves each finding by either:
@@
-```
+```text
 GitHub CLI not available or not authenticated!
 Run: gh auth login

@@
- +markdown

[file:line] [severity] Title

@@

  • Fixed: [what was done]
  • Skipped: [justification]
@@
-```
+```markdown
## Polish Summary

Also applies to: 48-51, 86-94, 162-177

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

In @.claude/commands/polish.md at line 6, Change the first heading "## General
Guidelines" to a top-level heading (e.g., "# General Guidelines") to satisfy
MD041, and add explicit language tags to the fenced code blocks flagged for
MD040: change the simple text fence that contains the GitHub CLI message to
```text, and change the two multi-line example/result fences (the snippet that
begins with "### [file:line] [severity] Title" and the final "## Polish Summary"
code block examples) to ```markdown as shown in the suggested patch; update the
three fences referenced (around the GitHub CLI message and the two markdown
examples) so their opening backticks include the language tag and leave the
closing backticks unchanged.
template/.claude/commands/issue.md (2)

46-49: ⚠️ Potential issue | 🟡 Minor

Add a language hint to the fenced error-message block.

Use ```text at Line 46 for markdownlint compliance.

Suggested fix
-```
+```text
 GitHub CLI not available or not authenticated!
 Run: gh auth login
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @template/.claude/commands/issue.md around lines 46 - 49, The fenced code
block containing the error message lacks a language hint; update the block
delimiter from totext so the snippet (the GitHub CLI error message
block) is explicitly marked as plain text for markdownlint compliance and proper
rendering.


</details>

---

`24-51`: _⚠️ Potential issue_ | _🟡 Minor_

**Process numbering is split into separate one-item lists.**

Lines 24, 36, and 51 restart at `1.` with content in between, which breaks sequential flow in rendering.

 

<details>
<summary>Suggested structure</summary>

```diff
-1. Get Issue Number
+### Step 1: Get Issue Number
...
-1. Fetch Issue
+### Step 2: Fetch Issue
...
-1. Analyze and Plan
+### Step 3: Analyze and Plan
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@template/.claude/commands/issue.md` around lines 24 - 51, The numbered
process sections ("Get Issue Number", "Fetch Issue", "Analyze and Plan") restart
at 1., causing broken sequential numbering; update the markdown in
template/.claude/commands/issue.md so the top-level ordered list is continuous
(e.g., 1., 2., 3.) instead of multiple one-item lists—ensure the "Get Issue
Number" block is 1., the "Fetch Issue" block becomes 2., and "Analyze and Plan"
becomes 3., and adjust any intermediate sublists so they remain nested rather
than restarting the parent numbering.
```

</details>

</blockquote></details>
<details>
<summary>.claude/commands/tdd-review.md (1)</summary><blockquote>

`76-96`: _⚠️ Potential issue_ | _🟡 Minor_

**Specify the fence language for the report example.**

Line 76 should use ` ```markdown ` to avoid unlabeled fenced-block lint issues.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.claude/commands/tdd-review.md around lines 76 - 96, The fenced code block
showing the "## Test Quality Report" example is missing a language label; update
the opening fence to use markdown so the block is explicitly labeled (look for the fenced block starting with three backticks before the "## Test Quality Report" header in .claude/commands/tdd-review.md) and ensure the closing fence remains to prevent unlabeled fenced-block lint warnings.


</details>

</blockquote></details>
<details>
<summary>tests/unit/copier_tasks/test_remove_precommit_hooks.py (1)</summary><blockquote>

`29-33`: _⚠️ Potential issue_ | _🟠 Major_

**Tests are coupled to the live repository pre-commit config.**

These segments read/copy `.pre-commit-config.yaml` from repo state, so unrelated hook changes can break test expectations even when script behavior is correct.

 

<details>
<summary>Suggested direction</summary>

```diff
- source_config = _PROJECT_ROOT / ".pre-commit-config.yaml"
- config_path = tmp_path / ".pre-commit-config.yaml"
- _ = shutil.copyfile(source_config, config_path)
+ config_path = tmp_path / ".pre-commit-config.yaml"
+ _ = config_path.write_text(
+     """repos:
+  - repo: local
+    hooks:
+      - id: check-json5
+      - id: trailing-whitespace
+""",
+     encoding="utf-8",
+ )
```
</details>


Also applies to: 46-50, 74-81

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/copier_tasks/test_remove_precommit_hooks.py` around lines 29 - 33,
The test is reading the real repository .pre-commit-config.yaml via
source_config and copying it into config_path which couples the test to repo
state; instead, create a controlled test fixture by writing a minimal, explicit
.pre-commit-config.yaml string to config_path in tmp_path (or load a test
fixture file committed under tests/fixtures) and use that content for assertions
(replace uses of source_config, config_path, and original in
test_remove_precommit_hooks.py with the newly written fixture content) so the
test no longer depends on the live repo file.
```

</details>

</blockquote></details>
<details>
<summary>template/.claude/commands/create-adr.md (2)</summary><blockquote>

`42-48`: _⚠️ Potential issue_ | _🟠 Major_

**Treat empty ADR directories as uninitialized.**

Line 43 currently checks only directory existence, then breaks. If the directory exists but has no ADR `.md` files, Step 1b is skipped incorrectly.

 

<details>
<summary>Suggested fix</summary>

```diff
 for dir in doc/adr docs/adr decisions doc/architecture/decisions; do
-  if [ -d "$dir" ]; then
+  if [ -d "$dir" ] && compgen -G "$dir"/*.md > /dev/null; then
     echo "Found: $dir"
     ls "$dir"/*.md 2>/dev/null
     break
   fi
 done
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@template/.claude/commands/create-adr.md` around lines 42 - 48, The current
loop over directories (for dir in doc/adr docs/adr decisions
doc/architecture/decisions; do ...) only checks for directory existence and
breaks immediately, which treats empty ADR dirs as initialized; update the logic
to treat an ADR directory as initialized only if it contains at least one .md
file: inside the loop, first check that the directory exists AND that a glob for
"$dir"/*.md yields at least one match (e.g., using a file-glob test or compgen
-G), then echo "Found: $dir", list the .md files, and break; if no .md files are
present, continue to the next candidate directory.
```

</details>

---

`230-235`: _⚠️ Potential issue_ | _🟠 Major_

**Keep `status` as an enum value when superseding ADRs.**

Line 235 instructs `status: superseded by ADR-NNNN`, which conflicts with the declared enum at Line 230 (`superseded`) and can break status-based tooling.

 

<details>
<summary>Suggested fix</summary>

```diff
 To supersede an existing ADR:
 
 1. Create new ADR with the updated decision
-2. Update old ADR's status to `superseded by ADR-NNNN`
+2. Update old ADR's status to `superseded`
+3. Record the replacement ADR reference in the body (or a dedicated metadata field), e.g. `Superseded by: ADR-NNNN`
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@template/.claude/commands/create-adr.md` around lines 230 - 235, The template
currently tells users to set "status: superseded by ADR-NNNN" which conflicts
with the declared enum value "superseded"; change the instructions so the old
ADR's status remains the enum value "superseded" and add a separate field or
note for the reference to the replacing ADR (e.g., "superseded_by: ADR-NNNN" or
"Replaced by: ADR-NNNN") instead of embedding the ADR id in the status string so
status-based tooling continues to work.
```

</details>

</blockquote></details>
<details>
<summary>template/.claude/commands/tdd-review.md (1)</summary><blockquote>

`76-96`: _⚠️ Potential issue_ | _🟡 Minor_

**Add a language specifier to the report example fence.**

Line 76 should use a typed fence (` ```markdown `) for markdownlint compliance and clearer rendering.

 

<details>
<summary>Suggested fix</summary>

```diff
-```
+```markdown
 ## Test Quality Report
 ...
 ```
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @template/.claude/commands/tdd-review.md around lines 76 - 96, The fenced
code block that contains the "Test Quality Report" example lacks a language
specifier; update the opening fence for that block (the triple backticks
immediately before the "## Test Quality Report" heading) to include "markdown"
so it reads as a typed fence (e.g. ```markdown), ensuring markdownlint
compliance and correct rendering of the example.


</details>

</blockquote></details>
<details>
<summary>.claude/settings/permissions/bash.jsonc (1)</summary><blockquote>

`91-92`: _⚠️ Potential issue_ | _🟠 Major_

**Broaden `rm -rf` deny rules to catch common path forms.**

Current patterns require a space after `/` or `~`, so commands like `rm -rf /tmp` or `rm -rf ~/projects` may not be blocked.

  

<details>
<summary>Suggested hardening</summary>

```diff
       "Bash(rm -rf / *)",
       "Bash(rm -rf ~ *)",
+      "Bash(rm -rf /*)",
+      "Bash(rm -rf ~/*)",
+      "Bash(rm -rf /tmp*)",
+      "Bash(rm -rf ~/*)",
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In @.claude/settings/permissions/bash.jsonc around lines 91 - 92, The deny
patterns "Bash(rm -rf / *)" and "Bash(rm -rf ~ *)" only match when a space
follows / or ~; update these entries to broader patterns that match rm -rf
against root and home with or without a space and with any following path (e.g.,
variants like "rm -rf /tmp", "rm -rf ~/projects", "rm -rf /", and "rm
-rf~/.ssh"). Replace the two specific patterns with more general regex-style or
glob-style rules that capture both "/<anything>" and "~/<anything>" as well as
the bare "/" and "~" forms, ensuring the matcher used by the permissions parser
(the entries currently named "Bash(rm -rf / *)" and "Bash(rm -rf ~ *)") will
block common path forms.
```

</details>

</blockquote></details>
<details>
<summary>.claude/commands/create-issues.md (2)</summary><blockquote>

`61-64`: _🧹 Nitpick_ | _🔵 Trivial_

**Add language specifier to fenced code block.**

The error message code block lacks a language specifier, as flagged by markdownlint MD040.

<details>
<summary>📝 Proposed fix</summary>

```diff
-```
+```text
 GitHub CLI not available or not authenticated!
 Run: gh auth login
 ```
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.claude/commands/create-issues.md around lines 61 - 64, Update the fenced
code block that contains the message "GitHub CLI not available or not
authenticated! Run: gh auth login" to include a language specifier (use text)
after the opening triple backticks so markdownlint MD040 is satisfied; locate
the exact snippet with that message and change the opening fence from ``` to


55-57: ⚠️ Potential issue | 🟠 Major

Missing --repo flag for cross-repository issue URLs.

Same issue as the template file: the gh issue view command omits --repo, causing cross-repo URLs to resolve against the current checkout. Line 72 also references "Linked issues → Dependencies" but the --json fields don't include linked issue data.

-gh issue view [ISSUE_NUMBER] --json title,body,labels,comments,state
+gh issue view [ISSUE_NUMBER] --repo [OWNER/REPO] --json title,body,labels,comments,state
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/commands/create-issues.md around lines 55 - 57, Update the gh CLI
example to include the missing --repo flag so cross-repo issue URLs resolve
correctly (e.g., add --repo OWNER/REPO to the gh issue view invocation) and
extend the --json fields to include linked issue data referenced later (add the
linked issues field such as linkedIssues to the --json list so "Linked issues →
Dependencies" can be populated); locate and update the gh issue view example and
the later reference around "Linked issues → Dependencies" in this file to keep
both consistent.
.claude/commands/create-adr.md (2)

232-235: ⚠️ Potential issue | 🟠 Major

Status field mixing enum with free text.

The table at lines 225-230 defines superseded as a valid enum value, but line 235 instructs writing superseded by ADR-NNNN into the same field. This turns status into free text and will break tooling that relies on the documented enum values.

Consider using a separate field for the reference:

2. Update old ADR's status to `superseded` and add `superseded_by: ADR-NNNN` to frontmatter
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/commands/create-adr.md around lines 232 - 235, The current
instructions mix enum values and free-text in the ADR frontmatter: keep the
status field strictly enum by updating the step that tells authors to write
"superseded by ADR-NNNN" so it instead sets status: `superseded` and adds a
separate frontmatter key such as `superseded_by: ADR-NNNN`; update the text in
create-adr.md (the step that references updating old ADR status) to instruct
authors to set status to `superseded` and add `superseded_by` (or equivalent) in
the frontmatter so tooling relying on the `status` enum continues to work.

40-48: ⚠️ Potential issue | 🟠 Major

Empty ADR directories treated as initialized.

The detection loop breaks on the first existing directory without verifying it contains any ADR files. An empty doc/adr directory would skip initialization even though no template or numbering baseline exists.

🔧 Proposed fix
 # Check common ADR directories (in order of preference)
 for dir in doc/adr docs/adr decisions doc/architecture/decisions; do
   if [ -d "$dir" ]; then
-    echo "Found: $dir"
-    ls "$dir"/*.md 2>/dev/null
-    break
+    if ls "$dir"/*.md >/dev/null 2>&1; then
+      echo "Found: $dir"
+      ls "$dir"/*.md
+      break
+    fi
   fi
 done
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/commands/create-adr.md around lines 40 - 48, The loop currently
breaks on the first existing directory without confirming it contains ADR
markdown files; update the logic in the for loop that iterates over doc/adr
docs/adr decisions doc/architecture/decisions to only treat a directory as
"found" when it both exists and contains at least one *.md file (e.g., replace
the plain -d check with a combined existence+glob test such as using ls/glob
check or conditional glob expansion so empty directories are skipped); ensure
the echo "Found: $dir" and ls "$dir"/*.md only run when markdown files are
present so initialization proceeds when directories are empty.
template/.claude/commands/create-issues.md (1)

55-57: ⚠️ Potential issue | 🟠 Major

Missing --repo flag for cross-repository issue URLs.

Step 1 extracts owner/repo/number from URLs, but the gh issue view command omits --repo, causing cross-repo URLs to resolve against the current checkout instead of the source repository.

Additionally, Step 3 expects to extract "Linked issues → Dependencies" (line 72), but --json doesn't include a linked-issues field.

-gh issue view [ISSUE_NUMBER] --json title,body,labels,comments,state
+gh issue view [ISSUE_NUMBER] --repo [OWNER/REPO] --json title,body,labels,comments,state
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@template/.claude/commands/create-issues.md` around lines 55 - 57, The gh
invocation shown (gh issue view [ISSUE_NUMBER] --json
title,body,labels,comments,state) omits --repo so cross-repo URLs resolve to the
current checkout and also relies on a non-existent linked-issues JSON field;
update the command to include the source repo (add --repo OWNER/REPO) and change
Step 3’s extraction of "Linked issues → Dependencies" to use a JSON field that
contains link info (e.g., request timelineItems via --json timelineItems or call
gh api /graphql to fetch linkedIssues/timeline entries) so linked dependencies
are reliably retrieved; adjust any parsing code that referenced the missing
linked-issues field to instead parse timelineItems or the GraphQL linkedIssues
result.
🤖 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/.beads/metadata.json:
- Around line 2-4: The metadata.json currently has duplicate fields "database"
and "backend" both set to "dolt"; decide whether both are required and either
remove the unnecessary field or document the duplication. If both are required
for schema/compatibility, add a short explanatory comment next to the duplicated
entry indicating why both exist (e.g., "kept for backward compatibility") and
ensure the retained field names ("database" and/or "backend") match the
canonical schema used by the code that reads metadata.json.

In @.claude/commands/issue.md:
- Around line 6-49: Add a top-level H1 heading to the file (e.g., "Issue
Processing Guide") to satisfy MD041 and update the fenced code block that
contains "GitHub CLI not available or not authenticated! Run: gh auth login" to
include a language identifier (use "text") so it becomes a ```text fenced block;
update the file .claude/commands/issue.md around the existing examples and the
GitHub CLI message accordingly.

In @.claude/commands/refactor.md:
- Around line 125-127: The file ends with an orphaned numbered list item "1.
**Consistency check**" that appears incomplete; update the trailing list by
either converting that single numbered item into a bulleted item (replace "1."
with "-" and keep "**Consistency check** - Look for inconsistent patterns,
naming conventions, or structure across the codebase") or complete the list by
adding the missing subsequent items; modify the section containing "1.
**Consistency check**" accordingly and ensure formatting is consistent with
surrounding list style.

In @.claude/commands/simplify.md:
- Line 6: Add a top-level H1 heading before the existing H2 ("## General
Guidelines") to satisfy markdownlint MD041; update the file
.claude/commands/simplify.md by inserting a single first-line heading (e.g., "#
Simplify" or an appropriate title) so the H2 becomes a subsequent section
heading and the file's first line is an H1.

In @.claude/commands/spike.md:
- Around line 6-14: Add a top-level H1 heading at the very start of the document
(before the existing "User arguments" block) to satisfy MD041; insert a
single-line Markdown heading (e.g., "# Spike") as the first line of the file so
the document begins with an H1 heading.

In @.claude/package.json:
- Around line 3-5: Update .claude/package.json to move the "json5" package from
"dependencies" into "devDependencies": remove the "json5": "2.2.3" entry under
the dependencies object and add the identical entry under a devDependencies
object (creating devDependencies if missing). Ensure the package remains private
and that any scripts (e.g., merge-claude-settings.sh) still resolve the
dependency via devDependencies during local development.

In @.claude/settings/permissions/read.jsonc:
- Line 6: Remove the explicit read allowance for the personal shell profile by
deleting the "Read(~/.zshrc)" entry from the "allow" array (the string
"Read(~/.zshrc)" in the JSONC entry), leaving no reference to ~/.zshrc; if
needed, add a brief comment or note explaining the removal so future reviewers
know it was intentional and not an oversight.

In @.coveragerc:
- Around line 22-23: Replace the ambiguous regex-like pattern `if __name__ ==
.__main__.:` with an explicit character class that matches either single or
double quotes: use `if __name__ == ['"]__main__['"]:` so the intent is clear;
update the pattern entry in .coveragerc where the `if __name__` line appears
(look for the exact `if __name__ == .__main__.:` string) and swap it to the
explicit `['"]` variant to improve readability and avoid regex wildcard
confusion.

In @.devcontainer/install-ci-tooling.py:
- Around line 10-12: The COPIER_VERSION constant is duplicated between
COPIER_VERSION in .devcontainer/install-ci-tooling.py and pyproject.toml;
centralize the source of truth: remove the hardcoded COPIER_VERSION in
install-ci-tooling.py and instead read the copier version from pyproject.toml
(or a single shared config/constant) at runtime, or add a small helper (e.g.,
load_project_version()) that parses pyproject.toml and returns the copier
version to be used by install-ci-tooling.py so COPIER_VERSION is only defined
once.

In @.github/actions/install_deps/action.yml:
- Line 67: The composite action upgrade must ensure runners meet
configure-aws-credentials v6.0.0 requirements (Node.js 24 and GitHub Actions
runner v2.327.1+); update the action manifest to either (a) keep using the older
configure-aws-credentials pin until all runners are updated, or (b) add an
explicit pre-check/validation step or documentation that verifies runner version
and Node runtime before using configure-aws-credentials v6.0.0, and ensure
actions/setup-node@v6.3.0 remains as-is for node setup; reference the
configure-aws-credentials usage and the actions/setup-node@v6.3.0 entry in this
file when making the change.

In @.github/workflows/ci.yaml:
- Around line 56-59: The composite action's inputs in the install_deps
action.yml include unsupported "type" fields; remove any "type" entries from all
input definitions in the install_deps composite action's action.yml so that each
input only uses allowed keys (description, required, default,
deprecationMessage); update every input block (e.g., python-version, any other
inputs) to delete the "type" property and keep the remaining keys intact to
comply with the GitHub Actions metadata schema.

In @.gitignore:
- Line 24: The current .gitignore entry '*.db' is too broad; remove that line
and instead add scoped ignore patterns that target only Dolt or known generated
DB artifacts (e.g., use a Dolt-specific directory pattern like '.dolt/**' or
'dolt/**' and/or the exact generated filename such as 'beads.db' at repo root)
so that legitimate .db files (SQLite fixtures, test DBs) remain tracked; update
the .gitignore to replace '*.db' with these narrower patterns and commit the
change.

In `@template/.claude/commands/create-issues.md`:
- Around line 61-64: Update the fenced code block in
template/.claude/commands/create-issues.md to include a language specifier so
markdownlint MD040 is satisfied: change the opening fence from ``` to ```text
for the block that contains "GitHub CLI not available or not authenticated!" /
"Run: gh auth login" so the code block is explicitly marked as plain text.

In `@template/.claude/commands/green.md`:
- Around line 55-66: The "Post-Green Verification" block interrupts the numbered
TDD cycle and breaks the ordered list continuity; to fix, relocate the entire
"Post-Green Verification" section so it appears after the "3. **Refactor
Phase**" paragraph (keeping its content intact), or alternatively convert the
TDD cycle items (e.g., "1. ...", "2. ...", "3. **Refactor Phase**") into
separate headings so "Post-Green Verification" no longer splits the ordered
list; adjust only the placement/formatting of the sections titled "Post-Green
Verification" and "3. **Refactor Phase**" to restore correct list structure.

In `@template/.claude/commands/refactor.md`:
- Around line 6-14: The document starts without a top-level heading which
triggers MD041; insert a single H1 heading line before the "**User arguments:**"
block (e.g., an H1 that names the template or "Refactor") so the file begins
with a top-level heading and the rest of the content (including the "Refactor:
$ARGUMENTS" block and General Guidelines) remains unchanged.

In `@template/.claude/commands/simplify.md`:
- Line 6: The file currently begins with a level-2 heading "## General
Guidelines" and lacks a top-level H1 required by MD041; insert a single H1
heading immediately after the YAML frontmatter (before the existing "## General
Guidelines") so the document has a top-level heading followed by the existing
subsection heading.

In `@template/.claude/commands/spike.md`:
- Around line 14-103: The TDD Fundamentals and General Guidelines blocks are
duplicated across spike.md, red.md, and green.md; extract those shared sections
into a new reusable partial (e.g., _tdd_fundamentals.md or a template partial)
and update spike.md, red.md, and green.md to include or reference that partial
instead of repeating the content; ensure the partial contains the "General
Guidelines" and "TDD Fundamentals" headings and preserve the optional Spike
Phase text so that the include usage in spike.md, red.md, and green.md yields
identical content and reduces duplication.
- Line 6: Add a top-level H1 heading immediately after the YAML frontmatter in
spike.md to satisfy MD041; open template/.claude/commands/spike.md and insert a
clear H1 (e.g., "# Spike" or a descriptive title) as the first content line
following the closing --- of the frontmatter so the file begins with an H1
heading.

In `@template/.claude/settings/permissions/bash.jsonc`:
- Around line 91-92: Replace the current deny patterns "Bash(rm -rf / *)" and
"Bash(rm -rf ~ *)" with explicit variants that also match the bare destructive
commands and globbed forms: add patterns for "Bash(rm -rf /)" and "Bash(rm -rf
/*)" and for "Bash(rm -rf ~)" and "Bash(rm -rf ~*)", so both the exact
no-argument commands and the forms with trailing globs are blocked; update the
entries that reference the original strings to include these four new patterns.

In `@template/.claude/settings/permissions/write.jsonc`:
- Line 6: The current permission entry "allow": ["Write(/tmp/**)"] is too broad;
update it to restrict writes to a tool-specific subdirectory (e.g. replace
Write(/tmp/**) with Write(/tmp/claude/**) or another dedicated folder name) and
ensure any runtime code or tests that write to /tmp are adjusted to use that
subdirectory (references: the permission string "Write(/tmp/**)" in the settings
file). Also create the subdirectory at startup or document that it must exist so
callers don't fail.

In `@template/.devcontainer/docker-compose.yml.jinja`:
- Around line 45-52: Add a Docker healthcheck to the beads-dolt service and make
the devcontainer service depend on beads-dolt using the service_healthy
condition so devcontainer waits until Dolt is ready; specifically, update the
beads-dolt service block (service name "beads-dolt") to include a healthcheck
(e.g., a lightweight SQL or dolt health probe command with interval, timeout and
retries) and update the devcontainer service to include depends_on: beads-dolt:
condition: service_healthy (or the Compose v2 equivalent) so startup ordering is
deterministic.

In `@template/.github/actions/pulumi_ephemeral_deploy/action.yml`:
- Line 76: The workflow currently references the mutable tag "uses:
aws-actions/configure-aws-credentials@v6.0.0" which can change unexpectedly;
update that step to use the immutable commit SHA (replace the tag with the
provided SHA "8df5847569e6427dd6c4fb1cf565c83acfa8afa7") so the
aws-actions/configure-aws-credentials action is pinned to a specific commit;
locate the line that contains uses: aws-actions/configure-aws-credentials@v6.0.0
and swap the tag for the commit SHA string to match the project's immutable
pinning policy.

In `@template/AGENTS.md`:
- Line 139: The doc line currently points to non-existent files ("README.md and
docs/QUICKSTART.md"); update AGENTS.md to reference documentation that actually
exists in the template or add the missing files. Locate the string "README.md
and docs/QUICKSTART.md" in AGENTS.md and either replace it with the correct
existing doc paths used by the template (or a single existing README) or create
the referenced README.md and docs/QUICKSTART.md files with the appropriate
content so the links resolve in generated projects.

---

Duplicate comments:
In @.claude/commands/create-adr.md:
- Around line 232-235: The current instructions mix enum values and free-text in
the ADR frontmatter: keep the status field strictly enum by updating the step
that tells authors to write "superseded by ADR-NNNN" so it instead sets status:
`superseded` and adds a separate frontmatter key such as `superseded_by:
ADR-NNNN`; update the text in create-adr.md (the step that references updating
old ADR status) to instruct authors to set status to `superseded` and add
`superseded_by` (or equivalent) in the frontmatter so tooling relying on the
`status` enum continues to work.
- Around line 40-48: The loop currently breaks on the first existing directory
without confirming it contains ADR markdown files; update the logic in the for
loop that iterates over doc/adr docs/adr decisions doc/architecture/decisions to
only treat a directory as "found" when it both exists and contains at least one
*.md file (e.g., replace the plain -d check with a combined existence+glob test
such as using ls/glob check or conditional glob expansion so empty directories
are skipped); ensure the echo "Found: $dir" and ls "$dir"/*.md only run when
markdown files are present so initialization proceeds when directories are
empty.

In @.claude/commands/create-issues.md:
- Around line 61-64: Update the fenced code block that contains the message
"GitHub CLI not available or not authenticated! Run: gh auth login" to include a
language specifier (use `text`) after the opening triple backticks so
markdownlint MD040 is satisfied; locate the exact snippet with that message and
change the opening fence from ``` to ```text.
- Around line 55-57: Update the gh CLI example to include the missing --repo
flag so cross-repo issue URLs resolve correctly (e.g., add --repo OWNER/REPO to
the gh issue view invocation) and extend the --json fields to include linked
issue data referenced later (add the linked issues field such as linkedIssues to
the --json list so "Linked issues → Dependencies" can be populated); locate and
update the gh issue view example and the later reference around "Linked issues →
Dependencies" in this file to keep both consistent.

In @.claude/commands/polish.md:
- Line 6: Change the first heading "## General Guidelines" to a top-level
heading (e.g., "# General Guidelines") to satisfy MD041, and add explicit
language tags to the fenced code blocks flagged for MD040: change the simple
text fence that contains the GitHub CLI message to ```text, and change the two
multi-line example/result fences (the snippet that begins with "### [file:line]
[severity] Title" and the final "## Polish Summary" code block examples) to
```markdown as shown in the suggested patch; update the three fences referenced
(around the GitHub CLI message and the two markdown examples) so their opening
backticks include the language tag and leave the closing backticks unchanged.

In @.claude/commands/tdd-review.md:
- Around line 76-96: The fenced code block showing the "## Test Quality Report"
example is missing a language label; update the opening fence to use ```markdown
so the block is explicitly labeled (look for the fenced block starting with
three backticks before the "## Test Quality Report" header in
.claude/commands/tdd-review.md) and ensure the closing fence remains ``` to
prevent unlabeled fenced-block lint warnings.

In @.claude/commands/tdd.md:
- Around line 96-102: The "TDD: $ARGUMENTS" label forwards user input using a
banned term pattern and risks echoing restricted content; replace that specific
label with a neutral placeholder (e.g., "User input: $ARGUMENTS" or "Arguments:
$ARGUMENTS") where it appears between the "**User arguments:**" and "**End of
user arguments**" blocks so the template no longer injects the banned term and
still forwards the variable, updating any references to "TDD: $ARGUMENTS"
accordingly.

In @.claude/helpers/merge-claude-settings.sh:
- Around line 44-54: The script merge-claude-settings.sh currently swallows
JSON/JSONC parse errors in the parsed_json block (JSON5.parse over
$settings_files) by printing '{}' in the catch, causing silent loss of settings;
change the try/catch to report the filename and the parse error to stderr and
immediately exit non‑zero (e.g., console.error(...) and process.exit(1)) so
malformed fragments fail fast and prevent rewriting settings.json, and ensure
the error mentions the specific file being parsed and the JSON5.parse error.

In @.claude/settings/permissions/bash.jsonc:
- Around line 91-92: The deny patterns "Bash(rm -rf / *)" and "Bash(rm -rf ~ *)"
only match when a space follows / or ~; update these entries to broader patterns
that match rm -rf against root and home with or without a space and with any
following path (e.g., variants like "rm -rf /tmp", "rm -rf ~/projects", "rm -rf
/", and "rm -rf~/.ssh"). Replace the two specific patterns with more general
regex-style or glob-style rules that capture both "/<anything>" and
"~/<anything>" as well as the bare "/" and "~" forms, ensuring the matcher used
by the permissions parser (the entries currently named "Bash(rm -rf / *)" and
"Bash(rm -rf ~ *)") will block common path forms.

In `@template/.claude/.beads/metadata.json`:
- Around line 2-4: The metadata contains duplicate fields "database" and
"backend" both set to "dolt"; clarify whether both are required by either (1)
keeping both and adding an inline comment in metadata.json explaining why both
fields exist for backward compatibility/schema reasons, or (2) removing the
redundant field and updating any reader code or docs that reference it to use
the single canonical field ("database" or "backend"). Ensure you update any
codepaths that parse metadata.json to read the chosen field name (or accept
either) so nothing breaks.

In `@template/.claude/commands/create-adr.md`:
- Around line 42-48: The current loop over directories (for dir in doc/adr
docs/adr decisions doc/architecture/decisions; do ...) only checks for directory
existence and breaks immediately, which treats empty ADR dirs as initialized;
update the logic to treat an ADR directory as initialized only if it contains at
least one .md file: inside the loop, first check that the directory exists AND
that a glob for "$dir"/*.md yields at least one match (e.g., using a file-glob
test or compgen -G), then echo "Found: $dir", list the .md files, and break; if
no .md files are present, continue to the next candidate directory.
- Around line 230-235: The template currently tells users to set "status:
superseded by ADR-NNNN" which conflicts with the declared enum value
"superseded"; change the instructions so the old ADR's status remains the enum
value "superseded" and add a separate field or note for the reference to the
replacing ADR (e.g., "superseded_by: ADR-NNNN" or "Replaced by: ADR-NNNN")
instead of embedding the ADR id in the status string so status-based tooling
continues to work.

In `@template/.claude/commands/create-issues.md`:
- Around line 55-57: The gh invocation shown (gh issue view [ISSUE_NUMBER]
--json title,body,labels,comments,state) omits --repo so cross-repo URLs resolve
to the current checkout and also relies on a non-existent linked-issues JSON
field; update the command to include the source repo (add --repo OWNER/REPO) and
change Step 3’s extraction of "Linked issues → Dependencies" to use a JSON field
that contains link info (e.g., request timelineItems via --json timelineItems or
call gh api /graphql to fetch linkedIssues/timeline entries) so linked
dependencies are reliably retrieved; adjust any parsing code that referenced the
missing linked-issues field to instead parse timelineItems or the GraphQL
linkedIssues result.

In `@template/.claude/commands/issue.md`:
- Around line 46-49: The fenced code block containing the error message lacks a
language hint; update the block delimiter from ``` to ```text so the snippet
(the GitHub CLI error message block) is explicitly marked as plain text for
markdownlint compliance and proper rendering.
- Around line 24-51: The numbered process sections ("Get Issue Number", "Fetch
Issue", "Analyze and Plan") restart at 1., causing broken sequential numbering;
update the markdown in template/.claude/commands/issue.md so the top-level
ordered list is continuous (e.g., 1., 2., 3.) instead of multiple one-item
lists—ensure the "Get Issue Number" block is 1., the "Fetch Issue" block becomes
2., and "Analyze and Plan" becomes 3., and adjust any intermediate sublists so
they remain nested rather than restarting the parent numbering.

In `@template/.claude/commands/tdd-review.md`:
- Around line 76-96: The fenced code block that contains the "Test Quality
Report" example lacks a language specifier; update the opening fence for that
block (the triple backticks immediately before the "## Test Quality Report"
heading) to include "markdown" so it reads as a typed fence (e.g. ```markdown),
ensuring markdownlint compliance and correct rendering of the example.

In `@template/.claude/commands/tdd.md`:
- Around line 96-102: Remove the literal "TDD: $ARGUMENTS" injection under the
"**User arguments:**" block and stop forwarding user input with the exact "TDD:"
label; instead, preserve the user's arguments without the "TDD:" prefix (i.e.,
replace the "TDD: $ARGUMENTS" line with a neutral placeholder or directly inject
$ARGUMENTS), update the template area that contains "**User arguments:**" so
generated artifacts do not include the string "TDD:", and ensure any template
variable or placeholder referencing $ARGUMENTS is used without prepending
"TDD:".

In `@tests/unit/copier_tasks/test_remove_precommit_hooks.py`:
- Around line 29-33: The test is reading the real repository
.pre-commit-config.yaml via source_config and copying it into config_path which
couples the test to repo state; instead, create a controlled test fixture by
writing a minimal, explicit .pre-commit-config.yaml string to config_path in
tmp_path (or load a test fixture file committed under tests/fixtures) and use
that content for assertions (replace uses of source_config, config_path, and
original in test_remove_precommit_hooks.py with the newly written fixture
content) so the test no longer depends on the live repo file.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c13b56cf-2b72-4fc8-bbb8-c16a22908d4e

📥 Commits

Reviewing files that changed from the base of the PR and between 6eb2eb7 and 2dce9f3.

⛔ Files ignored due to path filters (3)
  • .claude/package-lock.json is excluded by !**/package-lock.json
  • template/.claude/package-lock.json is excluded by !**/package-lock.json
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (87)
  • .claude/.beads/.gitignore
  • .claude/.beads/config.yaml
  • .claude/.beads/metadata.json
  • .claude/commands/add-command.md
  • .claude/commands/commit.md
  • .claude/commands/create-adr.md
  • .claude/commands/create-issues.md
  • .claude/commands/gap.md
  • .claude/commands/green.md
  • .claude/commands/issue.md
  • .claude/commands/polish.md
  • .claude/commands/red.md
  • .claude/commands/refactor.md
  • .claude/commands/research.md
  • .claude/commands/simplify.md
  • .claude/commands/spike.md
  • .claude/commands/summarize.md
  • .claude/commands/tdd-review.md
  • .claude/commands/tdd.md
  • .claude/helpers/merge-claude-settings.sh
  • .claude/package.json
  • .claude/settings/basics.jsonc
  • .claude/settings/permissions/bash.jsonc
  • .claude/settings/permissions/read.jsonc
  • .claude/settings/permissions/write.jsonc
  • .coderabbit.yaml
  • .copier-answers.yml
  • .coveragerc
  • .devcontainer/devcontainer.json
  • .devcontainer/docker-compose.yml
  • .devcontainer/install-ci-tooling.py
  • .devcontainer/on-create-command.sh
  • .devcontainer/post-start-command.sh
  • .github/actions/install_deps/action.yml
  • .github/reusable_workflows/build-docker-image.yaml
  • .github/workflows/ci.yaml
  • .github/workflows/pre-commit.yaml
  • .gitignore
  • .pre-commit-config.yaml
  • AGENTS.md
  • CLAUDE.md
  • copier.yml
  • extensions/context.py
  • pyproject.toml
  • src/copier_tasks/__init__.py
  • src/copier_tasks/remove_precommit_hooks.py
  • template/.claude/.beads/.gitignore
  • template/.claude/.beads/config.yaml
  • template/.claude/.beads/metadata.json
  • template/.claude/commands/add-command.md
  • template/.claude/commands/commit.md
  • template/.claude/commands/create-adr.md
  • template/.claude/commands/create-issues.md
  • template/.claude/commands/gap.md
  • template/.claude/commands/green.md
  • template/.claude/commands/issue.md
  • template/.claude/commands/polish.md
  • template/.claude/commands/red.md
  • template/.claude/commands/refactor.md
  • template/.claude/commands/research.md
  • template/.claude/commands/simplify.md
  • template/.claude/commands/spike.md
  • template/.claude/commands/summarize.md
  • template/.claude/commands/tdd-review.md
  • template/.claude/commands/tdd.md
  • template/.claude/helpers/merge-claude-settings.sh
  • template/.claude/package.json
  • template/.claude/settings/basics.jsonc
  • template/.claude/settings/permissions/bash.jsonc
  • template/.claude/settings/permissions/read.jsonc
  • template/.claude/settings/permissions/write.jsonc
  • template/.coderabbit.yaml
  • template/.devcontainer/devcontainer.json.jinja
  • template/.devcontainer/docker-compose.yml.jinja
  • template/.devcontainer/on-create-command.sh.jinja
  • template/.devcontainer/post-start-command.sh.jinja
  • template/.github/actions/ecr-auth/action.yml.jinja
  • template/.github/actions/install_deps/action.yml
  • template/.github/actions/pulumi_ephemeral_deploy/action.yml
  • template/.github/workflows/pre-commit.yaml
  • template/.github/workflows/pulumi-aws.yml
  • template/.gitignore
  • template/.pre-commit-config.yaml
  • template/AGENTS.md
  • template/CLAUDE.md
  • tests/unit/copier_tasks/__init__.py
  • tests/unit/copier_tasks/test_remove_precommit_hooks.py

Comment on lines +2 to +4
"database": "dolt",
"jsonl_export": "issues.jsonl",
"backend": "dolt",
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Clarify whether both database and backend fields are required.

Both fields are set to "dolt". If both are required for schema compliance or backward compatibility, consider adding a comment explaining the duplication. Otherwise, one field may be unnecessary.

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

In @.claude/.beads/metadata.json around lines 2 - 4, The metadata.json currently
has duplicate fields "database" and "backend" both set to "dolt"; decide whether
both are required and either remove the unnecessary field or document the
duplication. If both are required for schema/compatibility, add a short
explanatory comment next to the duplicated entry indicating why both exist
(e.g., "kept for backward compatibility") and ensure the retained field names
("database" and/or "backend") match the canonical schema used by the code that
reads metadata.json.

Comment on lines +6 to +49
Analyze GitHub issue and create TDD implementation plan.

## General Guidelines

### Output Style

- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues
- Write natural, descriptive code without meta-commentary about the development process
- The code should speak for itself - TDD is the process, not the product

Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands).

## Plan File Restriction

**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools).

Process:

1. Get Issue Number

**User arguments:**

Issue: $ARGUMENTS

**End of user arguments**

- Check if argument is an issue number
- Otherwise try branch name patterns: issue-123, 123-feature, feature/123, fix/123
- If not found: ask user

1. Fetch Issue

Use the GitHub CLI to fetch issue details:

```bash
gh issue view [ISSUE_NUMBER] --json title,body,labels,comments,state
```

If the `gh` CLI is not installed or authenticated, show:

```
GitHub CLI not available or not authenticated!
Run: gh auth login
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdownlint violations (MD041 + MD040).

The file is missing an initial H1 and the fenced block at Line 46 has no language identifier.

Suggested patch
 ---
 description: Analyze GitHub issue and create TDD implementation plan
 argument-hint: [optional-issue-number]
 ---
 
+# Issue Analysis
+
 Analyze GitHub issue and create TDD implementation plan.
@@
-```
+```text
 GitHub CLI not available or not authenticated!
 Run: gh auth login
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.21.0)</summary>

[warning] 6-6: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

---

[warning] 46-46: 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 @.claude/commands/issue.md around lines 6 - 49, Add a top-level H1 heading to
the file (e.g., "Issue Processing Guide") to satisfy MD041 and update the fenced
code block that contains "GitHub CLI not available or not authenticated! Run: gh
auth login" to include a language identifier (use "text") so it becomes a

existing examples and the GitHub CLI message accordingly.

Comment on lines +125 to +127
If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior.

1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incomplete numbered list at end of file.

Line 127 starts a numbered list with "1." but has no subsequent items. This appears to be either:

  • An incomplete list that should have more items
  • Content that should not be a numbered list

Consider completing the list or converting to a bullet point if this is a standalone item.

🔧 Proposed fix if this should be a bullet point
-1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase
+- **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/commands/refactor.md around lines 125 - 127, The file ends with an
orphaned numbered list item "1. **Consistency check**" that appears incomplete;
update the trailing list by either converting that single numbered item into a
bulleted item (replace "1." with "-" and keep "**Consistency check** - Look for
inconsistent patterns, naming conventions, or structure across the codebase") or
complete the list by adding the missing subsequent items; modify the section
containing "1. **Consistency check**" accordingly and ensure formatting is
consistent with surrounding list style.

argument-hint: [file, function, or area to simplify]
---

## General Guidelines
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a top-level heading to satisfy markdownlint.

Line 6 starts with an H2, which triggers MD041 (first-line-heading). Add an H1 before this section.

Suggested patch
 ---
 description: Reduce code complexity while keeping tests green
 argument-hint: [file, function, or area to simplify]
 ---
 
+# Simplify
+
 ## General Guidelines
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 6-6: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

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

In @.claude/commands/simplify.md at line 6, Add a top-level H1 heading before
the existing H2 ("## General Guidelines") to satisfy markdownlint MD041; update
the file .claude/commands/simplify.md by inserting a single first-line heading
(e.g., "# Simplify" or an appropriate title) so the H2 becomes a subsequent
section heading and the file's first line is an H1.

Comment on lines +6 to +14
**User arguments:**

Spike: $ARGUMENTS

**End of user arguments**

SPIKE PHASE! Apply the below to the user input above.

## General Guidelines
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add an H1 heading at the top of the document.

This file currently starts content without a top-level heading (MD041).

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 6-6: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

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

In @.claude/commands/spike.md around lines 6 - 14, Add a top-level H1 heading at
the very start of the document (before the existing "User arguments" block) to
satisfy MD041; insert a single-line Markdown heading (e.g., "# Spike") as the
first line of the file so the document begins with an H1 heading.

Comment on lines +91 to +92
"Bash(rm -rf / *)",
"Bash(rm -rf ~ *)",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Claude Code settings.json bash permission pattern syntax wildcards

💡 Result:

Claude Code (Anthropic's coding agent/CLI tool) uses settings.json files to configure permissions for tools like Bash, including wildcard/glob patterns for command matching. Settings files hierarchy (project-level overrides user-level): - Global: ~/.claude/settings.json - Project shared: .claude/settings.json (git-committable) - Project local: .claude/settings.local.json (gitignored, personal) Permissions are configured under "permissions": {"allow": [...], "deny": [...]}. Rules use format Tool or Tool(specifier). Evaluation: deny > ask > allow. For Bash: - "Bash" (no parens) or "Bash(*)" allows all Bash commands. - Wildcards with : "Bash(npm )" matches npm followed by anything (e.g., npm install, npm run build). - * can be anywhere: "Bash( install)" (ends with install), "Bash(git * main)" (git anything main). - Space before : "Bash(ls )" matches ls with args (word boundary, e.g., ls -la) but not lsof; "Bash(ls)" matches both. - Deprecated: "Bash:" (use "Bash()" instead). - Patterns are glob-like, fragile for arg constraints; prefer deny for blocks like "Bash(git push *)". Example settings.json: { "$schema": "https://json.schemastore.org/claude-code-settings.json", "permissions": { "allow": [ "Bash(npm run *)", "Bash(git commit )", "Bash(git * main)", "Bash( --version)" ], "deny": [ "Bash(git push *)", "Bash(rm *)" ] } } Use /permissions command in Claude Code to view/manage. Official docs: https://code.claude.com/docs/en/permissions. Note bugs reported with matching (e.g., GitHub issues #29616, #3428), but core syntax as above.

Citations:


🏁 Script executed:

cat -n template/.claude/settings/permissions/bash.jsonc | sed -n '85,100p'

Repository: LabAutomationAndScreening/copier-nuxt-static-aws

Length of output: 719


🏁 Script executed:

cat -n template/.claude/settings/permissions/bash.jsonc | sed -n '85,130p'

Repository: LabAutomationAndScreening/copier-nuxt-static-aws

Length of output: 1316


Deny patterns for rm -rf / and rm -rf ~ don't match exact commands without trailing arguments.

The patterns "Bash(rm -rf / *)" and "Bash(rm -rf ~ *)" include a space before *, which means they match commands like rm -rf / foo but fail to match rm -rf / or rm -rf ~ executed alone—the most destructive forms. Additionally, rm -rf /* and rm -rf ~* (without the space) also bypass these rules.

🛡️ Proposed fix
-      "Bash(rm -rf / *)",
-      "Bash(rm -rf ~ *)",
+      "Bash(rm -rf /)",
+      "Bash(rm -rf /*)",
+      "Bash(rm -rf ~)",
+      "Bash(rm -rf ~*)",
📝 Committable suggestion

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

Suggested change
"Bash(rm -rf / *)",
"Bash(rm -rf ~ *)",
"Bash(rm -rf /)",
"Bash(rm -rf /*)",
"Bash(rm -rf ~)",
"Bash(rm -rf ~*)",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@template/.claude/settings/permissions/bash.jsonc` around lines 91 - 92,
Replace the current deny patterns "Bash(rm -rf / *)" and "Bash(rm -rf ~ *)" with
explicit variants that also match the bare destructive commands and globbed
forms: add patterns for "Bash(rm -rf /)" and "Bash(rm -rf /*)" and for "Bash(rm
-rf ~)" and "Bash(rm -rf ~*)", so both the exact no-argument commands and the
forms with trailing globs are blocked; update the entries that reference the
original strings to include these four new patterns.

// This should only ever be running in a devcontainer, so pretty lenient permissions are allowed
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": ["Write(/tmp/**)"]
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider narrowing the write permission scope.

The permission Write(/tmp/**) grants unrestricted write access to the entire /tmp directory. Even in a devcontainer environment, restricting writes to a tool-specific subdirectory (e.g., /tmp/claude/**) would better align with least-privilege principles.

🔒 Suggested hardening
-    "allow": ["Write(/tmp/**)"]
+    "allow": ["Write(/tmp/claude/**)"]
📝 Committable suggestion

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

Suggested change
"allow": ["Write(/tmp/**)"]
"allow": ["Write(/tmp/claude/**)"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@template/.claude/settings/permissions/write.jsonc` at line 6, The current
permission entry "allow": ["Write(/tmp/**)"] is too broad; update it to restrict
writes to a tool-specific subdirectory (e.g. replace Write(/tmp/**) with
Write(/tmp/claude/**) or another dedicated folder name) and ensure any runtime
code or tests that write to /tmp are adjusted to use that subdirectory
(references: the permission string "Write(/tmp/**)" in the settings file). Also
create the subdirectory at startup or document that it must exist so callers
don't fail.

Comment on lines +45 to +52

beads-dolt:
image: dolthub/dolt-sql-server:1.83.0 # no explicit reason for this version, just pinning for best practice
volumes:
- beads_dolt_data:/var/lib/dolt
environment:
- DOLT_ROOT_HOST=%
- DOLT_DATABASE=beads_work{% endraw %}{% endif %}{% raw %}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding a healthcheck to ensure Dolt is ready before devcontainer scripts run.

The beads-dolt service has no healthcheck and the devcontainer service lacks depends_on with a health condition. While post-start scripts may handle initialization failures reactively, adding a healthcheck would ensure deterministic startup ordering and prevent race conditions.

Proposed healthcheck
   beads-dolt:
     image: dolthub/dolt-sql-server:1.83.0 # no explicit reason for this version, just pinning for best practice
     volumes:
       - beads_dolt_data:/var/lib/dolt
     environment:
       - DOLT_ROOT_HOST=%
-      - DOLT_DATABASE=beads_work{% endraw %}{% endif %}{% raw %}
+      - DOLT_DATABASE=beads_work
+    healthcheck:
+      test: ["CMD", "dolt", "version"]
+      interval: 10s
+      timeout: 5s
+      retries: 5{% endraw %}{% endif %}{% raw %}
📝 Committable suggestion

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

Suggested change
beads-dolt:
image: dolthub/dolt-sql-server:1.83.0 # no explicit reason for this version, just pinning for best practice
volumes:
- beads_dolt_data:/var/lib/dolt
environment:
- DOLT_ROOT_HOST=%
- DOLT_DATABASE=beads_work{% endraw %}{% endif %}{% raw %}
beads-dolt:
image: dolthub/dolt-sql-server:1.83.0 # no explicit reason for this version, just pinning for best practice
volumes:
- beads_dolt_data:/var/lib/dolt
environment:
- DOLT_ROOT_HOST=%
- DOLT_DATABASE=beads_work
healthcheck:
test: ["CMD", "dolt", "version"]
interval: 10s
timeout: 5s
retries: 5{% endraw %}{% endif %}{% raw %}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@template/.devcontainer/docker-compose.yml.jinja` around lines 45 - 52, Add a
Docker healthcheck to the beads-dolt service and make the devcontainer service
depend on beads-dolt using the service_healthy condition so devcontainer waits
until Dolt is ready; specifically, update the beads-dolt service block (service
name "beads-dolt") to include a healthcheck (e.g., a lightweight SQL or dolt
health probe command with interval, timeout and retries) and update the
devcontainer service to include depends_on: beads-dolt: condition:
service_healthy (or the Compose v2 equivalent) so startup ordering is
deterministic.


- name: AWS OIDC
uses: aws-actions/configure-aws-credentials@v5.1.1
uses: aws-actions/configure-aws-credentials@v6.0.0
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
git ls-remote https://github.com/aws-actions/configure-aws-credentials \
  refs/tags/v6.0.0 refs/tags/v6.0.0^{}

Repository: LabAutomationAndScreening/copier-nuxt-static-aws

Length of output: 149


Pin the AWS OIDC action to an immutable commit SHA.

This step uses OIDC-backed AWS credentials but references a mutable tag (@v6.0.0). Pin the action to the commit SHA to prevent unexpected updates:

uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7

This aligns with the immutable pinning approach already used elsewhere in this file.

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

In `@template/.github/actions/pulumi_ephemeral_deploy/action.yml` at line 76, The
workflow currently references the mutable tag "uses:
aws-actions/configure-aws-credentials@v6.0.0" which can change unexpectedly;
update that step to use the immutable commit SHA (replace the tag with the
provided SHA "8df5847569e6427dd6c4fb1cf565c83acfa8afa7") so the
aws-actions/configure-aws-credentials action is pinned to a specific commit;
locate the line that contains uses: aws-actions/configure-aws-credentials@v6.0.0
and swap the tag for the commit SHA string to match the project's immutable
pinning policy.

- ❌ Do NOT use external issue trackers
- ❌ Do NOT duplicate tracking systems

For more details, see README.md and docs/QUICKSTART.md.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if referenced documentation files exist
echo "Checking for README.md:"
fd -t f '^README\.md$' template/

echo "Checking for docs/QUICKSTART.md:"
fd -t f 'QUICKSTART\.md' template/

Repository: LabAutomationAndScreening/copier-nuxt-static-aws

Length of output: 148


Update line 139 documentation reference to point to files that exist in the template.

Line 139 references README.md and docs/QUICKSTART.md, but these files do not exist in the template directory. Either create these files or update the reference to point to documentation that will be available in the generated project.

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

In `@template/AGENTS.md` at line 139, The doc line currently points to
non-existent files ("README.md and docs/QUICKSTART.md"); update AGENTS.md to
reference documentation that actually exists in the template or add the missing
files. Locate the string "README.md and docs/QUICKSTART.md" in AGENTS.md and
either replace it with the correct existing doc paths used by the template (or a
single existing README) or create the referenced README.md and
docs/QUICKSTART.md files with the appropriate content so the links resolve in
generated projects.

@ejfine ejfine marked this pull request as ready for review March 24, 2026 19:07
@ejfine ejfine merged commit f0443b7 into main Mar 24, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant