Skip to content

chore: update rhiza to v1.3.2 - #26

Open
tschm wants to merge 2 commits into
masterfrom
rhiza_v1.3.2_20260805
Open

chore: update rhiza to v1.3.2#26
tschm wants to merge 2 commits into
masterfrom
rhiza_v1.3.2_20260805

Conversation

@tschm

@tschm tschm commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Template: jebel-quant/rhizav0.18.8v1.3.2

This is a major-version bump (0.x → 1.x), so the diff is much larger than a
routine sync: 48 template-owned files committed (30 modified, 18 added).

Conflicts

7 workflow files conflicted and were resolved by taking the upstream (template) side:
rhiza_benchmark.yml, rhiza_book.yml, rhiza_ci.yml, rhiza_codeql.yml,
rhiza_marimo.yml, rhiza_release.yml (2 blocks), rhiza_weekly.yml.

⚠️ 32 stale files deleted on disk but NOT in this PR

The v1.x template dropped the whole .rhiza/tests/{api,integration,structure,sync, stress,utils}/, .rhiza/requirements/ and .rhiza/utils/ layout, plus
.github/workflows/rhiza_sync.yml, .rhiza/make.d/releasing.mk,
.rhiza/.rhiza-version and .rhiza/.cfg.toml.

The sync removed them from the working tree, but they are not in the new
template.lock file list, so stage_synced.py deliberately left those deletions
unstaged — /rhiza:update only commits paths the template owns. They are therefore
still present on this branch.
Full list:

.github/workflows/rhiza_sync.yml       .rhiza/tests/integration/test_test_mk.py
.rhiza/.cfg.toml                       .rhiza/tests/integration/test_virtual_env_unexport.py
.rhiza/.rhiza-version                  .rhiza/tests/shell/test_scripts.sh
.rhiza/make.d/releasing.mk             .rhiza/tests/stress/README.md
.rhiza/requirements/README.md          .rhiza/tests/stress/__init__.py
.rhiza/requirements/docs.txt           .rhiza/tests/stress/conftest.py
.rhiza/requirements/marimo.txt         .rhiza/tests/structure/test_project_layout.py
.rhiza/requirements/tests.txt          .rhiza/tests/structure/test_pyproject.py
.rhiza/requirements/tools.txt          .rhiza/tests/structure/test_requirements.py
.rhiza/tests/api/conftest.py           .rhiza/tests/sync/conftest.py
.rhiza/tests/api/test_github_targets.py         .rhiza/tests/sync/test_docstrings.py
.rhiza/tests/api/test_make_variable_overrides.py .rhiza/tests/sync/test_readme_validation.py
.rhiza/tests/api/test_makefile_api.py  .rhiza/tests/test_utils.py
.rhiza/tests/api/test_makefile_targets.py .rhiza/tests/utils/test_git_repo_fixture.py
.rhiza/tests/integration/test_book_targets.py .rhiza/utils/pip_audit_policy.py
.rhiza/tests/integration/test_docs_targets.py .rhiza/utils/suppression_audit.py

They almost certainly should be removed as part of this migration — but that's a
deliberate call, so it needs a separate commit rather than being folded in here.

Note

profiles: is still just github-project; the other five repos also carry
templates: [legal]. Worth considering separately — a version bump shouldn't change it.


No gates were run — run /rhiza:quality for a scorecard.

Summary by CodeRabbit

  • New Features
    • Added Bash and Zsh completion installation with cached, faster target discovery.
    • Added Make helpers for viewing pull requests, issues, workflow status, releases, and authentication.
    • Added fuzzing, mutation testing, and security scorecard automation.
    • Added release assets containing SBOM and build attestations.
  • Bug Fixes
    • Improved release tag validation and retry handling for package metadata propagation.
    • Added clearer Windows setup diagnostics and safer branch cleanup.
  • Documentation
    • Added guidance for secrets, discussions, completions, testing, and environment configuration.
  • Chores
    • Updated automation tooling and workflow versions.

Copilot AI lite review requested due to automatic review settings August 5, 2026 04:52
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change updates GitHub governance and workflows to Rhiza v1.3.2, expands Rhiza Make targets and shell completions, adds repository validation tests, and refreshes development tooling and release configuration.

Changes

Rhiza repository refresh

Layer / File(s) Summary
GitHub governance and workflow integration
.github/*, .github/workflows/*, .github/rulesets/*
Adds discussion templates and protection rules, upgrades reusable workflows, adds fuzzing, mutation, and Scorecard workflows, and revises release permissions, attestations, tag checks, and artifact handling.
Rhiza Make targets and shell completions
.rhiza/make.d/*, .rhiza/rhiza.mk, .rhiza/completions/*, .rhiza/.env
Adds Python, GitHub, and completion targets; caches shell completion data; adds Windows shell checks; and removes obsolete core synchronization targets.
Repository validation suite
.rhiza/tests/*, tests/test_rhiza_packaging.py, pytest.ini, docs/development/TESTS.md
Adds doctest, metadata, README, tag-reachability, packaging, and test-environment validation.
Development tooling and release configuration
.pre-commit-config.yaml, ruff.toml, cliff.toml, Makefile, .gitignore, docs/index.md
Updates hooks and lint rules, adds changelog configuration, adjusts package tooling, and adds Rust artifact ignores.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • tschm/pycharting#25: Updates overlapping Rhiza templates, workflows, Make fragments, completions, tests, and configuration.

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the migration and conflicts but omits the required template sections, issue reference, testing checklist, and project checklist. Use the required template with Summary, issue reference, Changes, Testing, and Checklist sections, and record the status of each required check.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: updating Rhiza to version v1.3.2.
Docstring Coverage ✅ Passed Docstring coverage is 98.08% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rhiza_v1.3.2_20260805

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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR syncs the repository against the upstream jebel-quant/rhiza template, bumping from v0.18.8 to v1.3.2. It modernizes the Rhiza-managed project scaffolding (Makefile fragments, lint/test tooling, and GitHub workflows) to match the v1.x template structure.

Changes:

  • Updates Rhiza template pin and lockfile to v1.3.2, bringing in new/updated template-owned files (tests, make fragments, workflows, rulesets, config docs).
  • Refactors Makefile responsibilities into language-neutral core (.rhiza/rhiza.mk, quality/bootstrap) plus a Python language layer (.rhiza/make.d/python.mk), and moves optional test extras into the tests bundle fragment.
  • Updates lint/test config (ruff.toml, pytest.ini) and adds baseline packaging/test invariants.

Reviewed changes

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

Show a summary per file
File Description
tests/test_rhiza_packaging.py Adds a baseline packaging invariant test (declared vs installed version).
ruff.toml Updates Ruff rule selection and per-file ignores; removes explicit target-version setting.
pytest.ini Adds .rhiza/tests to pythonpath, changes default live logging, adds a specific warning-as-error filter.
Makefile Adjusts template override variables (notably mkdocs extra packages).
docs/index.md Trims trailing blank line from docs include stub.
docs/development/TESTS.md Documents opting into live pytest CLI logs; updates test dependency provisioning notes.
cliff.toml Adds git-cliff configuration for changelog generation.
.rhiza/tests/test_release_tags.py Adds git tag reachability invariant test for releases/changelog boundaries.
.rhiza/tests/test_readme.py Adds README existence + bash-fence syntax validation (language-neutral).
.rhiza/tests/test_readme_validation.py Adds Python README fence execution + result diffing (Python-only).
.rhiza/tests/test_pyproject.py Adds pyproject structure + release-config invariants (requires bump-my-version discoverability).
.rhiza/tests/test_docstrings.py Adds doctest discovery across source modules with .rhiza/.env-driven source folder.
.rhiza/tests/README.md Rewrites Rhiza test-suite documentation to match the new flat-per-concern layout.
.rhiza/tests/conftest.py Replaces legacy fixtures with shared root/logger/latest_tag fixtures.
.rhiza/template.yml Updates template ref to v1.3.2.
.rhiza/template.lock Updates template SHA/ref and the template-owned file list for v1.3.2.
.rhiza/rhiza.mk Updates core make logic (POSIX shell check on Windows, uv tool-runner framing, help regex tweak, etc.).
.rhiza/make.d/test.mk Moves core Python gates out; keeps optional extras (bench/hypothesis/stress/mutation) with uv run --with ….
.rhiza/make.d/quality.mk Switches fmt to prek; adds/updates rhiza-test runner; simplifies core quality gates.
.rhiza/make.d/python.mk Adds the Python language layer: install, all, test, typecheck, security, docs-coverage, deps, license, etc.
.rhiza/make.d/marimo.mk Contributes marimo folder to deptry scan with appropriate ignores.
.rhiza/make.d/github.mk Adds GitHub maintenance helper targets that shell out to gh.
.rhiza/make.d/doctor.mk Makes doctor extensible via double-colon; adjusts status output formatting.
.rhiza/make.d/completions.mk Adds a make target to install bundled bash/zsh completion scripts.
.rhiza/make.d/bootstrap.mk Removes language-specific install; keeps uv bootstrap + clean hooks; tightens stale-branch deletion filter.
.rhiza/make.d/book.mk Updates book build and marimo export to provision tools via uv run --with ….
.rhiza/completions/rhiza-completion.zsh Adds cached target/description parsing for faster completions.
.rhiza/completions/rhiza-completion.bash Adds cached target parsing for faster completions.
.rhiza/completions/README.md Updates completion docs; adds quick-install instructions; documents caching behavior.
.rhiza/.env Expands documentation and provides default override examples for core env variables.
.pre-commit-config.yaml Updates hook versions; pins node version for hook runtime; adds betterleaks; bumps rhiza-hooks major.
.gitignore Adds Rust-related ignores (template-shared).
.github/workflows/rhiza_weekly.yml Updates reusable workflow pin to v1.3.2.
.github/workflows/rhiza_scorecard.yml Adds OSSF Scorecard workflow stub pinned to v1.3.2.
.github/workflows/rhiza_release.yml Major release workflow updates (permissions hardening, attestation assets, changelog flow change, robustness tweaks).
.github/workflows/rhiza_mutation.yml Adds opt-in mutation testing workflow stub pinned to v1.3.2.
.github/workflows/rhiza_marimo.yml Updates reusable workflow pin to v1.3.2.
.github/workflows/rhiza_fuzzing.yml Adds fuzzing workflow stub pinned to v1.3.2.
.github/workflows/rhiza_codeql.yml Updates reusable workflow pin; moves required permissions to job-level.
.github/workflows/rhiza_ci.yml Updates reusable workflow pin to v1.3.2 and documents Python matrix source-of-truth.
.github/workflows/rhiza_book.yml Updates triggers/permissions; updates reusable workflow pin to v1.3.2.
.github/workflows/rhiza_benchmark.yml Updates reusable workflow pin to v1.3.2.
.github/rulesets/tag-protection.json Adds template-provided tag protection ruleset definition.
.github/rulesets/main-branch-protection.json Adds template-provided main branch protection ruleset definition.
.github/pull_request_template.md Updates checklist (CHANGELOG entry + make deps terminology).
.github/DISCUSSION_TEMPLATE/ideas.yml Adds an “Idea” discussion template.
.github/DISCUSSION_TEMPLATE/help-wanted.yml Adds a “Help Wanted” discussion template.
.github/dependabot.yml Trims trailing whitespace / formatting fix.
.github/CONFIG.md Adds documentation for configuring secrets used by Rhiza workflows.
Suppressed comments (1)

.rhiza/tests/test_pyproject.py:281

  • test_a_discoverable_config_exists will fail in this repository because there is currently no [tool.bumpversion] table in pyproject.toml and no other discoverable bump-my-version config files (.bumpversion.toml/.cfg/setup.cfg). To keep the new rhiza-test gate green after the template bump, add a minimal [tool.bumpversion] table to pyproject.toml (commit=false, tag=false, allow_dirty=false) as described in this docstring.

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

Comment on lines +63 to +65
code = "".join(code_blocks) # merged code
expected = "".join(result_blocks) # merged results

Comment thread .rhiza/tests/conftest.py
Comment on lines +6 to +8
Provides shared session-scoped fixtures (``root``, ``logger`` and ``latest_tag``) used
across the test modules.

Comment on lines +187 to +197
def test_no_license_classifier(self, project: dict) -> None:
"""No deprecated 'License :: ' classifier may be present.

PyPI has deprecated the ``License ::`` trove classifiers in favor of the SPDX
``license`` expression field, so the shipped pyproject must not declare one.
"""
classifiers = project.get("classifiers", [])
license_classifiers = [c for c in classifiers if c.startswith("License ::")]
assert not license_classifiers, (
f"classifiers must not include any deprecated 'License :: ' entry; found {license_classifiers}"
)
Comment thread ruff.toml
Comment on lines +69 to +72
"A", # flake8-builtins - Don't shadow Python builtins
"ANN001", # flake8-annotations - Require function argument annotations
"ANN2", # flake8-annotations - Require function return annotations
"ARG", # flake8-unused-arguments - Unused arguments (tests exempt below: pytest fixtures)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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 @.github/workflows/rhiza_weekly.yml:
- Around line 31-32: Update the reusable workflow call in rhiza_weekly.yml to
replace the version tag with a full commit SHA pin and remove secrets: inherit.
Pass only the explicitly required secrets using the called workflow’s expected
secret names.

In @.rhiza/rhiza.mk:
- Around line 128-146: Remove the orphaned readme and version-matrix entries
from the relevant .PHONY declaration in .rhiza/rhiza.mk, leaving all currently
defined targets such as print-logo, help, and ci-os-matrix unchanged.

In @.rhiza/tests/test_pyproject.py:
- Line 31: Update the _SEMVER_RE validation and its usage in test_pyproject.py
to require the entire version string to match the semantic-version pattern,
using fullmatch() and anchoring the pattern so values like 1.2.3invalid are
rejected.

In @.rhiza/tests/test_readme_validation.py:
- Around line 66-79: Update the README example execution around subprocess.run
in the test to enforce a finite timeout, and catch subprocess.TimeoutExpired so
the example is reported as a failed validation rather than hanging indefinitely.
Preserve the existing stdout, stderr, return-code, and expected-output
assertions for processes that complete normally.

In `@docs/development/TESTS.md`:
- Around line 80-82: Update the pytest command code block in TESTS.md by
replacing its opening and closing backtick fences with tilde fences, preserving
the existing bash content unchanged.
🪄 Autofix

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 6504fadd-dbc2-4c4e-a3c3-d1b4e58c3feb

📥 Commits

Reviewing files that changed from the base of the PR and between 372737f and 313f95b.

⛔ Files ignored due to path filters (1)
  • .rhiza/template.lock is excluded by !**/*.lock
📒 Files selected for processing (48)
  • .github/CONFIG.md
  • .github/DISCUSSION_TEMPLATE/help-wanted.yml
  • .github/DISCUSSION_TEMPLATE/ideas.yml
  • .github/dependabot.yml
  • .github/pull_request_template.md
  • .github/rulesets/main-branch-protection.json
  • .github/rulesets/tag-protection.json
  • .github/workflows/rhiza_benchmark.yml
  • .github/workflows/rhiza_book.yml
  • .github/workflows/rhiza_ci.yml
  • .github/workflows/rhiza_codeql.yml
  • .github/workflows/rhiza_fuzzing.yml
  • .github/workflows/rhiza_marimo.yml
  • .github/workflows/rhiza_mutation.yml
  • .github/workflows/rhiza_release.yml
  • .github/workflows/rhiza_scorecard.yml
  • .github/workflows/rhiza_weekly.yml
  • .gitignore
  • .pre-commit-config.yaml
  • .rhiza/.env
  • .rhiza/completions/README.md
  • .rhiza/completions/rhiza-completion.bash
  • .rhiza/completions/rhiza-completion.zsh
  • .rhiza/make.d/book.mk
  • .rhiza/make.d/bootstrap.mk
  • .rhiza/make.d/completions.mk
  • .rhiza/make.d/doctor.mk
  • .rhiza/make.d/github.mk
  • .rhiza/make.d/marimo.mk
  • .rhiza/make.d/python.mk
  • .rhiza/make.d/quality.mk
  • .rhiza/make.d/test.mk
  • .rhiza/rhiza.mk
  • .rhiza/template.yml
  • .rhiza/tests/README.md
  • .rhiza/tests/conftest.py
  • .rhiza/tests/test_docstrings.py
  • .rhiza/tests/test_pyproject.py
  • .rhiza/tests/test_readme.py
  • .rhiza/tests/test_readme_validation.py
  • .rhiza/tests/test_release_tags.py
  • Makefile
  • cliff.toml
  • docs/development/TESTS.md
  • docs/index.md
  • pytest.ini
  • ruff.toml
  • tests/test_rhiza_packaging.py
💤 Files with no reviewable changes (1)
  • docs/index.md

Comment on lines +31 to 32
uses: jebel-quant/rhiza/.github/workflows/rhiza_weekly.yml@v1.3.2
secrets: inherit

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching rhiza_weekly:"
fd -a 'rhiza_weekly\.yml|rhiza_weekly\.yaml' .github . 2>/dev/null || true

echo
echo "Target workflow:"
if [ -f .github/workflows/rhiza_weekly.yml ]; then
  nl -ba .github/workflows/rhiza_weekly.yml | sed -n '1,120p'
fi

echo
echo "Reusable workflow references in workflows:"
rg -n "uses: .*\.github/workflows/.*@|secrets:\s*inherit|secrets:" .github/workflows || true

Repository: tschm/pycharting

Length of output: 296


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Target workflow:"
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/rhiza_weekly.yml | sed -n '1,120p'

echo
echo "Reusable workflow references in workflows:"
rg -n "uses: .*\.github/workflows/.*@|secrets:\s*inherit|secrets:" .github/workflows || true

echo
echo "Workflow files:"
git ls-files .github/workflows

Repository: tschm/pycharting

Length of output: 3486


🌐 Web query:

GitHub Actions reusable workflows secrets inherit includes all secrets

💡 Result:

Yes, the secrets: inherit keyword in GitHub Actions reusable workflows does include all secrets that the calling workflow has access to [1][2]. When you use secrets: inherit, the called (reusable) workflow automatically receives all secrets available to the caller workflow, including repository-level secrets, organization-level secrets, and environment-scoped secrets [1][3][2]. Key points regarding this behavior: 1. Scope: The reusable workflow gains access to the entire set of secrets that the calling workflow is authorized to use [2]. 2. Implicit Access: Unlike standard reusable workflow calls where you must explicitly map secrets in the on.workflow_call.secrets block, secrets: inherit makes them available implicitly [1][4]. You do not need to define them in the reusable workflow's on key to use them [1][5]. 3. Security Implication: Because it passes the entire collection of available secrets, it is generally considered a best practice to use this feature with caution, particularly when calling third-party or external reusable workflows [2]. If the reusable workflow is compromised, it could theoretically access any secret inherited from the caller [2]. Explicitly passing only the required secrets by name is recommended for sensitive or untrusted workflows to minimize the attack surface [2]. 4. Chain Limitations: Secrets are only inherited by the directly called workflow [1]. If workflow A calls workflow B with secrets: inherit, and workflow B calls workflow C, workflow C will not automatically inherit secrets from A unless they are explicitly passed from B to C or B also uses secrets: inherit [1][4].

Citations:


Sensitive Data Exposure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor

Reachability: Internal

Avoid inheriting secrets into reusable workflow calls.

secrets: inherit forwards all caller secrets to the referenced workflow. Use a full-commit pin for .github/workflows/rhiza_weekly.yml and pass only the specific secrets the called workflow needs.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 31-31: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow

(secrets-inherit)

🤖 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 @.github/workflows/rhiza_weekly.yml around lines 31 - 32, Update the reusable
workflow call in rhiza_weekly.yml to replace the version tag with a full commit
SHA pin and remove secrets: inherit. Pass only the explicitly required secrets
using the called workflow’s expected secret names.

Source: Linters/SAST tools

Comment thread .rhiza/rhiza.mk
Comment on lines +128 to +146
.PHONY: print-logo

##@ Rhiza Workflows

print-logo:
@printf "${BLUE}$$RHIZA_LOGO${RESET}\n"


sync: pre-sync ## sync with template repository as defined in .rhiza/template.yml
@if git remote get-url origin 2>/dev/null | grep -iqE 'jebel-quant/rhiza(\.git)?$$'; then \
printf "${BLUE}[INFO] Skipping sync in rhiza repository (no template.yml by design)${RESET}\n"; \
else \
$(MAKE) install-uv && \
${UVX_BIN} "rhiza==$(RHIZA_VERSION)" sync . && \
$(MAKE) _apply-sync-schedule; \
fi
@$(MAKE) post-sync

_apply-sync-schedule: ## (internal) apply RHIZA_SYNC_SCHEDULE override to GitHub Actions sync workflow
@if [ "$(RHIZA_SYNC_SCHEDULE)" != "0 0 * * 1" ] && [ -f .github/workflows/rhiza_sync.yml ]; then \
sed -i.bak "s|cron: '[^']*'|cron: '$(RHIZA_SYNC_SCHEDULE)'|" .github/workflows/rhiza_sync.yml && rm -f .github/workflows/rhiza_sync.yml.bak; \
printf "${BLUE}[INFO] Applied custom sync schedule: $(RHIZA_SYNC_SCHEDULE)${RESET}\n"; \
fi

materialize: ## [DEPRECATED] use 'make sync' instead — materialize --force is now sync
@printf "${YELLOW}[WARN] 'make materialize' is deprecated and will be removed in a future release.${RESET}\n"
@printf "${YELLOW}[WARN] Please use 'make sync' instead (e.g. 'materialize --force' is now 'make sync').${RESET}\n"
@$(MAKE) sync

summarise-sync: install-uv ## summarise differences created by sync with template repository
@if git remote get-url origin 2>/dev/null | grep -iqE 'jebel-quant/rhiza(\.git)?$$'; then \
printf "${BLUE}[INFO] Skipping summarise-sync in rhiza repository (no template.yml by design)${RESET}\n"; \
else \
$(MAKE) install-uv; \
${UVX_BIN} "rhiza==$(RHIZA_VERSION)" summarise .; \
fi

rhiza-test: install ## run rhiza's own tests (if any)
@if [ -d ".rhiza/tests" ]; then \
${UV_BIN} run pytest .rhiza/tests; \
else \
printf "${YELLOW}[WARN] No .rhiza/tests directory found, skipping rhiza-tests${RESET}\n"; \
fi

validate: pre-validate rhiza-test ## validate project structure against template repository as defined in .rhiza/template.yml
@if git remote get-url origin 2>/dev/null | grep -iqE 'jebel-quant/rhiza(\.git)?$$'; then \
printf "${BLUE}[INFO] Skipping validate in rhiza repository (no template.yml by design)${RESET}\n"; \
else \
$(MAKE) install-uv; \
${UVX_BIN} "rhiza==$(RHIZA_VERSION)" validate .; \
fi
@$(MAKE) post-validate

readme: install-uv ## update README.md with current Makefile help output
@${UVX_BIN} "rhiza-tools>=0.2.0" update-readme

##@ Meta

help: print-logo ## Display this help message
+@printf "$(BOLD)Usage:$(RESET)\n"
+@printf " make $(BLUE)<target>$(RESET)\n\n"
+@printf "$(BOLD)Targets:$(RESET)\n"
+@awk 'BEGIN {FS = ":.*##"; printf ""} /^[a-zA-Z_-]+:.*?##/ { printf " $(BLUE)%-20s$(RESET) %s\n", $$1, $$2 } /^##@/ { printf "\n$(BOLD)%s$(RESET)\n", substr($$0, 5) }' $(MAKEFILE_LIST)
+@awk 'BEGIN {FS = ":.*##"; printf ""} /^[a-zA-Z0-9_-]+:.*?##/ { printf " $(BLUE)%-20s$(RESET) %s\n", $$1, $$2 } /^##@/ { printf "\n$(BOLD)%s$(RESET)\n", substr($$0, 5) }' $(MAKEFILE_LIST)
+@printf "\n"

version-matrix: install-uv ## Emit the list of supported Python versions from pyproject.toml
@${UVX_BIN} "rhiza-tools>=0.2.2" version-matrix

ci-os-matrix: ## Emit GitHub CI OSes (RHIZA_CI_OS_MATRIX as JSON array, default ["ubuntu-latest"])
@printf '%s\n' '$(or $(RHIZA_CI_OS_MATRIX),["ubuntu-latest"])'
@$(info $(or $(RHIZA_CI_OS_MATRIX),["ubuntu-latest"]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Target definitions =='
rg -nP --glob 'Makefile' --iglob '*.mk' \
  '^\s*(readme|version-matrix)\s*:(:)?(?:\s|#|$)' .

printf '%s\n' '== Target callers =='
rg -nP --glob 'Makefile' --iglob '*.mk' --iglob '*.yml' --iglob '*.yaml' \
  '(?:\$\(MAKE\)|make)\s+(?:[^\n]*\s)?(readme|version-matrix)\b' .

Repository: tschm/pycharting

Length of output: 179


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Candidate Makefiles =="
git ls-files | rg '(^|/)(Makefile|.*\.mk)$' || true

printf '%s\n' '== .rhiza/rhiza.mk target declarations/lines 45-150 =='
sed -n '45,155p' .rhiza/rhiza.mk

printf '%s\n' '== All readme/version-matrix references =='
rg -n --glob 'Makefile' --iglob '*.mk' --iglob '*.yml' --iglob '*.yaml' --iglob '*.md' 'readme|version-matrix' . || true

printf '%s\n' '== Makefile parsing probe: defined/readable targets and phony declarations =='
python3 - <<'PY'
import pathlib, re
files = [p for p in pathlib.Path('.').rglob('*') if p.is_file() and (p.name == 'Makefile' or p.suffix == '.mk')]
print('files=', files)
for path in files:
    text = path.read_text(errors='replace')
    lines = text.splitlines()
    print(f'--- {path} ---')
    for i,l in enumerate(lines,1):
        if re.match(r'^\s*(readme|version-matrix)\s*:(:)?(?:\s|#|$)', l):
            print(f'definition {i}: {l}')
        if re.match(r'^\s*\.PHONY\s*:', l):
            print(f'phony {i}: {l}')
PY

Repository: tschm/pycharting

Length of output: 207


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Candidate Makefiles =='
git ls-files | rg '(^|/)(Makefile|.*\.mk)$' || true

printf '%s\n' '== .rhiza/rhiza.mk target declarations/lines 45-150 =='
sed -n '45,155p' .rhiza/rhiza.mk

printf '%s\n' '== All readme/version-matrix references =='
rg -n --glob 'Makefile' --iglob '*.mk' --iglob '*.yml' --iglob '*.yaml' --iglob '*.md' 'readme|version-matrix' . || true

printf '%s\n' '== Makefile parsing probe: definitions and .PHONY declarations =='
python3 - <<'PY'
import pathlib, re

files = []
for p in pathlib.Path('.').rglob('*'):
    if p.is_file() and (p.name == 'Makefile' or p.suffix == '.mk'):
        files.append(p)
print('files=', files)

for path in files:
    text = path.read_text(errors='replace')
    for i, line in enumerate(text.splitlines(), 1):
        if re.match(r'^\s*(readme|version-matrix)\s*:(:)?(?:\s|#|$)', line):
            print(f'definition {path}:{i}: {line}')
        if re.match(r'^\s*\.PHONY\s*:', line):
            print(f'phony {path}:{i}: {line}')
PY

Repository: tschm/pycharting

Length of output: 6471


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Root Makefile =='
sed -n '1,220p' Makefile

printf '%s\n' '== README target references in workflow files =='
rg -n --iglob '*.yml' --iglob '*.yaml' 'readme|version-matrix|\.rhiza/tests/sync/test_readme_validation.py' .github .rhiza || true

printf '%s\n' '== Readme validation test file =='
sed -n '1,260p' .rhiza/tests/sync/test_readme_validation.py

printf '%s\n' '== Target definition text search in relevant files =='
python3 - <<'PY'
import pathlib, re
targets = ["readme", "version-matrix"]
files = [
    pathlib.Path("Makefile"),
    pathlib.Path(".rhiza/rhiza.mk"),
] + sorted(pathlib.Path(".rhiza/make.d").glob("*.mk"))
for path in files:
    text = path.read_text(errors="replace")
    hits = []
    for i, line in enumerate(text.splitlines(), 1):
        if re.search(r"(^|\s)(" + "|".join(re.escape(t) for t in targets) + r")\b", line):
            hits.append((i, line))
    if hits:
        print(f"--- {path} ---")
        for i, line in hits:
            print(f"{i}: {line}")
PY

Repository: tschm/pycharting

Length of output: 8960


Remove the orphaned phony declarations for readme and version-matrix.

.rhiza/rhiza.mk still declares these targets in .PHONY, but no included fragment defines them or calls them. make readme and make version-matrix will complete as no-op targets; remove the declarations unless the targets are restored.

🤖 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 @.rhiza/rhiza.mk around lines 128 - 146, Remove the orphaned readme and
version-matrix entries from the relevant .PHONY declaration in .rhiza/rhiza.mk,
leaving all currently defined targets such as print-logo, help, and ci-os-matrix
unchanged.

import pytest
from packaging.version import Version

_SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject invalid version suffixes.

_SEMVER_RE.match() accepts values such as 1.2.3invalid. This lets an invalid project version pass the semantic-version gate. Anchor the full version pattern and use fullmatch().

Proposed fix
-_SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+")
+_SEMVER_RE = re.compile(r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?")
...
-        assert _SEMVER_RE.match(str(version)), (
+        assert _SEMVER_RE.fullmatch(str(version)), (

Also applies to: 115-120

🤖 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 @.rhiza/tests/test_pyproject.py at line 31, Update the _SEMVER_RE validation
and its usage in test_pyproject.py to require the entire version string to match
the semantic-version pattern, using fullmatch() and anchoring the pattern so
values like 1.2.3invalid are rejected.

Comment on lines +66 to +79
# Trust boundary: we execute Python snippets sourced from README.md in this repo.
# The README is part of the trusted repository content and reviewed in PRs.
logger.debug("Executing README code via %s -c ...", sys.executable)
result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root) # nosec

stdout = result.stdout
logger.debug("Execution finished with return code %d", result.returncode)
if result.stderr:
logger.debug("Stderr from README code:\n%s", result.stderr)
logger.debug("Stdout from README code:\n%s", stdout)

assert result.returncode == 0, f"README code exited with {result.returncode}. Stderr:\n{result.stderr}"
logger.info("README code executed successfully; comparing output to expected result")
assert stdout.strip() == expected.strip()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Set a timeout for README example execution.

If a non-skipped README fence blocks or loops, subprocess.run() waits indefinitely. Set a child-process timeout and report TimeoutExpired as a failed example.

Proposed fix
-    result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root)  # nosec
+    try:
+        result = subprocess.run(
+            [sys.executable, "-c", code],
+            capture_output=True,
+            text=True,
+            cwd=root,
+            timeout=30,
+        )  # nosec
+    except subprocess.TimeoutExpired:
+        pytest.fail("README code exceeded the execution timeout")
📝 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
# Trust boundary: we execute Python snippets sourced from README.md in this repo.
# The README is part of the trusted repository content and reviewed in PRs.
logger.debug("Executing README code via %s -c ...", sys.executable)
result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root) # nosec
stdout = result.stdout
logger.debug("Execution finished with return code %d", result.returncode)
if result.stderr:
logger.debug("Stderr from README code:\n%s", result.stderr)
logger.debug("Stdout from README code:\n%s", stdout)
assert result.returncode == 0, f"README code exited with {result.returncode}. Stderr:\n{result.stderr}"
logger.info("README code executed successfully; comparing output to expected result")
assert stdout.strip() == expected.strip()
# Trust boundary: we execute Python snippets sourced from README.md in this repo.
# The README is part of the trusted repository content and reviewed in PRs.
logger.debug("Executing README code via %s -c ...", sys.executable)
try:
result = subprocess.run(
[sys.executable, "-c", code],
capture_output=True,
text=True,
cwd=root,
timeout=30,
) # nosec
except subprocess.TimeoutExpired:
pytest.fail("README code exceeded the execution timeout")
stdout = result.stdout
logger.debug("Execution finished with return code %d", result.returncode)
if result.stderr:
logger.debug("Stderr from README code:\n%s", result.stderr)
logger.debug("Stdout from README code:\n%s", stdout)
assert result.returncode == 0, f"README code exited with {result.returncode}. Stderr:\n{result.stderr}"
logger.info("README code executed successfully; comparing output to expected result")
assert stdout.strip() == expected.strip()
🧰 Tools
🪛 ast-grep (0.45.0)

[error] 68-68: Command coming from incoming request
Context: subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🪛 Ruff (0.16.1)

[error] 69-69: subprocess call: check for execution of untrusted input

(S603)

🤖 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 @.rhiza/tests/test_readme_validation.py around lines 66 - 79, Update the
README example execution around subprocess.run in the test to enforce a finite
timeout, and catch subprocess.TimeoutExpired so the example is reported as a
failed validation rather than hanging indefinitely. Preserve the existing
stdout, stderr, return-code, and expected-output assertions for processes that
complete normally.

Comment thread docs/development/TESTS.md
Comment on lines +80 to +82
```bash
uv run pytest -o log_cli=true --log-cli-level=DEBUG
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the configured tilde fence style.

Line 80 starts a backtick fence. Markdownlint reports MD048 because this repository requires tilde fences. Replace this fence pair with ~~~.

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 80-80: Code fence style
Expected: tilde; Actual: backtick

(MD048, code-fence-style)

🤖 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 `@docs/development/TESTS.md` around lines 80 - 82, Update the pytest command
code block in TESTS.md by replacing its opening and closing backtick fences with
tilde fences, preserving the existing bash content unchanged.

Source: Linters/SAST tools

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