Skip to content

Release v0.3.0 - #19

Merged
ortizeg merged 22 commits into
mainfrom
release/0.3.0
Jul 24, 2026
Merged

Release v0.3.0#19
ortizeg merged 22 commits into
mainfrom
release/0.3.0

Conversation

@ortizeg

@ortizeg ortizeg commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Release branch for v0.3.0, merging develop into main.

⚠️ Two things to know before merging

1. A release blocker was found and fixed here. I installed the built wheel into a clean venv and ran the CLI outside any checkout — what a user actually does — and got:

$ whet list
No skills found.

The wheel packaged only src/whet: 36 files, zero SKILL.md. The skills were listed for the sdist but never the wheel, and the bundled dirs resolved via Path(__file__).parents[3], which is the repo root in a checkout but points outside site-packages once installed. The currently published 0.2.1 almost certainly has this same defect. Fixed here; the wheel now carries 430 files (32 skills, 155 references, 125 template files).

2. Version had drifted. PyPI carries 0.2.1 while both main and develop declared 0.1.0, and the repo has no git tags — 0.2.1 was published outside this tag-triggered workflow. Releasing from 0.1.0 would have built a package older than what is live.

Version: 0.3.0

Breaking, so a minor bump under pre-1.0 semver:

  • the agents/ concept and --agents-only / --skills-only flags are removed
  • skills dvc and cv-model-selection deleted; pydantic-strict renamed to pydantic
  • whet init now installs skills instead of printing a hint

What's in the release

Thirteen merged PRs (#6#18) auditing all three pillars against Anthropic's Agent Skills guidance:

  • Skills — always-resident context down 73% (16.5k → 4,523 lines); 29 of 32 split into a thin index plus references/; every description rewritten as a trigger-first statement.
  • Agents — removed. Four of six had no frontmatter and were untriggerable; all six installed as skills; agent.toml was never parsed. GSD owns agency, whet owns knowledge.
  • Archetypes — all six now generate a project that runs, gated by CI that renders each one and runs the toolchain against the output.
  • Enforcement — the authoring rules are now CI-enforced rather than upheld by discipline.

Full detail in CHANGELOG.md, added in this PR.

Verification

  • uv run pytest tests/541 passed (5 new packaging guards)
  • ruff, format, mypy --strict, mkdocs build --strict → clean
  • uv buildwhet-0.3.0 wheel + sdist
  • Installed-wheel smoke test: clean venv, outside the repo — whet list shows 32 skills, whet doctor passes, and whet init cv-inference-service scaffolds a project with 8 skills and 43 reference files installed

After merge

Tagging v0.3.0 triggers release.yml, which publishes to PyPI and creates a GitHub Release. That step is irreversible — a PyPI version can never be reused — so I'll confirm before pushing the tag.

🤖 Generated with Claude Code

ortizeg and others added 21 commits February 9, 2026 17:19
Complete implementation of the whet CLI tool — a multi-platform installer
for expert AI coding agent skills. Encompasses all 6 development phases:

Phase 1 - Foundation: YAML frontmatter for SKILL.md files, skill.toml
  metadata, pixi→uv migration, dynamic test discovery
Phase 2 - CLI + Adapters: Typer CLI (add/remove/list/search/info/install),
  Claude Code, Google Antigravity, Cursor, GitHub Copilot adapters
Phase 3 - Gap Filling Wave 1: FastAPI, AWS SageMaker, Hugging Face skills,
  DevOps/Infra agent, Data Engineer agent
Phase 4 - Settings Engine + Doctor: settings generate/apply/diff with merge
  support, 10 health checks in doctor command
Phase 5 - Scaffolding: whet init with string.Template rendering,
  cv-inference-service and pytorch-training-project archetype templates
Phase 6 - Distribution + Polish: whet update command, Gradio and Kubernetes
  skills, release workflow, repo rename to whet

Content inventory:
- 30 skills (CV/ML, infrastructure, experiment tracking, code quality)
- 6 agents (Expert Coder, ML Engineer, DevOps/Infra, Data Engineer,
  Code Review, Test Engineer)
- 6 archetypes (pytorch-training, cv-inference, data-pipeline,
  library-package, model-zoo, research-notebook)
- 203 tests passing, ruff clean, mypy strict clean

Also includes:
- Rename all references from ai-cv-claude-skills to whet
- CI workflows updated for main/develop branching model
- Tag-triggered release workflow (PyPI trusted publishing + GitHub Releases)
- justfile task runner replacing pixi tasks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add proper type annotations to _install_to and _get_adapter in
  install.py (fixes no-untyped-call errors under --strict)
- Add mypy overrides for typer (no stubs), yaml, and tomli modules
- Disable untyped-decorator warnings for whet.cli.* (Typer decorators)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- `whet install` now installs both skills (30) and agents (6) by default
- Add --skills-only and --agents-only flags for granular control
- Add --with-settings / -s flag to apply settings template with merge
- Add discover_agents() to registry loader (reuses Skill model)
- Settings merge uses existing engine (preserves user customizations)

Closes #4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- load_existing() now extracts only permissions.allow, ignoring extra
  keys like defaultMode, hooks, statusLine that exist in real settings
- write_settings() preserves all existing keys in the file, only
  updating permissions.allow (non-destructive merge)
- Fixes crash when merging into ~/.claude/settings.json with hooks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Summary

- `whet target <platform>` and `whet config target <value>` now persist
the selection to `~/.config/whet/config.json`
- All 8 CLI call sites switched from `WhetConfig()` (hardcoded Claude
default) to `WhetConfig.load()` (reads from disk, falls back to
defaults)
- `whet target` (no args) now shows the current persisted target
alongside auto-detection

## Test plan

- [x] 203 tests pass (`uv run pytest tests/ -v`)
- [x] mypy strict passes (`uv run mypy src/whet/ tests/ --strict`)
- [x] ruff clean (`uv run ruff check . && uv run ruff format --check .`)
- [x] Manual: `whet target antigravity` → `whet config` shows `target:
antigravity`
- [x] Manual: `whet target claude` → `whet install --global` uses
`~/.claude/skills`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…y trigger descriptions (#6)

P0/P1 cleanup from the skill-library review. **32 skills** total.

## What changed

### 1. Trim bloat (progressive disclosure)
All **19 SKILL.md bodies over Anthropic's 500-line guideline** are now
under it — restated "what is X" prose and near-duplicate code blocks
removed, every distinct pattern/gotcha/anti-pattern kept.

| Skill | Before | After |
|---|---|---|
| kubernetes | 776 | 480 |
| onnx | 720 | 499 |
| abstraction-patterns | 687 | 464 |
| matplotlib | 675 | 452 |
| github-actions | 632 | 412 |
| pypi | 616 | 335 |
| …13 more | — | all <500 |

Avg SKILL.md 551 → ~440 lines; ~2.2k net lines removed. (Next step, not
in this PR: split survivors into thin index + `references/`.)

### 2. New skills (real coverage gaps)
- **model-evaluation** — detection mAP/IoU, confusion matrices,
per-class + per-size breakdowns, threshold selection, per-slice failure
analysis, eval-as-CI gate. Built on **`supervision`** (`import
supervision as sv`), the detection-native library already used alongside
RF-DETR/YOLOX, with `torchmetrics` retained for
classification/calibration. Feeds GSD's `eval-planner`; previously only
generic `testing` existed.
- **pydantic-ai** — typed LLM/VLM structured outputs; VLM-in-the-loop
auto-labeling (Gemini/Moondream → validated detections).

The supervision usage was verified against **0.29.1** and encodes the
real traps: import from `supervision.metrics` (the top-level
`sv.MeanAveragePrecision` is deprecated for 0.31.0 and inconsistent with
pycocotools); `MetricTarget.MASKS` is silently ignored by mAP in
released versions (it scores boxes — use pycocotools for mask mAP);
don't pre-filter confidence before mAP; `-1` is an "absent" sentinel,
not a score.

### 3. `pydantic-strict` → `pydantic`
Renamed for a simpler name and broader scope (strict remains the default
stance). Updates the dir, docs page, nav, archetype refs, and all
`skill.toml` cross-refs — no dangling references.

### 4. Pushy "use-when" descriptions
Every skill's `description:` rewritten as a third-person, trigger-first
"Use this skill when…" with keyword coverage and explicit disambiguation
between adjacent skills (code-quality vs pre-commit vs vscode; docker-cv
vs kubernetes vs gcp; pydantic vs pydantic-ai; mlflow vs wandb vs
tensorboard).

### 5. Env manager: pixi stays canonical
Kept **pixi** as the ML env manager (handles CUDA/system deps beyond
Python). Incidental run-commands are tool-agnostic (`pytest`, not `pixi
run pytest`); dependency installs use `pixi add`.

### 6. No vertex-ai skill — folded into `gcp` instead
A separate `vertex-ai` skill was **duplicating** the `gcp` skill, which
already covers Vertex AI training jobs (custom + custom-container, GPU
selection reference, prebuilt containers) alongside GCS and Artifact
Registry. Instead, `gcp` gains the one genuinely non-obvious bit:

> **Retrieving Artifacts After Training** — the training VM is
ephemeral, so write to `AIP_MODEL_DIR` and explicitly sync artifacts
down when the job ends.

## Verification
- `uv run pytest tests/` → **213 passed**
- `uv run ruff check .` → clean
- `uv run ruff format --check .` → clean

## Notes for review
- The `pydantic` rename touches archetype `.toml` skill lists and
cross-refs — worth a glance that nothing dangles.
- Worth sanity-checking the trimmed skills you use most
(pytorch-lightning, onnx, fastapi) for anything cut that you wanted
kept.
- `model-evaluation`'s supervision snippets assume
`sv.DetectionDataset.from_coco`; swap to `from_yolo` if that matches
your loader.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Follows [#6](#6) (merged). Result:
**32 skills, 0 agents.**

## Why remove the agents

They were never Claude Code subagents. Four independent pieces of
evidence:

1. **4 of 6 had no YAML frontmatter** — `expert-coder`, `ml-engineer`,
`code-review`, `test-engineer` began directly with an H1. They installed
with the heading as their entire description (`expert-coder: Expert
Coder Agent`) and were effectively untriggerable. The two that worked
were the two that happened to have frontmatter.
2. **All 6 installed to `.claude/skills/`, not `.claude/agents/`.**
`discover_agents()` returned `Skill` objects; there was no `Agent` model
anywhere in `src/`.
3. **`agent.toml` was never parsed** — only a doctor check and a test
asserted the file existed. The `advisory`/`blocking` field was read
exactly once, by a test.
4. **Both "blocking" `action.yml` files were broken** — they ran `pixi
run` against a repo with no `pixi.toml` (whet itself uses uv), and no
workflow referenced them.

Content-wise ~77% of their 3,047 lines duplicated existing skills.
Because they were written independently they *diverged*: `test-engineer`
timed GPU inference without `cuda.synchronize()` (meaningless) while
`skills/testing` does it correctly; `expert-coder` recommended
`logging.getLogger` against the repo's own Loguru mandate.

GSD already ships 33 correctly-formatted subagents. **GSD owns agency;
whet owns domain knowledge — and knowledge is a skill.**

## Promoted to a skill

- **`skills/data-pipelines`** (481 lines) from `data-engineer` —
storage-format decision matrix, **group-aware leakage-preventing
splitting** (frames from one match must not straddle splits), schema
evolution/migration, Great Expectations suites.

## Also removed (per review)

- **`dvc`** — not part of the workflow. Removed the skill, its docs
page, nav entry, archetype/`skill.toml` references, and rewrote prose
that pointed at it. Where DVC appeared as a *generic* data-versioning
concept, the guidance is now tool-neutral (object storage plus a
versioned/content-hashed manifest) rather than naming an unused tool.
- **`cv-model-selection`** — not wanted.

## Salvaged before deletion

| From | To |
|---|---|
| devops-infra containerization tree | `docker-cv` |
| devops-infra deployment tree ("is K8s right?") | `kubernetes` |
| devops-infra CI/CD tree | `github-actions` |
| test-engineer coverage thresholds + naming convention | `testing` |

## Removed plumbing

`agents/`, `docs/agents/`, `discover_agents()`,
`--agents-only`/`--skills-only`, `agents_dir` config, the two doctor
agent checks (remaining checks renumbered 1–8), `tests/test_agents.py`,
and the agents nav/how-to sections.

## Verification
- `uv run pytest tests/` → **192 passed**
- `uv run ruff check .` / `ruff format --check .` → clean
- `uv run mypy src/whet/ tests/ --strict` → clean
- `uv run mkdocs build --strict` → clean (caught and fixed dangling
agent links)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
)

**Stacked on [#7](#7 — base is
`p1-agents-to-skills`, so review #7 first.

## 1. Progressive disclosure

Split the 21 skills over 450 lines into a thin `SKILL.md` index plus
topic-organized `references/` loaded on demand.

A SKILL.md loads fully into context when triggered and **stays there for
the session**, so every line is a recurring token cost. Anthropic's
guidance is that SKILL.md should read like a table of contents.

**Always-resident lines: 14,930 → 7,122** across 32 skills, with ~10k
lines relocated into **115 reference files**. Content was *relocated,
not trimmed*.

| Skill | Index before → after |
|---|---|
| onnx | 503 → 120 |
| opencv | 502 → 131 |
| kubernetes | 496 → 125 |
| pytorch-lightning | 485 → 125 |
| testing | 479 → 103 |
| wandb | 477 → 121 |
| gcp | 477 → 116 |
| aws-sagemaker | 455 → 132 |
| …13 more | all → 95–143 |

Each index keeps the 80%-case patterns, conventions, and anti-patterns
inline, then ends with a `## Deep dives` list giving every reference a
*"read this when…"* trigger. Rules enforced: **one level deep** (no
reference links to another reference), TOC on any reference over 100
lines, descriptive filenames, frontmatter byte-for-byte unchanged.

## 2. Installer support (required — otherwise the links 404)

Without this, the deep-dive links would break exactly the way the
external `interface-design` skill's currently do:

- `Skill.references_dir` / `reference_files()` / `read_flattened()`
- **Claude + Antigravity** adapters copy `references/` on install
- **Cursor + Copilot** store one flat file, so references are
**inlined** rather than lost

## 3. Install tiers

New `tier` field in `skill.toml`. `tier = "extra"` marks a skill as
opt-in, skipped unless `whet install --include-extras`.

Marked extra: **aws-sagemaker, kubernetes, gradio, huggingface, mlflow**
— real skills, but no evidence they're in the flagship workflow, so they
no longer dilute the default trigger surface.

## 4. Documentation

CLAUDE.md now documents the description formula (trigger-first, pushy,
with disambiguation), the progressive-disclosure layout and its rules,
and the tier system. `docs/skills/index.md` gains an Install Tiers
section and marks extras.

Also documents the **`interface-design` companion**: whet ships no
general product-UI ruleset (`gradio` is ML demos only), so that external
skill is the recommended pairing — with a note that its `references/`
must be present or its deep-dive links won't resolve.

## Verification
- `uv run pytest tests/` → **192 passed**
- `uv run ruff check .` / `ruff format --check .` → clean
- `uv run mypy src/whet/ tests/ --strict` → clean
- `uv run mkdocs build --strict` → clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…' skills) (#9)

## Problem

`whet install` copies skills but never removes ones deleted upstream.
After the recent cleanup, **8 deleted skills were still installed** and
still being loaded by Claude Code:

```
code-review  data-engineer  devops-infra  dvc
expert-coder  ml-engineer  pydantic-strict  test-engineer
```

So the broken "agents" (the ones with no frontmatter), the removed
`dvc`, and a stale `pydantic-strict` sitting next to the new `pydantic`
were all still live. The repo was clean; the installed copies were not.

## Why this isn't just `rm -rf`

A skills directory is **shared**. `~/.claude/skills/` here holds 104
skills — most of them GSD's, plus `interface-design` and hand-written
ones. A prune that removed "anything not in whet's `skills/`" would
delete all of them.

So prune is **manifest-scoped**:

- Each install writes `.whet-manifest.json` into the target directory,
recording which skills whet owns there.
- Prune only ever removes names from that manifest → skills owned by
other tools are **invisible** to it.
- Orphans are computed against the **unfiltered** source set, so a skill
merely skipped by `--category` or by the `extra` tier is never mistaken
for a deleted one.
- Without `--prune`, orphans are **reported, not touched**.
- A corrupt or missing manifest yields an empty owned-set, which **fails
safe** — prune is authorized to remove nothing.

## Usage

```bash
whet install --prune
```

Installs predating the manifest are invisible to prune by design (whet
has no record it owns them); clear those once with `whet remove <name>`.

## Tests

10 new tests in `tests/test_manifest.py`, including the two that matter:

- `test_find_orphans_ignores_skills_whet_did_not_install` — the
GSD/interface-design protection
- `test_find_orphans_ignores_filtered_but_existing_skills` — an `extra`
skill skipped this run is not an orphan
- plus corrupt-manifest and wrong-shape tolerance

**202 passed**; ruff, `mypy --strict`, and `mkdocs build --strict`
clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…10)

## The headline bug

`cv-inference-service` is named for it, described as a *"FastAPI + ONNX
deployment service"*, and tagged `fastapi` — but **never required the
`fastapi` skill**. Generating that archetype gave you ONNX and Docker
with no serving-layer guidance. Flagged in the round-1 review, not yet
fixed.

## The pattern behind it

Several archetypes predate skills added during the cleanup, so they
scaffold a project that never pulls in the skill written for that exact
job:

| Archetype | Change |
|---|---|
| `cv-inference-service` | **+ `fastapi`** (required), +
`model-evaluation` (recommended) |
| `data-processing-pipeline` | **+ `data-pipelines`** (required) — the
skill built precisely for ETL, storage formats, and leakage-free
splitting |
| `pytorch-training-project` | + `model-evaluation`, + `data-pipelines`
(recommended) |
| `model-zoo` | + `model-evaluation` (recommended) — it's tagged
*"benchmarks"* |
| `research-notebook` | + `model-evaluation` (recommended) |

A training archetype with no evaluation story was the other real gap:
you could scaffold a full PyTorch project and never be pointed at mAP,
frozen test sets, or slice analysis.

## Also verified

No archetype lists an `extra` tier skill (`aws-sagemaker`, `kubernetes`,
`gradio`, `huggingface`, `mlflow`) as **required** — that would force an
opt-in skill onto every generated project.

## Verification
- `uv run pytest tests/` → **192 passed**
- `whet doctor` → all skill dependencies resolve
- `mkdocs build --strict` → clean

Scope is deliberately limited to `archetype.toml` skill lists. The
broader archetype audit (templates, staleness, whether 6 archetypes earn
their keep) is a separate PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Third pillar of the library review. Skills (#6, #8) and agents (#7) were
audited and fixed; the 6 archetypes had never been reviewed. An audit
that actually **rendered** each one and ran the toolchain against the
output found they were in worse shape than either.

## Why it went unnoticed

**Nothing ever rendered an archetype in CI.** The tests checked README
length and file existence — which is exactly how `cv-inference-service`
shipped for months without the `fastapi` skill. Every bug below was
invisible to a green build.

## What was broken

**4 of 6 archetypes generated nothing.** `whet init` fell through to a
4-file stub; `model-zoo` and `research-notebook` produced
*byte-identical* output apart from one description string.
`research-notebook` shipped no `notebooks/` directory — the one thing
its name promises.

**The 2 with templates produced projects that did not run:**

| Archetype | Failure |
|---|---|
| `pytorch-training-project` | **Zero runtime dependencies declared** —
couldn't import its own entry point. Crashed on the first training step
(`batch["image"]` against `FakeData`, which yields tuples). **Hydra was
a required skill but entirely absent**, so documented CLI overrides
silently did nothing — worse than crashing. Failed its own `ruff` gate.
|
| `cv-inference-service` | Named for ONNX, requires the `onnx` skill,
shipped **no `onnxruntime`, no inference engine, no `/predict`** — its
request/response schemas were dead code. `Dockerfile` COPY'd a `uv.lock`
that doesn't exist, so `docker build` failed. |

And the docs described a different product than the code: every README
said `pixi`, every template said `uv`; all six listed a `pixi.toml` no
template shipped.

## What this PR does

**Templates: 19 files → 125.** All six now render into a project that
passes its own gates.

- **pytorch-training-project** — deps declared; tuple unpacking; a real
`@hydra.main` driving Pydantic configs; `transforms.py` and
`configs/trainer/debug.yaml` added; 13 tests including a genuine
`fast_dev_run`. A *second* latent crash surfaced during verification and
was fixed: the `LazyLinear` head left `configure_optimizers` iterating
uninitialized params.
- **cv-inference-service** — real ONNX Runtime engine, wired `/predict`,
pixi-based Dockerfile, and the app starts cleanly with **no model
artifact present** (covered by a test, so the suite needs no weights).
- **data-processing-pipeline** — group-aware, leakage-preventing splits
(split on video/match/scene, never the row) with a disjointness
assertion.
- **library-package** — the packaging furniture the README promised:
`LICENSE`, `CHANGELOG.md`, `py.typed`, entry point.
- **model-zoo** — model-card schema + **sha256-verified** weight fetch,
with a test proving a corrupted file is rejected.
- **research-notebook** — an actual `notebooks/` dir with a valid
`.ipynb`, plus `nbstripout` so outputs stay out of git.

**The guardrails are the real point:**

- `tests/test_archetype_templates.py` renders every archetype through
whet's **real** `render_template` and asserts the generated project has
no unsubstituted placeholders, parses, passes `ruff`, **declares every
package it imports**, and has src-layout + tests. That
import-vs-dependency check catches the "zero dependencies declared"
class of bug without installing torch.
- `tests/test_archetypes.py` — archetype skills resolve to real skills,
no `extra`-tier skill sits in `required`, `required` is non-empty.
- New **`archetypes` CI job** running both suites.

**Staleness swept** — the deleted Code Review / Test Engineer agents
were still referenced in 5 archetype READMEs,
`docs/archetypes/index.md`, `master-skill`, and two guides
(`code-review.yml` → a plain `ci.yml`). Templates now use pixi
consistently with tool-agnostic run commands; all `uv` references
dropped from generated projects.

## Verification
- `uv run pytest tests/` → **250 passed**
- `uv run ruff check .` / `ruff format --check .` → clean
- `uv run mypy src/whet/ tests/ --strict` → clean
- `uv run mkdocs build --strict` → clean

## Notes for review

- Deliberately **not** in this PR: further skill-composition additions
the audit suggested (no archetype pulls in `pixi` despite every README
opening with `pixi install`; `pydantic-ai` is referenced by zero
archetypes). Kept separate to keep this diff to "make the templates
work."
- Each archetype still has up to three conflicting directory trees
documented (archetype README vs docs page vs actual `template/`). Worth
a follow-up making `template/` the single source of truth.
- Two caveats from template verification: mypy on a real 3.11
interpreter can trip over current numpy stubs (`--python-version 3.12`
is clean); and pixi warns that `[project]` is deprecated in favor of
`[workspace]` — left as-is because `skills/pixi/SKILL.md` documents
`[project]` repo-wide, so changing it should be coordinated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The two P0 items from the review's remaining-work list.

## 1. `whet init` didn't install the archetype's skills

It rendered the template, then **printed** `whet add ...` for you to run
yourself.

That matters because an archetype's value over a plain folder copy
**is** the skill set it composes — so leaving installation as homework
made the `[skills]` list advisory. It's also why the audit concluded
`whet init X` ≈ `whet add <X's skills>` + four boilerplate files.

Now:

```bash
whet init pytorch-training-project                    # + required skills
whet init pytorch-training-project --with-recommended # + recommended too
whet init pytorch-training-project --no-skills        # files only
```

Verified end to end — `whet init cv-inference-service` installs its 6
required skills into the new project's `.claude/skills/`, **including
their `references/`** (23 reference files), so the
progressive-disclosure deep-dive links resolve in the scaffolded project
rather than 404ing.

A name in `archetype.toml` that no longer resolves is reported rather
than silently skipped.

## 2. `docs/archetypes/*.md` had become the stale copy

All six pages still documented `uv run ...`. #11 corrected the archetype
READMEs and templates to pixi with tool-agnostic run commands, which
left the **published docs site** as the wrong half of the
README/docs/template split:

| Page | Was |
|---|---|
| cv-inference-service | `uv run uvicorn ...` |
| pytorch-training-project | `uv run python -m my_project.train` (×3) |
| library-package | `uv run pip install -e ".[dev]"`, `uv run test`, `uv
run python -m build` |
| research-notebook | `uv run jupyter lab`, `uv run pytest --nbmake` |
| model-zoo, data-processing-pipeline | `uv run python ...` (×5) |

Now consistent with what the templates actually ship. `grep` for `uv
run` / `code-review.yml` across `docs/archetypes/` returns nothing.

## Verification
- `uv run pytest tests/` → **255 passed** (4 new: install, references
travel, `--no-skills`, `--with-recommended`)
- `ruff check` / `ruff format --check` / `mypy --strict` / `mkdocs build
--strict` → all clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
P1 from the review. Archetype composition had drifted from what the
templates ship, so a generated project used tools nothing had taught the
agent about.

Every change below is grounded in **what each rendered template actually
contains** — I rendered all six and read their real dependencies rather
than working from a wishlist.

## The gaps

| Change | Evidence |
|---|---|
| **`pixi` → required on all 6** | Every template ships a `pixi.toml`
and every generated README opens with `pixi install` — yet **0 of 6**
archetypes composed the skill explaining it. |
| **`code-quality` → required on the 5 lacking it** | All six configure
ruff + mypy strict in their `pyproject.toml`. |
| **`testing` → required on research-notebook** | It ships `tests/`. |
| **`pydantic-ai` → recommended on data-processing-pipeline** |
VLM-assisted labeling is a natural extension of a dataset pipeline, and
it was referenced by **zero** archetypes. |
| `abstraction-patterns`, `library-review` → recommended on
library-package | Both are precisely the subject of publishing a
library. |
| `onnx`, `gcp` → recommended on pytorch-training-project | Its README
promises export; Vertex is the training target. |
| `tensorrt` → recommended on model-zoo | Listed as an extension point
in its README. |

## Two corrections the audit missed

Found by inspecting rendered dependencies rather than the READMEs:

- **`model-zoo`** *required* `pytorch-lightning` and `onnx`, but its
template deliberately keeps torch **optional** (real deps: `pydantic`,
`pyyaml`, `loguru`, `httpx`). Both moved to recommended, so `required`
means "needed to run."
- **`research-notebook`** *required* `pytorch-lightning` while its
template has **no torch at all** (real deps: `numpy`, `matplotlib`,
`pydantic`, `loguru`). Moved to recommended.

## What stops it drifting again

A new test ties composition to the template: if a template ships
`pixi.toml` or `tests/`, or configures ruff/mypy, the archetype **must**
require the matching skill. Composition can no longer silently diverge
from what the generated project uses.

## Deliberately not added

The audit also suggested `opencv` on cv-inference-service and
`matplotlib` on data-processing-pipeline. Both templates use neither
(`cv-inference-service` uses pillow; the data pipeline ships no
plotting), so adding them would be noise. `kubernetes` was skipped for
the same reason — the k8s claims were trimmed from that README in #11.

## Verification
- `uv run pytest tests/` → **257 passed** (1 new composition test)
- `whet doctor` → all skill dependencies resolve
- ruff / format / `mypy --strict` / `mkdocs build --strict` → clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Current pixi deprecates the `project` field. Parsing any of our
generated manifests emitted:

```
WARN The `project` field is deprecated. Use `workspace` instead.
  ╭─[pixi.toml:1:1]
1 │ ╭─▶ [project]
```

Two independent template agents hit this while verifying their work in
#11, which means it was reaching the user of **every scaffolded
project**.

## Changed

- `skills/pixi/SKILL.md` + both reference files
(`manifest-reference.md`, `project-templates.md`)
- All **6** archetype template `pixi.toml` manifests
- `[tool.pixi.project]` → `[tool.pixi.workspace]` for the
pyproject-based variant

## Deliberately surgical

`[project]` also means the **PEP 621 table** in `pyproject.toml`, which
is entirely unrelated and must not be renamed. Left untouched:

- `skills/pypi/SKILL.md` and `skills/master-skill/SKILL.md` — PEP 621
packaging metadata
- The `# pyproject.toml` example *inside* the pixi manifest reference —
same file, different meaning

I checked each occurrence's surrounding context rather than doing a
blanket find-and-replace, and asserted the expected line content before
rewriting it.

## Verification

Rendered all six archetypes and ran `pixi task list` in each:

| | deprecation warnings |
|---|---|
| before | 6 / 6 |
| after | **0 / 6** |

Plus `uv run pytest tests/` → **251 passed**; ruff and `mkdocs build
--strict` clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Final P2 item. These nine were already **under** the 500-line ceiling,
so no rule was broken — but a `SKILL.md` stays resident for the whole
session once triggered, so those lines cost on every turn that composes
them.

## Result

**Library-wide always-resident lines: 7,122 → 4,523** (16,500 before the
review began — a 73% cut overall). Reference files: 115 → 155. Content
was **relocated, not trimmed**.

| Skill | Before → After | | Skill | Before → After |
|---|---|---|---|---|
| library-review | 450 → **116** | | github-actions | 425 → **120** |
| loguru | 442 → **152** | | master-skill | 418 → **145** |
| code-quality | 438 → **157** | | pre-commit | 379 → **112** |
| github-repo-setup | 434 → **120** | | docker-cv | 362 → **143** |
| vscode | 430 → **114** | | | |

## What stayed inline

Skills that own a **canonical artifact** keep it in the index, because
other skills and all six archetype templates defer to them:

- **`code-quality`** — the ruff/mypy `pyproject` blocks (`pre-commit`
and `github-actions` point here for the standards themselves)
- **`docker-cv`** — the multi-stage pixi Dockerfile that every archetype
template follows
- **`github-actions`** — the standard pixi setup block, since later
examples abbreviate it as `# ...pixi setup...`
- **`loguru`** — `setup_logging()` and the mandatory "never `print()` /
never `logging.getLogger`" rule
- **`master-skill`** — the initialization steps and archetype list

## Left unsplit deliberately

`pypi` (338), `model-evaluation` (272), and `pydantic-ai` (206) are
already index-sized. Splitting them would add indirection without
reducing anything meaningful.

## Verification

- **Frontmatter byte-identical** for all nine (diffed against `git show
HEAD:`) — descriptions are the trigger surface, so they must not drift.
- Every reference over 100 lines opens with a table of contents.
- **One level deep holds** — `grep` for `references/` inside reference
files returns nothing, so no reference links to another.
- `uv run pytest tests/` → **251 passed**; ruff, format, `mypy
--strict`, `mkdocs build --strict` all clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The last item on the review's tail.

## The problem

Each archetype's directory tree was hand-maintained in **two** places —
the archetype README and the published docs page — so both drifted
independently of the actual template.

#11 and #15 corrected the archetype READMEs, which left **the docs pages
as the only stale copy**. They documented **78 files no template
generates**:

| Page | Ghosts |
|---|---|
| data-processing-pipeline | 21 (`base.py`, `dataset.py`, `download.py`,
`configs/`, …) |
| cv-inference-service | 13 (`serve.py`, `predictor.py`,
`preprocessing.py`, `schemas/`, `docker-compose.yml`, …) |
| model-zoo | 13 (`MODEL_CARD.md`, `benchmarks/`, `model_configs.py`, …)
|
| library-package | 11 (`helpers.py`, `module.py`, `api/`, `guides/`, …)
|
| pytorch-training-project | 10 (`datamodule.py`, `detector.py`,
`metrics.py`, `coco.yaml`, …) |
| research-notebook | 10 (three example notebooks, `utils.py`,
`plots.py`, …) |

## The fix

**Regenerated every `docs/archetypes/*.md` tree directly from the
rendered template**, so each now describes exactly what `whet init`
produces. **78 → 0** documented-but-absent files.

Also normalized placeholder syntax across the archetype READMEs. Three
different spellings were in use:

- `${var}` — correct, what `string.Template` substitutes
- `{{var}}` — **inert**; the engine never substitutes it, so
`cv-inference-service`'s README was actively misleading
- `<var>` — invented

All now use `${var}`.

## Guards

Three tests so the trees can't silently drift again:

- `test_readme_tree_matches_template`
- `test_docs_page_tree_matches_template`
- `test_readme_uses_real_placeholder_syntax`

I verified the tree guard actually bites by planting a ghost entry — it
failed with `model-zoo README documents files its template does not
ship: ['ghost_file.py']` — then restored the file.

## Verification
- `uv run pytest tests/` → **279 passed** (3 new guards)
- ruff / format / `mypy --strict` / `mkdocs build --strict` → clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Correcting an overclaim

I previously said body length was defended by CI. **It was not.** The
only length assertion in the suite was a `>500` **character** *floor* in
`test_skills_completeness.py`. The 500-**line** ceiling, the
progressive-disclosure structure, and description quality had **no guard
at all**.

They were passing because they'd just been fixed by hand — which is
precisely the state that rots. A scorecard that holds only because
someone recently tidied up isn't a scorecard, it's a snapshot.

## What this adds

`tests/test_skill_hygiene.py` — one check per scorecard rule, each tied
to a failure this library actually had:

| Guard | The failure it prevents |
|---|---|
| Body ≤ 500 lines | 19 of 30 skills once averaged **551** lines |
| Cited `references/...` exist | The deep-dive-404 failure mode |
| No orphaned reference files | Unreachable files that only cost tokens
|
| References one level deep | Nested refs cause partial reads |
| TOC on references >100 lines | Partial read must still reveal scope |
| Descriptions trigger-first | They were topic summaries ("Covers X, Y,
Z") — these under-trigger |
| Descriptions third person | First person hurts discovery |
| Descriptions within listing budget | Overflow silently truncates
matching keywords |
| Split skills link their deep dives | An index that doesn't route is
just a shorter skill |

## One subtlety worth reviewing

The third-person check **strips quoted user utterances first**. A
description listing what a user might *say* — `"set up my editor"` — is
doing keyword coverage, not speaking in first person, and that phrasing
is actively encouraged. Without the strip, `vscode` failed on good
practice.

## Every guard was proven to bite

Rather than trusting green, I planted a regression for each and observed
the failure, then reverted:

```
loguru/SKILL.md cites reference files that do not exist: ['references/does-not-exist.md']
loguru description is not trigger-first ... Got: structured logging for cv/ml. covers sinks, json, levels...
pypi/SKILL.md is 539 lines (limit 500). Move detail into references/ ...
```

## Verification
- `uv run pytest tests/` → **536 passed**
- ruff / format / `mypy --strict` → clean
- Wired into the `archetypes` CI job as "Enforce skill-authoring rules"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The last open item from the review's scorecard.

## Why these two

Both sit closest to the "restates what Claude already knows" line — the
rule that a skill should encode knowledge the model *lacks*, since every
loaded line is a recurring cost.

- **`vscode`** — `settings.json`, `launch.json`, extension lists.
Standard editor configuration.
- **`github-repo-setup`** — `gh repo create --public --license MIT`,
branch-protection calls. The tool's documented interface.

Neither is CV/ML domain knowledge, which is the value whet exists to
supply. Contrast with `model-evaluation` knowing that
`MetricTarget.MASKS` is silently ignored by supervision's mAP, or
`data-pipelines` knowing to split on video rather than frame — knowledge
that prevents a specific expensive mistake.

## Why demote rather than delete

They *do* carry non-obvious content worth keeping:

- remote-SSH GPU setup and devcontainer `--gpus` / `--shm-size`
- the branch-protection JSON payload, and the rule that required status
checks must match job names exactly

Both are already thin indexes (**114** and **120** lines). `extra` keeps
them one flag away — `whet install --include-extras` — while the default
trigger surface stays focused.

**Default install is now 25 core skills; 7 opt-in.**

## Verified safe to demote

- **No archetype** lists either as `required` or `recommended` — so this
can't trip the "archetype must not force an opt-in skill" guard.
- The only cross-references are **disambiguation clauses in
descriptions** (`"editor integration in vscode"`, `"see
github-repo-setup"`), not dependencies. Those read correctly whether or
not the skill is installed — they mark a scope boundary either way.

## Verification
- `uv run pytest tests/` → **279 passed**
- `whet doctor` → all skill dependencies resolve
- ruff + `mkdocs build --strict` → clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Bump version to 0.3.0 and add a CHANGELOG.

Version note: PyPI already carries 0.2.1 while both main and develop declared
0.1.0, and the repository has no git tags — 0.2.1 was published outside the
tag-triggered workflow. Releasing from 0.1.0 would build a package older than
what is already live, so this bump goes past it.

0.3.0 rather than 0.2.2 because the release is breaking: the agents concept and
the --agents-only/--skills-only flags are removed, two skills are deleted, one
is renamed, and whet init changes behaviour. Below 1.0.0, breaking changes ship
in a minor bump.

536 tests pass; ruff, format, mypy --strict, and mkdocs --strict clean.
uv build produces whet-0.3.0 wheel and sdist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Caught by installing the built wheel into a clean venv and running the CLI
outside any checkout, which is what a user actually does.

`whet list` printed "No skills found" and `whet doctor` reported a missing
skills directory. Two causes:

- [tool.hatch.build.targets.wheel] packaged only src/whet. The bundled content
  was listed for the sdist but never the wheel, so the published artifact was
  the CLI with none of the product: 36 files, zero SKILL.md.
- skills_dir, archetypes_dir, and the settings templates resolved via
  Path(__file__).parents[3], which is the repository root in a checkout but
  points outside site-packages once installed.

Fixes:
- Force-include skills/, archetypes/, and settings/ into the wheel under
  whet/_data/. The wheel goes from 36 files to 430 — 32 SKILL.md, 155
  reference files, 125 archetype template files.
- Add whet.core.paths.bundled_dir(), which prefers the packaged copy and falls
  back to the repo layout so a source checkout still works with no build step.
- Drop the stale agents/ entry from the sdist include list.

Verified end to end: installing the wheel in a clean venv and running outside
the repo now lists 32 skills, passes doctor, and `whet init cv-inference-service`
scaffolds a project with 8 skills and 43 reference files installed.

Adds tests/test_packaging.py so this cannot ship broken again, including a
check that packaging never references a directory that no longer exists.

541 tests pass; ruff, format, mypy --strict, mkdocs --strict clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main carried a squash commit (7ee1eaa, 'Release v0.1.0') that was never merged
back into develop, so the same files were added independently on both sides and
git saw ~100 add/add conflicts.

Resolved with -s ours: main's content is a strict subset of this branch, and
every file present only on main is a reviewed deletion — the agents/ tree and
its docs (removed in #7), skills/dvc, and skills/pydantic-strict (renamed to
pydantic). Nothing unique is discarded.

Recording main as a parent reconciles the histories so future develop -> main
merges are clean.
Branch protection on main requires a status check named 'docs', but nothing
could ever produce it:

- docs.yml only triggered on push to main, so it never ran on a pull request
- its jobs were named build/deploy, so even when it did run it reported
  'build' and 'deploy' — never 'docs'

The result was circular: merging to main required a check that only a push to
main could produce. PR #19 sat with three green checks and a fourth that was
not pending but simply absent.

- Rename the build job to `docs` so it satisfies the required context.
- Add a pull_request trigger for main and develop.
- Gate artifact upload and the deploy job to pushes on main, so PRs build the
  docs without touching GitHub Pages.
- Build with --strict on PRs. This is the check that caught the dangling
  agent-page links rather than letting them ship.
- Key concurrency by ref so parallel PR doc builds do not queue behind one
  another.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ortizeg
ortizeg merged commit fb823b1 into main Jul 24, 2026
5 checks passed
@ortizeg
ortizeg deleted the release/0.3.0 branch July 24, 2026 15:41
ortizeg added a commit that referenced this pull request Jul 24, 2026
Companion to **[#19](#19. Merge
this **after** #19 lands.

## Why this is required, not housekeeping

`develop` is currently missing four things that only exist on the
release branch — and one of them is a live bug:

| Missing from develop | Impact |
|---|---|
| **The packaging fix** (`force-include`, `core/paths.py`) | **A wheel
built from develop today ships zero skills.** `uv tool install whet`
would produce a CLI that reports "No skills found". |
| `tests/test_packaging.py` | Nothing on develop would *catch* that
regression. |
| Version bump to 0.3.0 | develop still declares `0.1.0`, behind the
published 0.2.1. |
| `CHANGELOG.md` | Would be lost on the next release. |

## It also fixes the divergence permanently

This is precisely the mistake that caused #19's ~100 add/add conflicts:
`main` received a squash commit (`7ee1eaa`, "Release v0.1.0") that was
**never merged back into develop**, so both branches recreated the same
files independently from their shared ancestor.

Skipping this back-merge would repeat that exactly — `main` would hold
the release commits, `develop` would not, and the next release would
conflict all over again. Merging here brings `7ee1eaa` into develop's
ancestry so all three branches finally share history.

## Verification

Merged cleanly — **no conflicts**.

- `uv run pytest tests/` → **541 passed**
- ruff, format, `mypy --strict`, `mkdocs build --strict` → clean
- Deliberate deletions confirmed still absent (no resurrection of
`agents/`, `skills/dvc`, `skills/pydantic-strict`)
- **Wheel built from this branch: 430 files, 32 skills** — the packaging
fix is genuinely carried across

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant