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
5 changes: 4 additions & 1 deletion .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ jobs:
- name: Run environment check
run: python scripts/check-env.py

# `make shell` only syntax-checks the template's single-use generator
# scripts when they are present, so this step becomes a no-op once they
# have been removed from a project generated from the template.
- name: Check shell syntax
run: bash -n scripts/create-project scripts/rename-project
run: make shell

- name: Run Ruff lint
run: ruff check .
Expand Down
83 changes: 83 additions & 0 deletions .github/workflows/template-chassis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Template chassis

# Self-tests for the python-cli-template chassis itself: generating a project
# and building the template's own distributions. They exercise the single-use
# generator scripts and assert the template's own version, so they are
# meaningless in a project created from the template. `scripts/create-project`
# deletes this workflow from the target, and the manual setup path removes it
# by hand — see README.md. If you are looking at this file in a generated
# project, delete it.

on:
push:
branches: [main]
tags: ["v*"]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
template-smoke:
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Check out repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"
cache: "pip"

- name: Install uv
run: python -m pip install uv

- name: Create and verify a generated project with uv
run: |
bash scripts/create-project "$RUNNER_TEMP/uv-tool" uv-tool --no-kit
test -f "$RUNNER_TEMP/uv-tool/uv.lock"
"$RUNNER_TEMP/uv-tool/.venv/bin/uv-tool" --version

- name: Create and verify the pip fallback
run: |
pip_target="$RUNNER_TEMP/pip[tool]qa"
bash scripts/create-project "$pip_target" pip-tool --no-kit --pip
test ! -e "$pip_target/uv.lock"
"$pip_target/.venv/bin/pip-tool" --version

package-smoke:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Check out repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"
cache: "pip"

- name: Install package tools
run: python -m pip install -e ".[dev]"

- name: Build and verify distributions
run: |
python -m build
wheel="$(find dist -name '*.whl' -print -quit)"
sdist="$(find dist -name '*.tar.gz' -print -quit)"
python -m venv "$RUNNER_TEMP/wheel-venv"
"$RUNNER_TEMP/wheel-venv/bin/pip" install --no-deps "$wheel"
test "$("$RUNNER_TEMP/wheel-venv/bin/skeleton-cli" --version)" = "skeleton-cli 0.2.0"
mkdir "$RUNNER_TEMP/sdist"
tar -xzf "$sdist" -C "$RUNNER_TEMP/sdist"
source_dir="$(find "$RUNNER_TEMP/sdist" -mindepth 1 -maxdepth 1 -type d -print -quit)"
(cd "$source_dir" && python -m pytest)
60 changes: 0 additions & 60 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,63 +37,3 @@ jobs:

- name: Run tests
run: pytest

template-smoke:
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Check out repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"
cache: "pip"

- name: Install uv
run: python -m pip install uv

- name: Create and verify a generated project with uv
run: |
bash scripts/create-project "$RUNNER_TEMP/uv-tool" uv-tool --no-kit
test -f "$RUNNER_TEMP/uv-tool/uv.lock"
"$RUNNER_TEMP/uv-tool/.venv/bin/uv-tool" --version

- name: Create and verify the pip fallback
run: |
pip_target="$RUNNER_TEMP/pip[tool]qa"
bash scripts/create-project "$pip_target" pip-tool --no-kit --pip
test ! -e "$pip_target/uv.lock"
"$pip_target/.venv/bin/pip-tool" --version

package-smoke:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Check out repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"
cache: "pip"

- name: Install package tools
run: python -m pip install -e ".[dev]"

- name: Build and verify distributions
run: |
python -m build
wheel="$(find dist -name '*.whl' -print -quit)"
sdist="$(find dist -name '*.tar.gz' -print -quit)"
python -m venv "$RUNNER_TEMP/wheel-venv"
"$RUNNER_TEMP/wheel-venv/bin/pip" install --no-deps "$wheel"
test "$("$RUNNER_TEMP/wheel-venv/bin/skeleton-cli" --version)" = "skeleton-cli 0.2.0"
mkdir "$RUNNER_TEMP/sdist"
tar -xzf "$sdist" -C "$RUNNER_TEMP/sdist"
source_dir="$(find "$RUNNER_TEMP/sdist" -mindepth 1 -maxdepth 1 -type d -print -quit)"
(cd "$source_dir" && python -m pytest)
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ All notable changes to this project are documented here. The format is based on
- Root `CLAUDE.md` that imports `AGENTS.md`, so Claude Code and Codex read one
set of repository instructions from a single source.

### Fixed

- Generated projects no longer ship with red CI. The template's chassis
self-tests — generating a project and building the template's own
distributions — moved from `tests.yml` into a dedicated
`.github/workflows/template-chassis.yml`, which `create-project` deletes from
the target (and the manual setup path removes by hand). Those jobs run the
single-use generator scripts and assert the template's own version, so they
only failed once a generated project deleted the scripts. The code-quality
"Check shell syntax" step now runs `make shell`, whose `shell` target already
no-ops when the generator scripts are absent, instead of a hardcoded
`bash -n scripts/create-project scripts/rename-project` that exited 127 in a
generated project.

### Changed

- README now leads the "Using the template" section with the rename step the
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,20 @@ cannot portably use them.

```bash
bash scripts/rename-project your-tool-name
rm scripts/rename-project scripts/create-project tests/test_create_project.py
rm scripts/rename-project scripts/create-project \
tests/test_create_project.py .github/workflows/template-chassis.yml
```

`rename-project` rewrites the package (`skeleton_cli`), distribution
(`skeleton-cli`), and env-var prefix (`SKELETON_CLI`) everywhere, moves the
source directory, substitutes your `git config user.name` and `user.email`
as owner and contact in the docs and community files, and resets the project
to its own `0.1.0` with a fresh `CHANGELOG.md` and `docs/log.md`. The `rm`
drops the two generator scripts and their smoke test — template tooling that
has no role in a downstream project. (The preferred path removes these same
three files for you.)
drops the two generator scripts, their smoke test, and the `template-chassis`
workflow — template tooling that has no role in a downstream project, and
whose CI jobs run those scripts and assert the template's own version, so
they only fail once the scripts are gone. (The preferred path removes these
same four files for you.)

The rename step prunes Git internals, virtual environments, and tool caches
by directory name, so checkout paths containing spaces or square brackets do
Expand Down Expand Up @@ -236,7 +239,7 @@ you what you missed.

```
├── AGENTS.md # Codex setup, verification, and safety instructions
├── .github/workflows/ # quality, tests, coverage — Python 3.12–3.14 matrix
├── .github/workflows/ # quality, tests, coverage (3.12–3.14); template-chassis self-test, dropped on generate
├── docs/ # knowledge bundle indexes and documentation log
├── scripts/
│ ├── check-env.py # file-sync damage preflight, first step of make check
Expand Down
13 changes: 12 additions & 1 deletion docs/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Documentation log
type: log
status: current
date: 2026-07-17
date: 2026-07-18
owner: Lila Brooks
deciders: [Lila Brooks]
tags: [documentation, log]
Expand All @@ -12,6 +12,17 @@ tags: [documentation, log]

Dated changes to the docs bundle, newest first.

## 2026-07-18

- Documented that generated projects no longer inherit red CI. The README
manual path, `create-project`, and `rename-project`'s printed next steps now
remove `.github/workflows/template-chassis.yml` alongside the single-use
generator scripts, and the README describes the template-chassis self-test
workflow that generated projects drop. The template's chassis self-tests
(project generation and building the template's own distributions) moved out
of `tests.yml` into that dedicated workflow, and the code-quality shell check
now runs `make shell` so it self-heals once the generator scripts are gone.

## 2026-07-17

- Fixed `rename-project`'s excluded-directory traversal for checkout paths
Expand Down
11 changes: 7 additions & 4 deletions scripts/create-project
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# environment with uv (or the venv/pip fallback) and run `make check`, install
# the claude-okf-repo-kit operating contract when a kit clone is available,
# rerun the gate after installation, and delete the single-use template scripts
# from the target. What it cannot
# and the template-only CI workflow from the target. What it cannot
# do — the project README, CHANGELOG, goal, and playbook content — it prints as
# next steps.
#
Expand Down Expand Up @@ -146,11 +146,14 @@ else
fi

# Rename the skeleton with the target's own copy of the script, then remove
# the template-side tooling from the target: both single-use scripts and the
# smoke test that exercises them (it has nothing to test once they are gone).
# the template-side tooling from the target: both single-use scripts, the
# smoke test that exercises them (it has nothing to test once they are gone),
# and the template-chassis workflow (its jobs run those scripts and assert the
# template's own version, so they only fail in a generated project).
bash "$TARGET/scripts/rename-project" "$DIST" | sed -n '1,2p'
rm -f "$TARGET/scripts/rename-project" "$TARGET/scripts/create-project" \
"$TARGET/tests/test_create_project.py"
"$TARGET/tests/test_create_project.py" \
"$TARGET/.github/workflows/template-chassis.yml"

venv_note="skipped (--skip-venv); preferred: uv sync --all-extras && uv run make check; fallback: python3 -m venv .venv && .venv/bin/pip install -e \".[dev,anthropic,openai]\" && make check"
if [ "$SKIP_VENV" -eq 0 ]; then
Expand Down
5 changes: 3 additions & 2 deletions scripts/rename-project
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ Next steps:
and .env.example.
5. Install the operating contract on top (goal, mapping, hooks, loop):
bash /path/to/claude-okf-repo-kit/scripts/update-existing-repo .
6. Remove the single-use generator files; they have no role downstream:
6. Remove the single-use generator files and the template-only CI workflow;
they have no role downstream and its jobs fail once the scripts are gone:
rm scripts/rename-project scripts/create-project \
tests/test_create_project.py
tests/test_create_project.py .github/workflows/template-chassis.yml
EOF
31 changes: 28 additions & 3 deletions tests/test_create_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
The script is the deterministic path agents and humans run to stamp this
template into a new project, so its contract is guarded here: extraction from
the committed tree, rename, .gitignore merge, collision refusal, uv setup,
path-safe internal-directory pruning, kit-candidate reporting, and removal of
the single-use scripts. CI exercises the complete uv and pip paths against a
generated project.
path-safe internal-directory pruning, kit-candidate reporting, removal of the
single-use scripts, and removal of the template-only CI so a generated project
ships green. CI exercises the complete uv and pip paths against a generated
project.
"""

import os
Expand Down Expand Up @@ -180,6 +181,30 @@ def test_removes_single_use_scripts_from_target(tmp_path: Path) -> None:
assert result.returncode == 0, result.stderr


def test_generated_project_ci_has_no_template_only_references(tmp_path: Path) -> None:
_require_git_checkout()
target = tmp_path / "new-proj"

_create(target, "sample-tool")

workflows = target / ".github" / "workflows"
# The template-only self-test workflow is dropped; the everyday workflows stay.
assert not (workflows / "template-chassis.yml").exists()
assert {path.name for path in workflows.glob("*.yml")} == {
"tests.yml",
"code-quality.yml",
"coverage.yml",
}

# No surviving workflow may invoke the deleted generator scripts or their
# smoke test; a lingering reference is exactly what ships a generated
# project with red CI.
for path in workflows.glob("*.yml"):
text = path.read_text(encoding="utf-8")
for token in ("create-project", "rename-project", "test_create_project"):
assert token not in text, f"{path.name} still references {token}"


def test_prefers_uv_and_writes_project_lock(tmp_path: Path) -> None:
_require_git_checkout()
target = tmp_path / "new-proj"
Expand Down