v4.0.0
[4.0.0] — 2026-09-02
Breaking: /productize is now /skillify, and templates/productized-skill/ is now
templates/generated-skill/. There is no alias — the old name is gone, and an older install's
retired skill directory is warned about rather than deleted. Everything else in this release is
additive: /setup scaffolds a human-facing README, writes stack.yaml first and resumes a
half-configured repo cleanly, and every rebuild hint names the plugin, pip and vendored routes.
Changed
/productizeis now/skillify;templates/productized-skill/is nowtemplates/generated-skill/.
The policy that drives the skill has always been calledskillify_everything, so the command name
was the odd one out — and "productize" carried a product-shipping connotation the skill never had:
it turns a repeated workflow into a skill, it does not ship a product. The skill directory
(.claude/skills/skillify/), itsname:frontmatter, the skeleton it stamps from, and the
adjective in prose ("productized skill" → "generated skill") all move together. There is no
backwards-compatible alias — the old name is gone. Note thattemplates/AGENTS.md.tmplwas
already rendered into existing repos naming/productize, and/refreshdoes not re-render
AGENTS.md: an existing repo needs/setup rolere-run, or that one line edited by hand. See
docs/troubleshooting.mdfor the rename map.
Added
/setupnow scaffolds a human-facingREADME.md. Setup wroteAGENTS.md(the agent's rules
file) but nothing a person landing on the repo could read in a minute. It now also renders a short
intro (under 250 words of prose) — what this is (a ticket-driven work repo) and how work moves
through it — from the newtemplates/project-README.md.tmpl. It's tool-agnostic (only{{repo_name}}
and{{domain}}tokens) and never overwrites an existing README: if one is present (fresh or
adopt), it rendersREADME.ticketwright.mdfor the human to merge, mirroring theAGENTS.ticketwright.md
convention. Written once, then human-owned —/setup roledoes not re-render it.
Fixed
/setupwritesstack.yamlfirst and stops cleanly when the runtime refuses it. Phase 3 used
to compose.claude/config/stack.yamland the scaffold in one sweep. On a brand-new empty repo
under a headless run whose runtime refuses every write beneath.claude/(a categorical path guard
no allow rule lifts non-interactively), setup had already writtenAGENTS.md,CLAUDE.md,
README.md,.gitignore,people/*.yaml, the AI-layer index and the seeded ticket index before
the refusal landed — leaving anAGENTS.mdthat describes a stack absent from disk and no source
of truth. The order is nowstack.yaml→.claude/settings.json→ the derived scaffold (which
renders FROMstack.yaml), and a refusedstack.yamlwrite halts the whole scaffold and reports
the refusal text, the cause, and the two ways forward (approve the prompt interactively, or
hand-create the.claude/files from the printed plan); everything else is written on the next
confirm, and a re-run that findsstack.yamlwithoutAGENTS.mdresumes the scaffold instead of
offering an edit — that route is checked before the Bootstrap route (a hand-created recovery has no
people/yet), and it first writes whatever of.claude/is still missing (settings.json, the
round-1 machine pin) before rendering.scaffold.mdalso now says to allow a CLI's named read verbs (jobs list,
jobs get,current-user me,api get), never the bare CLI.- The generated
tickets/INDEX.mdandOBJECTS.mdheaders no longer point plugin users at a script
that isn't there. Both headers said "Re-runpython3 bin/build_ticket_index.py". On a plugin install
the repo has nobin/(the engines live in the plugin cache and the skills resolve them), so a fresh
plugin user following the hint got "No such file or directory". The hint now names the skill first —
/refresh index(/ticketwright:refresh indexon a plugin install) — thenticketwright indexfor pip
installs, and the script path only for vendored repos; the--prune/--checkmessages and
docs/ticket-index.md/docs/troubleshooting.mdcarry the same three routes. Selftest section 10 pins the new header on both files.
Docs
- README: headless / agent-driven setup. A new Track 1 subsection says plainly that
/setupunder
claude -pis a two-turn flow — the plan turn halts at the confirm gate by design, and the confirm
is a second turn with--continue— and that a non-interactive runtime refuses every write under
.claude/(a categorical sensitive-file guard no allow rule lifts), sostack.yaml,
settings.json,statusline.shand the two gitignored*.local.yamlrecords need an interactive
approval or a person writing them from the printed plan, while everything outside.claude/
scaffolds headlessly. It names--strict-mcp-configas the way to keep MCP out of a headless run. - README: install-block accuracy. The Track 1 install note now says that on a machine that already
knows aticketwrightmarketplace,marketplace add … --scope projectprints "already on disk —
declared in project settings" — expected, not an error. The finished-file example notes that the
owner/reposhorthand writes{"source": "github", "repo": …}where thehttps://…gitform writes
{"source": "git", "url": …}; both are valid, and setup's merge keeps whichever exists.