From 0ac97d40912f834f427e2c873a816f79076b98dd Mon Sep 17 00:00:00 2001 From: CodeSigils Date: Fri, 17 Jul 2026 10:10:30 +0300 Subject: [PATCH] docs: refresh Python workflow guidance --- README.md | 19 ++++++++------ SECURITY.md | 7 +++--- references/drift-classes.md | 6 +++-- references/lint-format-typing-testing.md | 5 ++-- references/pyproject-template.md | 25 ++++++++++++------- scripts/validate.py | 19 ++++++++++++++ skills/python-project-workflow/SKILL.md | 15 ++++++++--- .../references/drift-classes.md | 6 +++-- .../references/lint-format-typing-testing.md | 5 ++-- .../references/pyproject-template.md | 25 ++++++++++++------- 10 files changed, 92 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 9db14fd..00e1985 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ agentskills.io-compatible. Suppose you ask your agent: -> Create a Python 3.10+ command-line project named `invoice-checker`. Use a +> Create a Python 3.11+ command-line project named `invoice-checker`. Use a > `src/` layout, add tests and GitHub Actions, and leave it ready to build. The workflow guides the agent to: @@ -83,7 +83,12 @@ Make the skill discoverable by your agent.
Hermes Agent -**Recommended for development — clone the repo and add to `external_dirs`:** +**Install directly from GitHub:** +```bash +hermes skills install CodeSigils/python-project-workflow-skill/skills/python-project-workflow +``` + +**For development, clone the repo and add its `skills/` directory to `external_dirs`:** ```yaml skills: external_dirs: @@ -91,10 +96,6 @@ skills: ``` Every commit is immediately reflected without reinstalling. -The repository is not currently indexed in the Hermes hub, so no hub-install -command is advertised. Clone plus `external_dirs` is the verified Hermes path -until registration. - *Other agents: see sections below for their native setup commands.*
@@ -102,6 +103,7 @@ until registration. Claude Code ```bash +mkdir -p ~/.claude/skills cp -r skills/python-project-workflow ~/.claude/skills/ ``` @@ -110,6 +112,7 @@ cp -r skills/python-project-workflow ~/.claude/skills/ Codex ```bash +mkdir -p ~/.agents/skills cp -r skills/python-project-workflow ~/.agents/skills/ ``` @@ -118,6 +121,7 @@ cp -r skills/python-project-workflow ~/.agents/skills/ Gemini CLI / .agents/ path ```bash +mkdir -p .agents/skills cp -r skills/python-project-workflow .agents/skills/ ``` @@ -126,6 +130,7 @@ cp -r skills/python-project-workflow .agents/skills/ OpenCode ```bash +mkdir -p .opencode/skills cp -r skills/python-project-workflow .opencode/skills/ ``` @@ -233,7 +238,7 @@ python-project-workflow/ │ ├── sync-payload.sh # Synchronizes or checks the runtime payload mirror │ ├── test-validate-ci.py # Regression tests for CI policy enforcement │ ├── test-sync-payload.py # Regression tests for payload drift behavior -│ ├── validate-ci.py # Enforces CI routing and toolchain policy +│ ├── validate-ci.py # Enforces CI routing, required gates, toolchain policy, and action pins │ ├── validate.py # Checks skill structure, metadata, and references │ └── verify-urls.py # Checks documented external links on a schedule └── skills/ diff --git a/SECURITY.md b/SECURITY.md index f6bcdd3..7f0e48c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -28,8 +28,9 @@ metadata, and the canonical-to-payload mirror process. These components must preserve the declared shipping boundary, avoid exposing credentials, and prevent unreviewed content from entering the runtime payload. -CI validates payload structure, canonical-reference parity, portable -instructions, security directives, and the absence of known unsafe probes. A +CI validates payload structure, regular-file-only canonical and runtime entries, +canonical-reference parity, portable instructions, security directives, and the +absence of known unsafe probes. A passing validator is evidence for these defined checks, not a claim that the repository or generated projects are free of vulnerabilities. @@ -64,4 +65,4 @@ summarizes the trust boundary; it does not replace those runtime instructions. from heuristic or filename checks. - [x] The shipped payload contains no destructive reset, forced-push, or secret-file dumping commands. -Last reviewed: 2026-07-16. +Last reviewed: 2026-07-17. diff --git a/references/drift-classes.md b/references/drift-classes.md index 5871f4f..0b22a03 100644 --- a/references/drift-classes.md +++ b/references/drift-classes.md @@ -10,7 +10,7 @@ difference is critical because they require fundamentally different fixes. | Class | Name | What Drifts | Root Cause | Detection | Fix | |-------|------|-------------|------------|-----------|-----| -| **A** | **Payload drift** | Mirrored payload references differ from their canonical root sources, or an undeclared payload file appears | Human edited a canonical reference but forgot to run sync, edited a mirrored reference directly, or left an orphan | Payload sync check (`sync-payload.sh --ci`) fails | Run `bash scripts/sync-payload.sh` to synchronize declared mirrors and remove orphans | +| **A** | **Payload drift** | Mirrored payload references differ from their canonical root sources, an undeclared payload entry appears, or a canonical/runtime entry is a symlink instead of a regular file | Human edited a canonical reference but forgot to run sync, edited a mirrored reference directly, left an orphan, or introduced a link across the shipping boundary | Payload sync check (`sync-payload.sh --ci`) fails | Run `bash scripts/sync-payload.sh` to synchronize declared mirrors and remove payload-side links/orphans; replace canonical source links manually | | **B** | **Mirror staleness** | Installed copy predates a restructuring | Agent loads a static copy that does not auto-update | `diff -rq /skills// /skills//` shows mismatch | Re-install from source or use a client-supported live source directory | --- @@ -41,12 +41,13 @@ an explicitly allowed authored-in-place file. No scripts currently ship. 3. Added a new source type without updating the manifest or sync policy 4. Renamed/deleted a source file but the old copy remains in payload 5. CI payload sync check was disabled or bypassed +6. Replaced a canonical source or payload entry with a symlink ### Detection - **CI gate**: `bash scripts/sync-payload.sh --ci` exits 1 on any drift - **Local check**: Run the same command locally before committing -- **Manifest validation**: the script verifies declared sources and flags payload orphans +- **Manifest validation**: the script requires regular canonical source files and flags payload symlinks and orphans ### Remediation @@ -147,6 +148,7 @@ prevention for mirror staleness.** | Committing a mirrored reference change without its root source | Creates reference drift | Keep canonical and mirrored reference content identical through the sync command | | Installing skill once and never updating | Mirror staleness guaranteed | Use a live source directory for development; schedule re-installs for production | | Linking individual files instead of the skill directory | Fragile; breaks on restructure | Link or configure the complete `skills/` directory when the client supports it | +| Committing symlinks inside the canonical or runtime payload trees | Can escape the shipping boundary or become broken after copying only the payload | Keep every shipped and canonical reference entry as a regular file; use client-level directory configuration only outside the payload | --- diff --git a/references/lint-format-typing-testing.md b/references/lint-format-typing-testing.md index bdd7355..6d6452b 100644 --- a/references/lint-format-typing-testing.md +++ b/references/lint-format-typing-testing.md @@ -50,7 +50,7 @@ When testing cross-platform compatibility: - Use `nox` or `tox` with matrices covering different Python versions and operating systems - GitHub Actions example: - `strategy.matrix: {os: [ubuntu-latest, windows-latest, macos-latest], python-version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\", \"3.14\"]}` + `strategy.matrix: {os: [ubuntu-latest, windows-latest, macos-latest], python-version: [\"3.11\", \"3.12\", \"3.13\", \"3.14\"]}` - Test console scripts/installation on all target platforms - Consider using `cibuildwheel` for building wheels across platforms - Test path handling with `pathlib` to avoid platform-specific issues @@ -112,7 +112,8 @@ positives or negatives. ## Special Cases -- **Jupyter notebooks**: use `nbqa ruff check notebook.ipynb`. Keep core logic in `.py` files. +- **Jupyter notebooks**: Ruff supports notebooks directly; use `ruff check notebook.ipynb` and + `ruff format --check notebook.ipynb`. Keep core logic in `.py` files. - **Cython**: MyPy may need config to understand `.pyx` files; invoke build system before type checking. - **Generated files**: exclude from lint/format/type checking via `.ruffignore` or tool config. Document ownership. diff --git a/references/pyproject-template.md b/references/pyproject-template.md index af91977..72f545c 100644 --- a/references/pyproject-template.md +++ b/references/pyproject-template.md @@ -2,7 +2,7 @@ This file provides a minimal, modern `pyproject.toml` using PEP 621 metadata. Adjust the values to fit your project. -> **Freshness:** Packaging metadata and Python EOL guidance were reviewed on 2026-07-16. +> **Freshness:** Packaging metadata and Python EOL guidance were reviewed on 2026-07-17. > Verify against official docs before using in production. > See [ruff docs](https://docs.astral.sh/ruff/), [mypy docs](https://mypy.readthedocs.io/), > [pytest docs](https://docs.pytest.org/), [uv docs](https://docs.astral.sh/uv/) for current versions. @@ -23,7 +23,7 @@ name = "your-package" version = "0.1.0" description = "A short description of your package." readme = "README.md" -requires-python = ">=3.10" # or >=3.12 for controlled applications; preserve ecosystem floors +requires-python = ">=3.11" # or >=3.12 for controlled applications; preserve ecosystem floors authors = [ {name = "Your Name", email = "you@example.com"} ] @@ -78,7 +78,7 @@ This tells setuptools to look for packages under the `src/` directory. ```toml [tool.ruff] -target-version = "py310" # Align with requires-python +target-version = "py311" # Align with requires-python line-length = 88 [tool.ruff.lint] @@ -94,7 +94,7 @@ quote-style = "double" ```toml [tool.mypy] -python_version = "3.10" # Target the minimum version in requires-python +python_version = "3.11" # Target the minimum version in requires-python warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true @@ -134,23 +134,30 @@ When authoring cross-platform Python packages: - If you control the deployment environment (e.g., internal tool, web service), consider `requires-python = ">=3.12"` to use the latest features. -- If deploying to older LTS systems (e.g., Ubuntu 22.04 with Python 3.10), use `>=3.10`. +- If deployment must use a distribution's system Python, set the floor to the oldest supported image you actually + deploy. For example, Ubuntu 26.04 LTS currently defaults to Python 3.14; older supported images may require a lower + floor. ### For Libraries -- Aim for broad compatibility: `>=3.10` is the current default when no ecosystem constraint says otherwise. +- Aim for broad compatibility without starting on a near-EOL interpreter: `>=3.11` is the current default when no + ecosystem constraint says otherwise. +- For new projects, do not select Python 3.10 by default: it is in source-only security maintenance and reaches + end-of-life in October 2026. Use `>=3.11` or a newer floor that fits the deployment environment. +- For existing projects that still declare 3.10, preserve the contract unless a compatibility-breaking migration is in + scope. Document the constraint and plan a tested migration before changing `requires-python` or the CI matrix. - For new projects, do not select Python 3.8: it reached end-of-life in October 2024 and no longer receives security - fixes. Use `>=3.10` or a newer floor that fits the deployment environment. + fixes. Use `>=3.11` or a newer floor that fits the deployment environment. - For existing projects that still declare 3.8, preserve the contract unless a compatibility-breaking migration is in scope. Document the legacy constraint, warn that 3.8 is unsupported upstream, and plan a tested migration before changing `requires-python` or the CI matrix. -- Review this floor as Python 3.10 approaches end-of-life in October 2026. ### Dependency Floor - When choosing a dependency version, check its `requires-python` to ensure it doesn't declare a higher floor than your project. -- Use tools like `pip-index-versions` or `pipdeptree` to audit dependency compatibility. +- Use `python -m pip index versions ` to inspect available releases and `uv tree` or `pipdeptree` to audit the + resolved dependency graph. ## Notes diff --git a/scripts/validate.py b/scripts/validate.py index ac608ef..bbdfb9d 100755 --- a/scripts/validate.py +++ b/scripts/validate.py @@ -166,6 +166,16 @@ def check_skill() -> None: if not contains_markdown_phrase(body, directive): fail(f"{rel(SKILL)} missing Python 3.8 EOL policy: {directive}") + python_310_directives = [ + "For new projects, do not select Python 3.10 by default", + "reaches end-of-life in October 2026", + "For an existing project that still declares Python 3.10", + "do not silently raise `requires-python` or remove 3.10 from CI.", + ] + for directive in python_310_directives: + if not contains_markdown_phrase(body, directive): + fail(f"{rel(SKILL)} missing Python 3.10 EOL policy: {directive}") + for command in UV_COMMANDS: if command not in body: fail(f"{rel(SKILL)} missing uv-managed verification command: {command}") @@ -210,6 +220,9 @@ def check_references() -> None: 'license-files = ["LICENSE*"]', "setuptools>=77.0.3", "[dependency-groups]", + 'requires-python = ">=3.11"', + 'target-version = "py311"', + 'python_version = "3.11"', ): if required not in pyproject: fail(f"pyproject template missing current packaging metadata: {required}") @@ -222,12 +235,16 @@ def check_references() -> None: fail(f"tooling reference missing uv-managed command: {command}") if "python -m uv" in tooling: fail("tooling reference must invoke the uv executable directly") + if "nbqa ruff check" in tooling: + fail("tooling reference must use Ruff's native notebook support") drift = read_text_checked(REF_DIR / "drift-classes.md") if "Edit source (root `SKILL.md`" in drift: fail("drift guide points to nonexistent root SKILL.md") if "Its `SKILL.md` is authored in place" not in drift: fail("drift guide does not describe mixed payload ownership") + if "flags payload symlinks and orphans" not in drift: + fail("drift guide does not describe regular-file payload enforcement") safe_editing = read_text_checked(REF_DIR / "safe-editing.md") for platform_marker in ('terminal("cat")', "write_file", "sed -i 's/"): @@ -253,6 +270,7 @@ def check_readme() -> None: "agentskills.io-compatible", "skills/python-project-workflow/", "Shipping boundary: `skills/python-project-workflow/` is the runtime payload", + "hermes skills install CodeSigils/python-project-workflow-skill/skills/python-project-workflow", ] for phrase in required: if not contains_markdown_phrase(readme, phrase): @@ -269,6 +287,7 @@ def check_security() -> None: "effective `.gitignore` behavior", "tracked sensitive files", "redact sensitive values from reports", + "regular-file-only canonical and runtime entries", ] for phrase in required: if not contains_markdown_phrase(security, phrase): diff --git a/skills/python-project-workflow/SKILL.md b/skills/python-project-workflow/SKILL.md index 18a8922..5516010 100644 --- a/skills/python-project-workflow/SKILL.md +++ b/skills/python-project-workflow/SKILL.md @@ -209,17 +209,24 @@ declared Python version range. - If you control the deployment environment (e.g., internal tool, web service), consider `requires-python = ">=3.12"` to use the latest features. -- If deploying to older LTS systems (e.g., Ubuntu 22.04 with Python 3.10), use `>=3.10`. +- If deployment must use a distribution's system Python, set the floor to the oldest supported image you actually + deploy. For example, Ubuntu 26.04 LTS currently defaults to Python 3.14; older supported images may require a lower + floor. ### For Libraries -- Aim for broad compatibility: `>=3.10` is the current default when no ecosystem constraint says otherwise. +- Aim for broad compatibility without starting on a near-EOL interpreter: `>=3.11` is the current default when no + ecosystem constraint says otherwise. +- For new projects, do not select Python 3.10 by default: it is in source-only security maintenance and reaches + end-of-life in October 2026. Use `>=3.11` or a newer floor that fits the deployment environment. +- For an existing project that still declares Python 3.10, preserve the current contract unless the user authorizes a + compatibility-breaking change. Identify the constraint keeping it in scope and recommend a tested migration plan; do + not silently raise `requires-python` or remove 3.10 from CI. - For new projects, do not select Python 3.8: it reached end-of-life in October 2024 and no longer receives security - fixes. Use `>=3.10` or a newer floor that fits the deployment environment. + fixes. Use `>=3.11` or a newer floor that fits the deployment environment. - For an existing project that still declares Python 3.8, preserve the current contract unless the user authorizes a compatibility-breaking change. Warn that 3.8 is unsupported upstream, identify the constraint keeping it in scope, and recommend a tested migration plan. Do not silently raise `requires-python` or remove 3.8 from CI. -- Review this floor as Python 3.10 approaches end-of-life in October 2026. ## Core Python Footguns diff --git a/skills/python-project-workflow/references/drift-classes.md b/skills/python-project-workflow/references/drift-classes.md index 5871f4f..0b22a03 100644 --- a/skills/python-project-workflow/references/drift-classes.md +++ b/skills/python-project-workflow/references/drift-classes.md @@ -10,7 +10,7 @@ difference is critical because they require fundamentally different fixes. | Class | Name | What Drifts | Root Cause | Detection | Fix | |-------|------|-------------|------------|-----------|-----| -| **A** | **Payload drift** | Mirrored payload references differ from their canonical root sources, or an undeclared payload file appears | Human edited a canonical reference but forgot to run sync, edited a mirrored reference directly, or left an orphan | Payload sync check (`sync-payload.sh --ci`) fails | Run `bash scripts/sync-payload.sh` to synchronize declared mirrors and remove orphans | +| **A** | **Payload drift** | Mirrored payload references differ from their canonical root sources, an undeclared payload entry appears, or a canonical/runtime entry is a symlink instead of a regular file | Human edited a canonical reference but forgot to run sync, edited a mirrored reference directly, left an orphan, or introduced a link across the shipping boundary | Payload sync check (`sync-payload.sh --ci`) fails | Run `bash scripts/sync-payload.sh` to synchronize declared mirrors and remove payload-side links/orphans; replace canonical source links manually | | **B** | **Mirror staleness** | Installed copy predates a restructuring | Agent loads a static copy that does not auto-update | `diff -rq /skills// /skills//` shows mismatch | Re-install from source or use a client-supported live source directory | --- @@ -41,12 +41,13 @@ an explicitly allowed authored-in-place file. No scripts currently ship. 3. Added a new source type without updating the manifest or sync policy 4. Renamed/deleted a source file but the old copy remains in payload 5. CI payload sync check was disabled or bypassed +6. Replaced a canonical source or payload entry with a symlink ### Detection - **CI gate**: `bash scripts/sync-payload.sh --ci` exits 1 on any drift - **Local check**: Run the same command locally before committing -- **Manifest validation**: the script verifies declared sources and flags payload orphans +- **Manifest validation**: the script requires regular canonical source files and flags payload symlinks and orphans ### Remediation @@ -147,6 +148,7 @@ prevention for mirror staleness.** | Committing a mirrored reference change without its root source | Creates reference drift | Keep canonical and mirrored reference content identical through the sync command | | Installing skill once and never updating | Mirror staleness guaranteed | Use a live source directory for development; schedule re-installs for production | | Linking individual files instead of the skill directory | Fragile; breaks on restructure | Link or configure the complete `skills/` directory when the client supports it | +| Committing symlinks inside the canonical or runtime payload trees | Can escape the shipping boundary or become broken after copying only the payload | Keep every shipped and canonical reference entry as a regular file; use client-level directory configuration only outside the payload | --- diff --git a/skills/python-project-workflow/references/lint-format-typing-testing.md b/skills/python-project-workflow/references/lint-format-typing-testing.md index bdd7355..6d6452b 100644 --- a/skills/python-project-workflow/references/lint-format-typing-testing.md +++ b/skills/python-project-workflow/references/lint-format-typing-testing.md @@ -50,7 +50,7 @@ When testing cross-platform compatibility: - Use `nox` or `tox` with matrices covering different Python versions and operating systems - GitHub Actions example: - `strategy.matrix: {os: [ubuntu-latest, windows-latest, macos-latest], python-version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\", \"3.14\"]}` + `strategy.matrix: {os: [ubuntu-latest, windows-latest, macos-latest], python-version: [\"3.11\", \"3.12\", \"3.13\", \"3.14\"]}` - Test console scripts/installation on all target platforms - Consider using `cibuildwheel` for building wheels across platforms - Test path handling with `pathlib` to avoid platform-specific issues @@ -112,7 +112,8 @@ positives or negatives. ## Special Cases -- **Jupyter notebooks**: use `nbqa ruff check notebook.ipynb`. Keep core logic in `.py` files. +- **Jupyter notebooks**: Ruff supports notebooks directly; use `ruff check notebook.ipynb` and + `ruff format --check notebook.ipynb`. Keep core logic in `.py` files. - **Cython**: MyPy may need config to understand `.pyx` files; invoke build system before type checking. - **Generated files**: exclude from lint/format/type checking via `.ruffignore` or tool config. Document ownership. diff --git a/skills/python-project-workflow/references/pyproject-template.md b/skills/python-project-workflow/references/pyproject-template.md index af91977..72f545c 100644 --- a/skills/python-project-workflow/references/pyproject-template.md +++ b/skills/python-project-workflow/references/pyproject-template.md @@ -2,7 +2,7 @@ This file provides a minimal, modern `pyproject.toml` using PEP 621 metadata. Adjust the values to fit your project. -> **Freshness:** Packaging metadata and Python EOL guidance were reviewed on 2026-07-16. +> **Freshness:** Packaging metadata and Python EOL guidance were reviewed on 2026-07-17. > Verify against official docs before using in production. > See [ruff docs](https://docs.astral.sh/ruff/), [mypy docs](https://mypy.readthedocs.io/), > [pytest docs](https://docs.pytest.org/), [uv docs](https://docs.astral.sh/uv/) for current versions. @@ -23,7 +23,7 @@ name = "your-package" version = "0.1.0" description = "A short description of your package." readme = "README.md" -requires-python = ">=3.10" # or >=3.12 for controlled applications; preserve ecosystem floors +requires-python = ">=3.11" # or >=3.12 for controlled applications; preserve ecosystem floors authors = [ {name = "Your Name", email = "you@example.com"} ] @@ -78,7 +78,7 @@ This tells setuptools to look for packages under the `src/` directory. ```toml [tool.ruff] -target-version = "py310" # Align with requires-python +target-version = "py311" # Align with requires-python line-length = 88 [tool.ruff.lint] @@ -94,7 +94,7 @@ quote-style = "double" ```toml [tool.mypy] -python_version = "3.10" # Target the minimum version in requires-python +python_version = "3.11" # Target the minimum version in requires-python warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true @@ -134,23 +134,30 @@ When authoring cross-platform Python packages: - If you control the deployment environment (e.g., internal tool, web service), consider `requires-python = ">=3.12"` to use the latest features. -- If deploying to older LTS systems (e.g., Ubuntu 22.04 with Python 3.10), use `>=3.10`. +- If deployment must use a distribution's system Python, set the floor to the oldest supported image you actually + deploy. For example, Ubuntu 26.04 LTS currently defaults to Python 3.14; older supported images may require a lower + floor. ### For Libraries -- Aim for broad compatibility: `>=3.10` is the current default when no ecosystem constraint says otherwise. +- Aim for broad compatibility without starting on a near-EOL interpreter: `>=3.11` is the current default when no + ecosystem constraint says otherwise. +- For new projects, do not select Python 3.10 by default: it is in source-only security maintenance and reaches + end-of-life in October 2026. Use `>=3.11` or a newer floor that fits the deployment environment. +- For existing projects that still declare 3.10, preserve the contract unless a compatibility-breaking migration is in + scope. Document the constraint and plan a tested migration before changing `requires-python` or the CI matrix. - For new projects, do not select Python 3.8: it reached end-of-life in October 2024 and no longer receives security - fixes. Use `>=3.10` or a newer floor that fits the deployment environment. + fixes. Use `>=3.11` or a newer floor that fits the deployment environment. - For existing projects that still declare 3.8, preserve the contract unless a compatibility-breaking migration is in scope. Document the legacy constraint, warn that 3.8 is unsupported upstream, and plan a tested migration before changing `requires-python` or the CI matrix. -- Review this floor as Python 3.10 approaches end-of-life in October 2026. ### Dependency Floor - When choosing a dependency version, check its `requires-python` to ensure it doesn't declare a higher floor than your project. -- Use tools like `pip-index-versions` or `pipdeptree` to audit dependency compatibility. +- Use `python -m pip index versions ` to inspect available releases and `uv tree` or `pipdeptree` to audit the + resolved dependency graph. ## Notes