Skip to content

Copier provenance#186

Merged
zendern merged 28 commits into
mainfrom
copier-provenance
Jun 29, 2026
Merged

Copier provenance#186
zendern merged 28 commits into
mainfrom
copier-provenance

Conversation

@zendern

@zendern zendern commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Link to Issue or Message thread

Downstream repo PR conversation

Why is this change necessary?

Knowing where files come from as far as copier templates go is a pain unless you are Eli

How does this change address the issue?

Adds header/footer to files that support it, creates a json file that contains which template owns which file and AI guidance to add a comment/summary about which files touched copier related files.

What side effects does this change have?

Tons of files touched

How is this change tested?

Unit tests and downstream

Summary by CodeRabbit

Release Notes

  • New Features

    • Added file provenance tracking for copier-managed assets, including standardized managed-file headers and an ownership manifest to support repeat template generations and attribution.
  • Chores

    • Added/updated copier template metadata documentation and warnings across configs, scripts, and docs, plus guidance to backport shared changes appropriately.
  • Tests

    • Added comprehensive tests for provenance/header insertion and manifest behavior, and improved async test reliability by failing when background task errors occur.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1887d39c-e3da-45e3-b343-e4bd6affc5e4

📥 Commits

Reviewing files that changed from the base of the PR and between 4f9ac0c and 3397378.

📒 Files selected for processing (1)
  • .copier-answers.yml

📝 Walkthrough

Walkthrough

Adds copier-managed provenance metadata, manifest ownership tracking, copier wiring updates, managed-file header rollouts, and asyncio task tracking utilities with tests.

Changes

Copier-managed provenance and header system

Layer / File(s) Summary
Core provenance CLI behavior
src/copier_tasks/copier_provenance.py
Defines provenance comment formats, header rendering and stripping, destination filename resolution, shebang-aware placement, file attribution, manifest updates, and CLI orchestration.
Manifest ownership wiring and docs
.copier-managed-files.json, .coderabbit.yaml, .copier-answers.yml, copier.yml, AGENTS.md, template/AGENTS.md, template/.coderabbit.yaml
Adds managed-files metadata, CodeRabbit and copier ownership instructions, copier task ordering updates, the copier commit pin change, and managed-file ownership documentation.
Asyncio background task tracking resources
copier_template_resources/python_asyncio/background_tasks.py, copier_template_resources/python_asyncio/asyncio_fixtures.py
Adds background-task tracking state and registration logic, plus a pytest fixture that waits for tracked tasks and fails tests when captured background task exceptions exist.
Provenance tests and task helper
tests/unit/copier_tasks/helpers.py, tests/unit/copier_tasks/test_copier_provenance.py
Adds the subprocess helper and comprehensive provenance tests for Jinja filename handling, comment styles and placement, shebang relocation, and managed-files manifest behavior.
Repository managed-header rollout and config updates
.devcontainer/*, .github/**, .clause/??, .*rc, *.toml, *.ini, *.md, src/, scripts/, sh.bat, tests/copier_data/*, .claude/*
Adds managed-file warning headers across repository assets, config files, scripts, docs, issue templates, Claude resources, and supporting test data.
Template mirror managed-header rollout
template/.devcontainer/*, template/.github/**, template/.claude/**, template/.*, template/*.toml, template/*.md
Adds matching managed-file headers across template mirror files and keeps template-specific formatting/comment-placement adjustments.
Template mirror config and issue-template content
template/.github/ISSUE_TEMPLATE/feature-request.yml, template/.github/ISSUE_TEMPLATE/tech-debt.yml, template/pyrefly.toml, template/pnpm-workspace.yaml
Updates template issue-template bodies, preserves template config values, and adjusts a small workspace formatting detail.
Additional repo and template config headers
biome.jsonc, pyrefly.toml, ty.toml, ruff*.toml, pytest.ini, pylintrc.toml, pre-commit-config.yaml, _typos.toml, LICENSE, README.md, CHANGELOG.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md, CLAUDE.md, .gitignore, .claudeignore, .claude/settings/*
Adds managed-file headers and a small trailing-comma/config formatting cleanup in _typos.toml.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • idonaldson
  • ejfine
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly matches the main change: adding copier provenance tracking and related metadata.
Description check ✅ Passed The description covers the required main sections and testing notes; only the optional "Other" section is missing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zendern zendern marked this pull request as ready for review June 11, 2026 19:10
zendern added 2 commits June 11, 2026 19:28
Sync from copier-base-template: normalize all directory parts so conditional
dirs like {% if has_backend %}backend{% endif %} resolve correctly.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
tests/unit/copier_tasks/helpers.py (1)

12-23: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Preserve inherited env vars in the subprocess helper.

Passing env= directly replaces the whole environment, so callers that only want to tweak PATH also drop everything else from the parent process state. Merge overrides into os.environ instead.

♻️ Proposed fix
+import os
 import subprocess
 import sys
 from pathlib import Path
@@
     return subprocess.run(  # noqa: S603 -- these are our own scripts
         [sys.executable, str(script_path), *args],
         check=False,
         capture_output=True,
         text=True,
-        env=env,
+        env={**os.environ, **(env or {})},
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/copier_tasks/helpers.py` around lines 12 - 23, The helper
run_copier_task currently passes env directly to subprocess.run which replaces
the entire process environment; instead, merge any provided env overrides into a
copy of the current environment (os.environ.copy()), apply the overrides, and
pass that merged dict to subprocess.run so callers can tweak PATH or other vars
without losing inherited environment variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.coderabbit.yaml:
- Around line 21-26: The PR touches copier-managed files but lacks the required
single summary comment; add one consolidated comment (not per-file) stating
which files in this change are listed in .copier-managed-files.json and that
these changes may need backporting to the owning template chain, and include the
template ownership info resolved from the manifest "templates" array by tracing
parent_src to the authoritative owner (use the manifest entries to map
.coderabbit.yaml, copier.yml, AGENTS.md, and
src/copier_tasks/copier_provenance.py to their template repo); ensure the
comment mentions that ownership is decided per template chain and not per-file.

In `@src/copier_tasks/copier_provenance.py`:
- Around line 106-121: The file marker write path in _write_file_marker uses
Path.open(file, "r+") without specifying encoding, risking locale-dependent
corruption for non-ASCII files; change the open call(s) to explicitly use
encoding="utf-8" (e.g., Path.open(file, "r+", encoding="utf-8")) so reads and
writes use UTF-8 consistently and avoid data loss when manipulating content in
_write_file_marker.

---

Outside diff comments:
In `@tests/unit/copier_tasks/helpers.py`:
- Around line 12-23: The helper run_copier_task currently passes env directly to
subprocess.run which replaces the entire process environment; instead, merge any
provided env overrides into a copy of the current environment
(os.environ.copy()), apply the overrides, and pass that merged dict to
subprocess.run so callers can tweak PATH or other vars without losing inherited
environment variables.
🪄 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: 6f8e2b78-31be-480e-be85-e8f7c1df343f

📥 Commits

Reviewing files that changed from the base of the PR and between d3aa8b9 and 21ac93c.

📒 Files selected for processing (96)
  • .coderabbit.yaml
  • .copier-answers.yml
  • .copier-managed-files.json
  • .coveragerc
  • .devcontainer/Dockerfile
  • .devcontainer/create-aws-profile.sh
  • .devcontainer/devcontainer.json
  • .devcontainer/docker-compose.yml
  • .devcontainer/initialize-command.sh
  • .devcontainer/install-ci-tooling.py
  • .devcontainer/manual-setup-deps.py
  • .devcontainer/on-create-command-boilerplate.sh
  • .devcontainer/on-create-command.sh
  • .devcontainer/post-start-command.sh
  • .devcontainer/windows-host-helper.sh
  • .github/actions/check-skip-duplicates/action.yml
  • .github/actions/install_deps/action.yml
  • .github/actions/update-devcontainer-hash/action.yml
  • .github/dependabot.yml
  • .github/pull_request_template.md
  • .github/reusable_workflows/build-docker-image.yaml
  • .github/workflows/ci.yaml
  • .github/workflows/confirm-on-tagged-copier-template.yaml
  • .github/workflows/get-values.yaml
  • .github/workflows/hash_git_files.py
  • .github/workflows/pre-commit.yaml
  • .github/workflows/replace_private_package_registries.py
  • .github/workflows/tag-on-merge.yaml
  • .github/zizmor.yml
  • .gitignore
  • .pre-commit-config.yaml
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • LICENSE
  • README.md
  • _typos.toml
  • copier.yml
  • copier_template_resources/eslint.config.mjs
  • copier_template_resources/vcrpy_fixtures.py
  • extensions/context.py
  • pylintrc.toml
  • pyproject.toml
  • pyrefly.toml
  • pytest.ini
  • ruff-non-src.toml
  • ruff.toml
  • scripts/delete_false_positive_rej.py
  • sh.bat
  • src/copier_tasks/__init__.py
  • src/copier_tasks/copier_provenance.py
  • src/copier_tasks/ensure_pnpm_minimum_release_age_exclude.py
  • src/copier_tasks/remove_precommit_hooks.py
  • template/.coderabbit.yaml
  • template/.copier-managed-files.json
  • template/.coveragerc
  • template/.devcontainer/Dockerfile
  • template/.devcontainer/initialize-command.sh
  • template/.devcontainer/manual-setup-deps.py
  • template/.devcontainer/on-create-command-boilerplate.sh
  • template/.devcontainer/windows-host-helper.sh
  • template/.github/pull_request_template.md
  • template/.github/workflows/confirm-on-tagged-copier-template.yaml
  • template/.github/workflows/extract_project_version.py
  • template/.github/workflows/get-values.yaml
  • template/.github/workflows/git_tag.py
  • template/.github/workflows/hash_git_files.py
  • template/.github/workflows/pre-commit.yaml
  • template/.gitignore
  • template/.python-version.jinja
  • template/AGENTS.md
  • template/CHANGELOG.md
  • template/CLAUDE.md
  • template/CONTRIBUTING.md
  • template/README.md.jinja
  • template/_typos.toml
  • template/pnpm-workspace.yaml
  • template/pylintrc.toml
  • template/pyrefly.mutmut.toml
  • template/pyrefly.toml
  • template/pytest.ini
  • template/ruff-non-src.toml
  • template/ruff.toml
  • template/scripts/delete_false_positive_rej.py
  • template/sh.bat
  • template/ty.toml
  • tests/copier_data/data1.yaml
  • tests/copier_data/data2.yaml
  • tests/unit/copier_tasks/__init__.py
  • tests/unit/copier_tasks/helpers.py
  • tests/unit/copier_tasks/test_copier_provenance.py
  • tests/unit/copier_tasks/test_ensure_pnpm_minimum_release_age_exclude.py
  • tests/unit/copier_tasks/test_remove_precommit_hooks.py
  • ty.toml

Comment thread .coderabbit.yaml
Comment thread src/copier_tasks/copier_provenance.py
zendern added 7 commits June 11, 2026 19:43
Adds a second provenance task invocation that runs with src=dst=template/,
so template source files like template/backend/src/__init__.py get the
managed-file header and appear in the nuxt template's own manifest entry.
The symlink template/.copier-managed-files.json → ../.copier-managed-files.json
caused copier to inject the nuxt repo's own manifest (listing internal paths)
into every downstream repo, producing a corrupt manifest. The provenance task
creates the file fresh in the destination, so no pre-seeding is needed.
The task runs in all destination repos (downstream + the nuxt repo itself).
Downstream repos have no ./template/ dir, so the script crashed with
FileNotFoundError. Skip the task when ./template/ is absent.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (4)
copier_template_resources/python_asyncio/asyncio_fixtures.py (2)

22-23: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Drop this one-line docstring or expand it into a real multi-line docstring.

The function name already explains the behavior, so the current one-line docstring just violates the repo docstring rule without adding useful detail. As per coding guidelines, “Never write a one-line docstring — either the name is sufficient or the behavior warrants a full explanation.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@copier_template_resources/python_asyncio/asyncio_fixtures.py` around lines 22
- 23, The one-line docstring for fail_on_background_task_errors should be
removed or expanded into a proper multi-line docstring: either delete the
triple-quoted line so the function name stands alone, or replace it with a
multi-line docstring that describes what the fixture does, when it is applied,
what exceptions it watches for, expected behavior (how tests fail), and any
side-effects or usage notes (e.g., scope or return value). Locate the
fail_on_background_task_errors definition and implement one of these two options
to comply with the repo docstring rule.

Source: Coding guidelines


28-36: ⚠️ Potential issue | 🟠 Major

Fix asyncio teardown: wait tasks on their originating event loop + remove one-line docstring

  • fail_on_background_task_errors() teardown (lines 28-36) waits for asyncio.Task objects from background_tasks_set using asyncio.run(...) or loop.run_until_complete(...). Both are incompatible with task/loop ownership (new-loop waiting from asyncio.run, or run_until_complete on an already-running loop), so the fixture can error instead of reliably surfacing background task failures. Make the teardown an async fixture and await _wait_for_tasks(...) on the originating loop.
  • Remove/expand the one-line docstring on fail_on_background_task_errors() (line 23).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@copier_template_resources/python_asyncio/asyncio_fixtures.py` around lines 28
- 36, Convert the fail_on_background_task_errors fixture teardown to be async so
it awaits _wait_for_tasks on the originating event loop rather than calling
asyncio.run or loop.run_until_complete; specifically, change the fixture
(fail_on_background_task_errors) to async def and in the teardown await
_wait_for_tasks(list(background_tasks_set)) so tasks are awaited on their proper
loop/ownership, and remove the one-line docstring on
fail_on_background_task_errors (replace with a short multi-line docstring if
needed) to satisfy the review comment.
copier_template_resources/python_asyncio/background_tasks.py (1)

15-18: ⚠️ Potential issue | 🟠 Major

Keep strong references to registered background tasks.

background_tasks_set is a WeakSet[asyncio.Task[None]] (Lines 15-18); if the caller doesn’t keep another strong reference, the task can be garbage-collected before _task_done_callback() runs, breaking “fire-and-forget” reliability and exception tracking. Replace it with set[asyncio.Task[None]] and keep the existing discard() cleanup in _task_done_callback() (Lines 51-52).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@copier_template_resources/python_asyncio/background_tasks.py` around lines 15
- 18, Replace the WeakSet with a strong reference set so background tasks aren't
GC'd: change the declaration of background_tasks_set from
WeakSet[asyncio.Task[None]] to set[asyncio.Task[None]] (preserve the name
background_tasks_set and the existing background_task_exceptions deque), and
retain the existing cleanup call to background_tasks_set.discard(...) inside
_task_done_callback() so tasks are removed when done.
src/copier_tasks/copier_provenance.py (1)

137-148: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add explicit UTF-8 encoding and keyword-only parameter marker.

Two issues:

  1. Line 138 opens the file without specifying encoding, which can cause locale-dependent corruption for non-ASCII content (as noted in previous review).
  2. Per coding guidelines, this function should use keyword-only parameters (has three parameters).
🛡️ Proposed fix
-def _write_file_marker(file: Path, comment_format: CommentFormat, specific_header: str) -> None:
-    with Path.open(file, "r+") as f:
+def _write_file_marker(*, file: Path, comment_format: CommentFormat, specific_header: str) -> None:
+    with Path.open(file, "r+", encoding="utf-8") as f:
         content = f.read()
         content = _strip_existing_header(content, comment_format)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/copier_tasks/copier_provenance.py` around lines 137 - 148, Change
_write_file_marker to require keyword-only parameters and open files with
explicit UTF-8 encoding: update the signature from def _write_file_marker(file:
Path, comment_format: CommentFormat, specific_header: str) -> None: to def
_write_file_marker(file: Path, *, comment_format: CommentFormat,
specific_header: str) -> None: and add encoding="utf-8" to the file open call
(currently Path.open(file, "r+")) so the with statement becomes an open that
specifies encoding="utf-8" (preserving mode "r+"); keep the existing logic in
_write_file_marker and _strip_existing_header unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/copier_tasks/copier_provenance.py`:
- Around line 95-110: Several helper functions accept multiple positional
parameters and must enforce keyword-only arguments per project style; update the
function definitions for _build_specific_header, _strip_existing_header,
_resolve_file_src, and _get_comment_format_for_file to include a keyword-only
marker (*) so subsequent parameters must be passed by name. Locate each function
(e.g., def _build_specific_header(comment_type: CommentType, template_src: str =
"") -> str | None:) and change the signature to put a lone * before the second+
parameters (for example def _build_specific_header(comment_type: CommentType, *,
template_src: str = "") -> str | None:), doing the analogous change for
_strip_existing_header, _resolve_file_src, and _get_comment_format_for_file so
all non-first parameters become keyword-only while preserving types, defaults,
and return annotations.

---

Outside diff comments:
In `@copier_template_resources/python_asyncio/asyncio_fixtures.py`:
- Around line 22-23: The one-line docstring for fail_on_background_task_errors
should be removed or expanded into a proper multi-line docstring: either delete
the triple-quoted line so the function name stands alone, or replace it with a
multi-line docstring that describes what the fixture does, when it is applied,
what exceptions it watches for, expected behavior (how tests fail), and any
side-effects or usage notes (e.g., scope or return value). Locate the
fail_on_background_task_errors definition and implement one of these two options
to comply with the repo docstring rule.
- Around line 28-36: Convert the fail_on_background_task_errors fixture teardown
to be async so it awaits _wait_for_tasks on the originating event loop rather
than calling asyncio.run or loop.run_until_complete; specifically, change the
fixture (fail_on_background_task_errors) to async def and in the teardown await
_wait_for_tasks(list(background_tasks_set)) so tasks are awaited on their proper
loop/ownership, and remove the one-line docstring on
fail_on_background_task_errors (replace with a short multi-line docstring if
needed) to satisfy the review comment.

In `@copier_template_resources/python_asyncio/background_tasks.py`:
- Around line 15-18: Replace the WeakSet with a strong reference set so
background tasks aren't GC'd: change the declaration of background_tasks_set
from WeakSet[asyncio.Task[None]] to set[asyncio.Task[None]] (preserve the name
background_tasks_set and the existing background_task_exceptions deque), and
retain the existing cleanup call to background_tasks_set.discard(...) inside
_task_done_callback() so tasks are removed when done.

In `@src/copier_tasks/copier_provenance.py`:
- Around line 137-148: Change _write_file_marker to require keyword-only
parameters and open files with explicit UTF-8 encoding: update the signature
from def _write_file_marker(file: Path, comment_format: CommentFormat,
specific_header: str) -> None: to def _write_file_marker(file: Path, *,
comment_format: CommentFormat, specific_header: str) -> None: and add
encoding="utf-8" to the file open call (currently Path.open(file, "r+")) so the
with statement becomes an open that specifies encoding="utf-8" (preserving mode
"r+"); keep the existing logic in _write_file_marker and _strip_existing_header
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7be30e7a-fb93-4b3b-9620-6754c25f6f8e

📥 Commits

Reviewing files that changed from the base of the PR and between 21ac93c and 031aad0.

📒 Files selected for processing (97)
  • .coderabbit.yaml
  • .copier-answers.yml
  • .copier-managed-files.json
  • .coveragerc
  • .devcontainer/Dockerfile
  • .devcontainer/create-aws-profile.sh
  • .devcontainer/devcontainer.json
  • .devcontainer/docker-compose.yml
  • .devcontainer/initialize-command.sh
  • .devcontainer/install-ci-tooling.py
  • .devcontainer/manual-setup-deps.py
  • .devcontainer/on-create-command-boilerplate.sh
  • .devcontainer/on-create-command.sh
  • .devcontainer/post-start-command.sh
  • .devcontainer/windows-host-helper.sh
  • .github/actions/check-skip-duplicates/action.yml
  • .github/actions/install_deps/action.yml
  • .github/actions/update-devcontainer-hash/action.yml
  • .github/dependabot.yml
  • .github/pull_request_template.md
  • .github/reusable_workflows/build-docker-image.yaml
  • .github/workflows/ci.yaml
  • .github/workflows/confirm-on-tagged-copier-template.yaml
  • .github/workflows/get-values.yaml
  • .github/workflows/hash_git_files.py
  • .github/workflows/pre-commit.yaml
  • .github/workflows/replace_private_package_registries.py
  • .github/workflows/tag-on-merge.yaml
  • .github/zizmor.yml
  • .gitignore
  • .pre-commit-config.yaml
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • LICENSE
  • README.md
  • _typos.toml
  • copier.yml
  • copier_template_resources/eslint.config.mjs
  • copier_template_resources/python_asyncio/asyncio_fixtures.py
  • copier_template_resources/python_asyncio/background_tasks.py
  • copier_template_resources/vcrpy_fixtures.py
  • extensions/context.py
  • pylintrc.toml
  • pyproject.toml
  • pyrefly.toml
  • pytest.ini
  • ruff-non-src.toml
  • ruff.toml
  • scripts/delete_false_positive_rej.py
  • sh.bat
  • src/copier_tasks/__init__.py
  • src/copier_tasks/copier_provenance.py
  • src/copier_tasks/ensure_pnpm_minimum_release_age_exclude.py
  • src/copier_tasks/remove_precommit_hooks.py
  • template/.coderabbit.yaml
  • template/.coveragerc
  • template/.devcontainer/Dockerfile
  • template/.devcontainer/initialize-command.sh
  • template/.devcontainer/manual-setup-deps.py
  • template/.devcontainer/on-create-command-boilerplate.sh
  • template/.devcontainer/windows-host-helper.sh
  • template/.github/pull_request_template.md
  • template/.github/workflows/confirm-on-tagged-copier-template.yaml
  • template/.github/workflows/extract_project_version.py
  • template/.github/workflows/get-values.yaml
  • template/.github/workflows/git_tag.py
  • template/.github/workflows/hash_git_files.py
  • template/.github/workflows/pre-commit.yaml
  • template/.gitignore
  • template/.python-version.jinja
  • template/AGENTS.md
  • template/CHANGELOG.md
  • template/CLAUDE.md
  • template/CONTRIBUTING.md
  • template/README.md.jinja
  • template/_typos.toml
  • template/pnpm-workspace.yaml
  • template/pylintrc.toml
  • template/pyrefly.mutmut.toml
  • template/pyrefly.toml
  • template/pytest.ini
  • template/ruff-non-src.toml
  • template/ruff.toml
  • template/scripts/delete_false_positive_rej.py
  • template/sh.bat
  • template/ty.toml
  • tests/copier_data/data1.yaml
  • tests/copier_data/data2.yaml
  • tests/unit/copier_tasks/__init__.py
  • tests/unit/copier_tasks/helpers.py
  • tests/unit/copier_tasks/test_copier_provenance.py
  • tests/unit/copier_tasks/test_ensure_pnpm_minimum_release_age_exclude.py
  • tests/unit/copier_tasks/test_remove_precommit_hooks.py
  • ty.toml

Comment thread src/copier_tasks/copier_provenance.py
@zendern zendern force-pushed the copier-provenance branch from 090f274 to b9394ce Compare June 12, 2026 16:59
@zendern zendern marked this pull request as draft June 16, 2026 11:34
@zendern zendern marked this pull request as ready for review June 18, 2026 19:52
@zendern zendern requested a review from ejfine June 18, 2026 19:52
Comment thread .claude/commands/commit.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (2)
copier.yml (1)

389-411: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Moving .coveragerc before provenance drops it from the managed-files contract.

copier_provenance.py only stamps files whose destination-relative path matches a template-relative path. The template still contributes .coveragerc, but this task renames it to backend/.coveragerc before the provenance pass runs, so that file will no longer get a header or appear in .copier-managed-files.json for backend repos.

Please either keep the move after the provenance task or teach the provenance matcher about this relocation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@copier.yml` around lines 389 - 411, The `.coveragerc` relocation in the
copier tasks is happening before provenance is generated, so
`copier_provenance.py` no longer sees the file under its template-relative path.
Fix the task order in `copier.yml` so the provenance step runs before moving
`.coveragerc`, or update `copier_provenance.py` to recognize the
`backend/.coveragerc` destination when stamping managed files. Use the existing
`copier_provenance.py` calls and the `.coveragerc` move block as the place to
make the change.
template/.claude/skills/fix-mutants/utils.py (1)

209-228: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Expand or remove the one-line docstring.

The coding guidelines prohibit one-line docstrings. Either the function name run_mutmut is sufficient (in which case remove the docstring) or the behavior warrants a fuller explanation (e.g., describing how the returned CompletedProcess should be interpreted, timeout behavior, and exit conditions).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@template/.claude/skills/fix-mutants/utils.py` around lines 209 - 228, The
`run_mutmut` helper currently has a prohibited one-line docstring, so either
remove it entirely if the function name is self-explanatory or expand it into a
proper multi-line docstring. If you keep documentation, update `run_mutmut` to
describe the `CompletedProcess` return value, the `uv run mutmut` invocation
context, and the `FileNotFoundError`/`TimeoutExpired` exit behavior so the
comment matches the function’s actual behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/copier_tasks/copier_provenance.py`:
- Around line 177-185: The symlink-following walk in
_collect_template_base_paths (and the matching destination traversal) needs
cycle and escape protection: os.walk(..., followlinks=True) can loop forever on
directory symlink cycles and can leave the intended root. Update the traversal
to track visited real paths for each directory before descending, and reject or
skip symlink targets whose resolved path is outside src_template_directory or
dst_directory. Keep the existing symlink support, but make the walk in
_collect_template_base_paths and the destination-marking logic stop on
already-seen real directories and anything that escapes the repo root.

In `@template/.claude/skills/fix-mutants/group-by-line.py`:
- Around line 181-185: The tests-for-mutant call in group_by_line.py is not
checking for command failure, so a nonzero result can be misread as test names.
Update the logic around run_mutmut(["tests-for-mutant", key], ...) to validate
the returncode the same way the earlier mutmut show handling does, and only
parse stdout into tests after a successful run; on failure, surface the
stderr/exit status instead of silently continuing. Use the existing run_mutmut
and tests_result handling in the mutant grouping loop as the fix point.

In `@template/.claude/skills/fix-mutants/list-survived.py`:
- Around line 74-80: The copier warning block in list_survived.py is placed as a
footer instead of a header, which is inconsistent with the rest of the codebase.
Move the managed-by-copier warning comment to the top of the file so it matches
the other Python files, keeping the same warning text and ensuring the file
still remains a valid copier-managed template artifact.
- Around line 37-43: Update parse_status_filter so its optional flag handling is
expressed with keyword-only parameters by adding a * in the signature, and
adjust the main() call site to pass sys.argv[1:] using the new explicit argument
style. Keep the behavior for "--all", "--status", and the ACTIONABLE_STATUSES
fallback unchanged while updating the unique symbols parse_status_filter and
main accordingly.
- Around line 62-68: The call to emit in list_survived.py is discarding a likely
non-void return value, which violates reportUnusedCallResult. Update the code at
the emit call site to explicitly capture the return value in _ if emit returns
something, and keep the surrounding logic unchanged; use the emit helper
invocation and the by_file/total_actionable payload as the anchor for the fix.

In `@template/.claude/skills/fix-mutants/utils.py`:
- Around line 56-58: The `emit` helper currently uses a prohibited one-line
docstring, so either remove the docstring entirely if the function name is
sufficient or expand it into a multi-line description of what `obj` represents
and why output is written to stdout. Update the `emit` function in `utils.py`
accordingly while keeping its JSON serialization behavior unchanged.

---

Outside diff comments:
In `@copier.yml`:
- Around line 389-411: The `.coveragerc` relocation in the copier tasks is
happening before provenance is generated, so `copier_provenance.py` no longer
sees the file under its template-relative path. Fix the task order in
`copier.yml` so the provenance step runs before moving `.coveragerc`, or update
`copier_provenance.py` to recognize the `backend/.coveragerc` destination when
stamping managed files. Use the existing `copier_provenance.py` calls and the
`.coveragerc` move block as the place to make the change.

In `@template/.claude/skills/fix-mutants/utils.py`:
- Around line 209-228: The `run_mutmut` helper currently has a prohibited
one-line docstring, so either remove it entirely if the function name is
self-explanatory or expand it into a proper multi-line docstring. If you keep
documentation, update `run_mutmut` to describe the `CompletedProcess` return
value, the `uv run mutmut` invocation context, and the
`FileNotFoundError`/`TimeoutExpired` exit behavior so the comment matches the
function’s actual behavior.
🪄 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: b94ce3fb-61b6-4c0a-ae8e-750ccf17212b

📥 Commits

Reviewing files that changed from the base of the PR and between 031aad0 and 4f9ac0c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (103)
  • .claude/.beads/.gitignore
  • .claude/.beads/config.yaml
  • .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/hooks/bash-chain-guard.js
  • .claude/hooks/caveman-activate.js
  • .claude/hooks/caveman-config.js
  • .claude/hooks/caveman-mode-tracker.js
  • .claude/settings/basics.jsonc
  • .claude/settings/hooks.jsonc
  • .claude/settings/permissions/additional-dirs.jsonc
  • .claude/settings/permissions/bash.jsonc
  • .claude/settings/permissions/read.jsonc
  • .claude/settings/permissions/write.jsonc
  • .claude/skills/address-pr-comments/SKILL.md
  • .claude/skills/address-pr-comments/check-footer.py
  • .claude/skills/address-pr-comments/fetch-pr-comments.py
  • .claude/skills/address-pr-comments/post-reply.py
  • .claude/skills/address-pr-comments/utils.py
  • .claude/skills/caveman/SKILL.md
  • .claudeignore
  • .copier-answers.yml
  • .copier-managed-files.json
  • .devcontainer/devcontainer.json
  • .github/ISSUE_TEMPLATE/bug-report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature-request.yml
  • .github/ISSUE_TEMPLATE/tech-debt.yml
  • biome.jsonc
  • copier.yml
  • extensions/context.py
  • pyproject.toml
  • pyrefly.toml
  • src/copier_tasks/copier_provenance.py
  • template/.claude/.beads/.gitignore
  • template/.claude/.beads/config.yaml
  • 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/hooks/bash-chain-guard.js
  • template/.claude/hooks/caveman-activate.js
  • template/.claude/hooks/caveman-config.js
  • template/.claude/hooks/caveman-mode-tracker.js
  • template/.claude/settings/basics.jsonc
  • template/.claude/settings/hooks.jsonc
  • template/.claude/settings/permissions/additional-dirs.jsonc
  • template/.claude/settings/permissions/bash.jsonc
  • template/.claude/settings/permissions/read.jsonc
  • template/.claude/settings/permissions/write.jsonc
  • template/.claude/skills/address-pr-comments/SKILL.md
  • template/.claude/skills/address-pr-comments/check-footer.py
  • template/.claude/skills/address-pr-comments/fetch-pr-comments.py
  • template/.claude/skills/address-pr-comments/post-reply.py
  • template/.claude/skills/address-pr-comments/utils.py
  • template/.claude/skills/caveman/SKILL.md
  • template/.claude/skills/fix-mutants/SKILL.md
  • template/.claude/skills/fix-mutants/check-results.py
  • template/.claude/skills/fix-mutants/group-by-line.py
  • template/.claude/skills/fix-mutants/list-survived.py
  • template/.claude/skills/fix-mutants/run-mutmut.py
  • template/.claude/skills/fix-mutants/show-mutant.py
  • template/.claude/skills/fix-mutants/utils.py
  • template/.claude/skills/fix-mutants/verify-mutant.py
  • template/.github/ISSUE_TEMPLATE/bug-report.yml
  • template/.github/ISSUE_TEMPLATE/config.yml
  • template/.github/ISSUE_TEMPLATE/feature-request.yml
  • template/.github/ISSUE_TEMPLATE/tech-debt.yml
  • template/.github/actions/check-skip-duplicates/action.yml
  • template/.github/actions/install_deps/action.yml
  • template/.github/actions/update-devcontainer-hash/action.yml
  • template/biome.jsonc
  • template/pyrefly.mutmut.toml
  • template/pyrefly.toml
  • tests/unit/copier_tasks/test_copier_provenance.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (2)
copier.yml (1)

389-411: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Moving .coveragerc before provenance drops it from the managed-files contract.

copier_provenance.py only stamps files whose destination-relative path matches a template-relative path. The template still contributes .coveragerc, but this task renames it to backend/.coveragerc before the provenance pass runs, so that file will no longer get a header or appear in .copier-managed-files.json for backend repos.

Please either keep the move after the provenance task or teach the provenance matcher about this relocation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@copier.yml` around lines 389 - 411, The `.coveragerc` relocation in the
copier tasks is happening before provenance is generated, so
`copier_provenance.py` no longer sees the file under its template-relative path.
Fix the task order in `copier.yml` so the provenance step runs before moving
`.coveragerc`, or update `copier_provenance.py` to recognize the
`backend/.coveragerc` destination when stamping managed files. Use the existing
`copier_provenance.py` calls and the `.coveragerc` move block as the place to
make the change.
template/.claude/skills/fix-mutants/utils.py (1)

209-228: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Expand or remove the one-line docstring.

The coding guidelines prohibit one-line docstrings. Either the function name run_mutmut is sufficient (in which case remove the docstring) or the behavior warrants a fuller explanation (e.g., describing how the returned CompletedProcess should be interpreted, timeout behavior, and exit conditions).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@template/.claude/skills/fix-mutants/utils.py` around lines 209 - 228, The
`run_mutmut` helper currently has a prohibited one-line docstring, so either
remove it entirely if the function name is self-explanatory or expand it into a
proper multi-line docstring. If you keep documentation, update `run_mutmut` to
describe the `CompletedProcess` return value, the `uv run mutmut` invocation
context, and the `FileNotFoundError`/`TimeoutExpired` exit behavior so the
comment matches the function’s actual behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/copier_tasks/copier_provenance.py`:
- Around line 177-185: The symlink-following walk in
_collect_template_base_paths (and the matching destination traversal) needs
cycle and escape protection: os.walk(..., followlinks=True) can loop forever on
directory symlink cycles and can leave the intended root. Update the traversal
to track visited real paths for each directory before descending, and reject or
skip symlink targets whose resolved path is outside src_template_directory or
dst_directory. Keep the existing symlink support, but make the walk in
_collect_template_base_paths and the destination-marking logic stop on
already-seen real directories and anything that escapes the repo root.

In `@template/.claude/skills/fix-mutants/group-by-line.py`:
- Around line 181-185: The tests-for-mutant call in group_by_line.py is not
checking for command failure, so a nonzero result can be misread as test names.
Update the logic around run_mutmut(["tests-for-mutant", key], ...) to validate
the returncode the same way the earlier mutmut show handling does, and only
parse stdout into tests after a successful run; on failure, surface the
stderr/exit status instead of silently continuing. Use the existing run_mutmut
and tests_result handling in the mutant grouping loop as the fix point.

In `@template/.claude/skills/fix-mutants/list-survived.py`:
- Around line 74-80: The copier warning block in list_survived.py is placed as a
footer instead of a header, which is inconsistent with the rest of the codebase.
Move the managed-by-copier warning comment to the top of the file so it matches
the other Python files, keeping the same warning text and ensuring the file
still remains a valid copier-managed template artifact.
- Around line 37-43: Update parse_status_filter so its optional flag handling is
expressed with keyword-only parameters by adding a * in the signature, and
adjust the main() call site to pass sys.argv[1:] using the new explicit argument
style. Keep the behavior for "--all", "--status", and the ACTIONABLE_STATUSES
fallback unchanged while updating the unique symbols parse_status_filter and
main accordingly.
- Around line 62-68: The call to emit in list_survived.py is discarding a likely
non-void return value, which violates reportUnusedCallResult. Update the code at
the emit call site to explicitly capture the return value in _ if emit returns
something, and keep the surrounding logic unchanged; use the emit helper
invocation and the by_file/total_actionable payload as the anchor for the fix.

In `@template/.claude/skills/fix-mutants/utils.py`:
- Around line 56-58: The `emit` helper currently uses a prohibited one-line
docstring, so either remove the docstring entirely if the function name is
sufficient or expand it into a multi-line description of what `obj` represents
and why output is written to stdout. Update the `emit` function in `utils.py`
accordingly while keeping its JSON serialization behavior unchanged.

---

Outside diff comments:
In `@copier.yml`:
- Around line 389-411: The `.coveragerc` relocation in the copier tasks is
happening before provenance is generated, so `copier_provenance.py` no longer
sees the file under its template-relative path. Fix the task order in
`copier.yml` so the provenance step runs before moving `.coveragerc`, or update
`copier_provenance.py` to recognize the `backend/.coveragerc` destination when
stamping managed files. Use the existing `copier_provenance.py` calls and the
`.coveragerc` move block as the place to make the change.

In `@template/.claude/skills/fix-mutants/utils.py`:
- Around line 209-228: The `run_mutmut` helper currently has a prohibited
one-line docstring, so either remove it entirely if the function name is
self-explanatory or expand it into a proper multi-line docstring. If you keep
documentation, update `run_mutmut` to describe the `CompletedProcess` return
value, the `uv run mutmut` invocation context, and the
`FileNotFoundError`/`TimeoutExpired` exit behavior so the comment matches the
function’s actual behavior.
🪄 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: b94ce3fb-61b6-4c0a-ae8e-750ccf17212b

📥 Commits

Reviewing files that changed from the base of the PR and between 031aad0 and 4f9ac0c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (103)
  • .claude/.beads/.gitignore
  • .claude/.beads/config.yaml
  • .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/hooks/bash-chain-guard.js
  • .claude/hooks/caveman-activate.js
  • .claude/hooks/caveman-config.js
  • .claude/hooks/caveman-mode-tracker.js
  • .claude/settings/basics.jsonc
  • .claude/settings/hooks.jsonc
  • .claude/settings/permissions/additional-dirs.jsonc
  • .claude/settings/permissions/bash.jsonc
  • .claude/settings/permissions/read.jsonc
  • .claude/settings/permissions/write.jsonc
  • .claude/skills/address-pr-comments/SKILL.md
  • .claude/skills/address-pr-comments/check-footer.py
  • .claude/skills/address-pr-comments/fetch-pr-comments.py
  • .claude/skills/address-pr-comments/post-reply.py
  • .claude/skills/address-pr-comments/utils.py
  • .claude/skills/caveman/SKILL.md
  • .claudeignore
  • .copier-answers.yml
  • .copier-managed-files.json
  • .devcontainer/devcontainer.json
  • .github/ISSUE_TEMPLATE/bug-report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature-request.yml
  • .github/ISSUE_TEMPLATE/tech-debt.yml
  • biome.jsonc
  • copier.yml
  • extensions/context.py
  • pyproject.toml
  • pyrefly.toml
  • src/copier_tasks/copier_provenance.py
  • template/.claude/.beads/.gitignore
  • template/.claude/.beads/config.yaml
  • 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/hooks/bash-chain-guard.js
  • template/.claude/hooks/caveman-activate.js
  • template/.claude/hooks/caveman-config.js
  • template/.claude/hooks/caveman-mode-tracker.js
  • template/.claude/settings/basics.jsonc
  • template/.claude/settings/hooks.jsonc
  • template/.claude/settings/permissions/additional-dirs.jsonc
  • template/.claude/settings/permissions/bash.jsonc
  • template/.claude/settings/permissions/read.jsonc
  • template/.claude/settings/permissions/write.jsonc
  • template/.claude/skills/address-pr-comments/SKILL.md
  • template/.claude/skills/address-pr-comments/check-footer.py
  • template/.claude/skills/address-pr-comments/fetch-pr-comments.py
  • template/.claude/skills/address-pr-comments/post-reply.py
  • template/.claude/skills/address-pr-comments/utils.py
  • template/.claude/skills/caveman/SKILL.md
  • template/.claude/skills/fix-mutants/SKILL.md
  • template/.claude/skills/fix-mutants/check-results.py
  • template/.claude/skills/fix-mutants/group-by-line.py
  • template/.claude/skills/fix-mutants/list-survived.py
  • template/.claude/skills/fix-mutants/run-mutmut.py
  • template/.claude/skills/fix-mutants/show-mutant.py
  • template/.claude/skills/fix-mutants/utils.py
  • template/.claude/skills/fix-mutants/verify-mutant.py
  • template/.github/ISSUE_TEMPLATE/bug-report.yml
  • template/.github/ISSUE_TEMPLATE/config.yml
  • template/.github/ISSUE_TEMPLATE/feature-request.yml
  • template/.github/ISSUE_TEMPLATE/tech-debt.yml
  • template/.github/actions/check-skip-duplicates/action.yml
  • template/.github/actions/install_deps/action.yml
  • template/.github/actions/update-devcontainer-hash/action.yml
  • template/biome.jsonc
  • template/pyrefly.mutmut.toml
  • template/pyrefly.toml
  • tests/unit/copier_tasks/test_copier_provenance.py
🛑 Comments failed to post (6)
src/copier_tasks/copier_provenance.py (1)

177-185: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard the symlink-following walks before this can hang or escape the repo root.

Both new os.walk(..., followlinks=True) traversals descend through directory symlinks without tracking visited real paths. A cycle like a -> .. will recurse forever, and the destination walk can also follow a symlink out of dst_directory, letting the marker pass rewrite files outside the project tree.

Please keep the symlink support, but stop descending into already-seen real directories and reject symlink targets outside the traversal root.

Also applies to: 205-207

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/copier_tasks/copier_provenance.py` around lines 177 - 185, The
symlink-following walk in _collect_template_base_paths (and the matching
destination traversal) needs cycle and escape protection: os.walk(...,
followlinks=True) can loop forever on directory symlink cycles and can leave the
intended root. Update the traversal to track visited real paths for each
directory before descending, and reject or skip symlink targets whose resolved
path is outside src_template_directory or dst_directory. Keep the existing
symlink support, but make the walk in _collect_template_base_paths and the
destination-marking logic stop on already-seen real directories and anything
that escapes the repo root.
template/.claude/skills/fix-mutants/group-by-line.py (1)

181-185: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add error handling for mutmut tests-for-mutant failure.

The returncode of run_mutmut(["tests-for-mutant", key], ...) is not checked, unlike the mutmut show call above. If this command fails, the error output is silently parsed as test names, producing confusing results. Add a returncode check consistent with line 157.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@template/.claude/skills/fix-mutants/group-by-line.py` around lines 181 - 185,
The tests-for-mutant call in group_by_line.py is not checking for command
failure, so a nonzero result can be misread as test names. Update the logic
around run_mutmut(["tests-for-mutant", key], ...) to validate the returncode the
same way the earlier mutmut show handling does, and only parse stdout into tests
after a successful run; on failure, surface the stderr/exit status instead of
silently continuing. Use the existing run_mutmut and tests_result handling in
the mutant grouping loop as the fix point.
template/.claude/skills/fix-mutants/list-survived.py (3)

37-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use keyword-only parameters for optional flags.

The parse_status_filter function accepts a positional argv list but is only called with sys.argv[1:] in main. Since this is a script entry-point helper that shouldn't be called with keyword arguments, but per project guidelines prefer * in Python signatures to enforce clarity:

-def parse_status_filter(argv: list[str]) -> set[str] | None:
+def parse_status_filter(*, argv: list[str]) -> set[str] | None:

This would require updating the call site in main() accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@template/.claude/skills/fix-mutants/list-survived.py` around lines 37 - 43,
Update parse_status_filter so its optional flag handling is expressed with
keyword-only parameters by adding a * in the signature, and adjust the main()
call site to pass sys.argv[1:] using the new explicit argument style. Keep the
behavior for "--all", "--status", and the ACTIONABLE_STATUSES fallback unchanged
while updating the unique symbols parse_status_filter and main accordingly.

Source: Coding guidelines


62-68: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assign unused emit return value to _ if it returns a value.

The emit helper likely returns a value (e.g., the JSON string or bytes), but the result is discarded here. Per project guidelines, respect reportUnusedCallResult by assigning to _:

-    emit(
+    _ = emit(
         {
             "backend_root": str(backend_root),
             "total_actionable": total,
             "by_file": dict(sorted(by_file.items())),
         }
     )
📝 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.

    _ = emit(
        {
            "backend_root": str(backend_root),
            "total_actionable": total,
            "by_file": dict(sorted(by_file.items())),
        }
    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@template/.claude/skills/fix-mutants/list-survived.py` around lines 62 - 68,
The call to emit in list_survived.py is discarding a likely non-void return
value, which violates reportUnusedCallResult. Update the code at the emit call
site to explicitly capture the return value in _ if emit returns something, and
keep the surrounding logic unchanged; use the emit helper invocation and the
by_file/total_actionable payload as the anchor for the fix.

Source: Coding guidelines


74-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider moving the copier warning header to the top of the file for consistency.

Other files in this PR place the warning comment block at the top (header), but this file places it at the bottom (footer). If the tooling supports headers in Python files, consider moving it to the top for consistency with the rest of the codebase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@template/.claude/skills/fix-mutants/list-survived.py` around lines 74 - 80,
The copier warning block in list_survived.py is placed as a footer instead of a
header, which is inconsistent with the rest of the codebase. Move the
managed-by-copier warning comment to the top of the file so it matches the other
Python files, keeping the same warning text and ensuring the file still remains
a valid copier-managed template artifact.
template/.claude/skills/fix-mutants/utils.py (1)

56-58: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Expand or remove the one-line docstring.

The coding guidelines prohibit one-line docstrings. Either the function name emit is self-explanatory (in which case remove the docstring) or the behavior warrants a fuller explanation (e.g., describing the expected shape of obj and why stdout is used).

🧰 Tools
🪛 ast-grep (0.44.0)

[info] 57-57: use jsonify instead of json.dumps for JSON output
Context: json.dumps(obj, indent=2)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@template/.claude/skills/fix-mutants/utils.py` around lines 56 - 58, The
`emit` helper currently uses a prohibited one-line docstring, so either remove
the docstring entirely if the function name is sufficient or expand it into a
multi-line description of what `obj` represents and why output is written to
stdout. Update the `emit` function in `utils.py` accordingly while keeping its
JSON serialization behavior unchanged.

Source: Coding guidelines

@zendern zendern enabled auto-merge (squash) June 29, 2026 15:25
@zendern zendern merged commit d852a01 into main Jun 29, 2026
25 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.

2 participants