Repository navigation
TaskFlow 1.1.7
TaskFlow 1.1.7 — 2026-10-21
Release commit: c0eca3d. Pin commit: 1897ffc.
Marketplace pins: v1.1.7 → c0eca3d0c8f387f6bf226a81f686d8e964b69867 in .claude-plugin/marketplace.json and .codebuddy-plugin/marketplace.json.
Added
- A task name is validated before it becomes a path (PR #89).
hooks/task promotejoined whatever string it was given intoTaskFlowDocs/<name>/and ranmkdir -pon it, so a name that was not anYYYY-MM-DD-short-slug— a mistyped--size, a stray-rf, an empty argument — became a directory rather than an error. Measured before the rule existed:../evilcreated a fully usable task directory aboveTaskFlowDocs, and../../escapedcreated one outside the repository. Neither was recoverable from the CLI. The rule now lives in one executable,hooks/task-id, and is checked by every entry point that turns a name into a path or reads one out of one —promote,state,progress,approve,complete,archive,reopen, andunpromote— before the join, not after. Nine bad shapes are refused by all seven entry points, and three of them carry a legal date with an illegal slug so the character-class half of the rule is exercised rather than the date half alone. hooks/task unpromote <todo-id> <reason>undoes a promotion (PR #89). A promotion made under a name you meant to change had no inverse: the directory was on disk, the Todo entry saidpromoted, and the only way back was to delete files by hand.unpromoteremoves the scaffold and returns the entry toinbox. It refuses in the four states where the directory has stopped being an empty scaffold — the entry is no longerpromoted, the directory holds Git-tracked files, the Plan's## Approvalhas leftrequested, or a core document has left> Status: planning— because at that point undoing a promotion is a Git decision rather than a TaskFlow one.hooks/archivecompacts the entry it closes, and moves its prose first (PR #88). A done entry kept every field it ever had plus the free-form Notes accumulated while it was worked. Measured at01a067a: eighty-fivedoneentries held 89,554 oftodo.md's 114,226 bytes — 78% — and the file had gone from 55 KB atv1.1.1to 114 KB atv1.1.6, about 12 KB per release.archivealready owned the one moment an entry is known to be finished, and it was already editing that entry in place; it now reduces it to its heading plusID,Status,Goal,TaskandNext action. The prose is moved, not dropped: every non-blank line that is not a field line is appended to the achievedplan.mdunder## Todo Record, verbatim, because measured over 195 sentences just 2 appear in the archived documents — the Notes hold the reasoning, and the Plan records the decision. An entry with no achievedplan.mdto link is left exactly as it is, since a## Todo Recordheading at column 0 inside an entry would be a new section to every hook that splits on##. After the one-time migration of the existing entries:todo.mdis 65,329 bytes, down from 114,226.hooks/archivebackfills- ID:from an entry's heading (PR #88). Entries written before the- ID:field existed carried their ID in the heading only, wheretask getcould not reach them: nineteen of them, of which ten had no collision and are now reachable. The other nine keep no ID line, because the old numbering restarted per day of work and their ID already names a different entry. The backfill and the compaction land in one commit on purpose —merge-todokeys an ID-less entry by heading, so split across commits a merge spanning the boundary would conflict on all nineteen.bash hooks/repository-check .names stale task worktrees (PR #87). Removing a task's working tree was nobody's job:.worktrees/is ignored, so a leftover one costs nothing visible until the directory listing gets long — nine had accumulated on one machine before it was noticed. The check now lists every linked worktree whose branch is already in the base and whose tree is clean, andhooks/archivestates the teardown together with its condition. The list never setsneeds, because the check's exit code is a workflow verdict and folding housekeeping into it would makeneeds-user-inputmean two things at once.- The
Hooksworkflow runs only the jobs a change can affect (PR #91). Every pull request ran all six jobs, so a task archive paid for a three-OS smoke matrix it could not have changed. Achangesjob now classifies the changed paths throughtools/workflow-affectedandsmoke,releaseandevalsread its answer throughneeds;todo-merge-auditstays unconditional. On the archive pull request that carries this release's own task,releaseandevalswereskippedand nothing else was.concurrencyalso cancels a pull-request run that a newer push has superseded — three pushes to one pull request used to queue three macOS jobs, the first two of which could not have told anyone anything the third would not.
Changed
- Task-name samples in
hooks/smoke-testare literal rather than this repository's own directories (PR #91). The tolerance half of the name rule used the liveTaskFlowDocs/*/names as its samples, which is circular — a directory is there only because this same rule accepted it — and it made the suite's input depend on which branch was checked out. It also made the claim "no job reads a task directory" false while the classifier was asserting it: the loop read them through a relative glob, so a new or removed task directory was a smoke input. Five literal names replace it. hooks/reopenwrites- Updated:when the entry has none (PR #88). A compacted entry carries five fields andUpdatedis not one of them, so the entry a reopen targets is exactly the case that has none. Leaving it out was not a stale value but a hard break:hooks/task'sloadpairsdies on an absent field, so the documented next steptask state <task> planningfailed right afterreopenreported success. Reproduced on a five-field fixture:reopen OK, then exit 1.CONTRIBUTING.mdandskills/taskflow/SKILL.mdstate the teardown and the name rule where the phases are defined, rather than leaving both to be inferred from a command's output.
Compatibility
- No install needs to change, and no hook gains an interpreter dependency. The four host manifests keep their shape, every
hooks-*.jsonwiring file is untouched, and the new code usesgit,case, and POSIX shell builtins only. The smoke job still installs no language runtime — and the one place this release had to be corrected was exactly there: a new workflow assertion used PyYAML, which ubuntu's runner image ships and macOS's does not, so it passed on two hosts and never executed on the third. - A promotion made under a bad name is now refused rather than created. Nothing that worked before stops working: every legal
YYYY-MM-DD-short-slugis accepted, and the rule is the shape every task in this repository already has. - Existing Todo entries are compacted one time, in a reviewed migration, not by a hook on read. A live entry keeps every field; only a closed one is reduced, and only
hooks/archivedoes it, sotask next,task stateandreopennever see a shortened entry they write to. hooks/archive's output is byte-identical in a repository that never made a worktree. The teardown instruction is printed only from a linked worktree, tested through Git's owncommondirthe same wayhooks/task'sworktree_kindtests it.
Verification
bash hooks/release-check .—STATUS: pass.bash hooks/repository-check .—STATUS: pass,Orphan task directories: none,Stale task worktrees: none.quick_validate.py skills/taskflow—Skill is valid!git diff --check— clean.bash hooks/smoke-test— not run locally, and recorded as skipped, per the CI-first policy inCONTRIBUTING.mdandRELEASE.md. The tagged commit's difference from315472e— the last commit onmainbefore this release began, CI run39618744277,successwith all seven jobs green — is the four manifests, the two README version samples, and this section. None of those is a surface the suite reads:grep -nE '\$repo/(README|CHANGELOG|package\.json|\.claude-plugin|\.codex-plugin|\.codebuddy-plugin)' hooks/smoke-testreturns nothing, so neither README nor any manifest reaches it, and its release-surface coverage builds its own manifests and catalogs in a fixture (smoke-test:3646-3854) rather than reading the shipped ones. The commit after it moves only the two marketplace catalogs.- The suite ran on all three hosts for every change this release ships: PR #87 run
39260513659at3ac9dab, PR #88 run39423844137at931fe87, PR #89 run39440741438at68dbc5c, PR #91 run39618244116at9e1283d— allsuccess. The suite grew from 73 sections atv1.1.6to 82 here. - Each change carries its own local evidence in the task Plan that landed with it: nine refused name shapes across seven entry points — three of which carry a legal date with an illegal slug, so both halves of the rule are exercised — and four refusal states for
unpromote(PR #89); the compaction, the Notes destination, the ID backfill and the reopen round trip (PR #88); and six mutations for the CI classifier, each reddening its own assertion (PR #91).
Known limitations
hooks/repository-check's stale-worktree list under-reports on purpose. Only ancestry is tested, so a squash- or rebase-merged branch reads as unmerged — a false negative, which is the safe direction when the alternative is inviting removal of a tree that still holds the only copy of a commit. A repository with no upstream has no base to compare against and says so.hooks/task'sfindsecstill matches an entry by substring where an exact ID would be the right comparison. Recorded as a follow-up in the task that found it rather than fixed there.- Skipped CI jobs render
skipped, not green. This repository has neither branch protection nor a ruleset, so nothing reads the checks; enabling one later would needskippedtreated as passing, or every task-archive pull request would look stuck. tools/fixture-compareand the newtools/workflow-affectedare repository tooling, not shipped plugin runtime. They live outsidehooks/and are not covered by the every-hook-parses loop in the smoke suite;workflow-affectedcarries its ownbash -nassertion inside that suite in place of it.