Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -83,25 +83,27 @@ Make the skill discoverable by your agent.
<details>
<summary><b>Hermes Agent</b></summary>

**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:
- /path/to/python-project-workflow/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.*
</details>

<details>
<summary><b>Claude Code</b></summary>

```bash
mkdir -p ~/.claude/skills
cp -r skills/python-project-workflow ~/.claude/skills/
```
</details>
Expand All @@ -110,6 +112,7 @@ cp -r skills/python-project-workflow ~/.claude/skills/
<summary><b>Codex</b></summary>

```bash
mkdir -p ~/.agents/skills
cp -r skills/python-project-workflow ~/.agents/skills/
```
</details>
Expand All @@ -118,6 +121,7 @@ cp -r skills/python-project-workflow ~/.agents/skills/
<summary><b>Gemini CLI / .agents/ path</b></summary>

```bash
mkdir -p .agents/skills
cp -r skills/python-project-workflow .agents/skills/
```
</details>
Expand All @@ -126,6 +130,7 @@ cp -r skills/python-project-workflow .agents/skills/
<summary><b>OpenCode</b></summary>

```bash
mkdir -p .opencode/skills
cp -r skills/python-project-workflow .opencode/skills/
```
</details>
Expand Down Expand Up @@ -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/
Expand Down
7 changes: 4 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.
6 changes: 4 additions & 2 deletions references/drift-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <repo>/skills/<name>/ <installed>/skills/<name>/` shows mismatch | Re-install from source or use a client-supported live source directory |

---
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 |

---

Expand Down
5 changes: 3 additions & 2 deletions references/lint-format-typing-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down
25 changes: 16 additions & 9 deletions references/pyproject-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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"}
]
Expand Down Expand Up @@ -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]
Expand All @@ -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
Expand Down Expand Up @@ -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 <package>` to inspect available releases and `uv tree` or `pipdeptree` to audit the
resolved dependency graph.

## Notes

Expand Down
19 changes: 19 additions & 0 deletions scripts/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down Expand Up @@ -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}")
Expand All @@ -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/"):
Expand All @@ -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):
Expand All @@ -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):
Expand Down
15 changes: 11 additions & 4 deletions skills/python-project-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 4 additions & 2 deletions skills/python-project-workflow/references/drift-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <repo>/skills/<name>/ <installed>/skills/<name>/` shows mismatch | Re-install from source or use a client-supported live source directory |

---
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 |

---

Expand Down
Loading