Skip to content

TaskFlow 1.1.7

Choose a tag to compare

@hkwuks hkwuks released this 21 Oct 15:02
· 77 commits to main since this release

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 promote joined whatever string it was given into TaskFlowDocs/<name>/ and ran mkdir -p on it, so a name that was not an YYYY-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: ../evil created a fully usable task directory above TaskFlowDocs, and ../../escaped created 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, and unpromote — 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 said promoted, and the only way back was to delete files by hand. unpromote removes the scaffold and returns the entry to inbox. It refuses in the four states where the directory has stopped being an empty scaffold — the entry is no longer promoted, the directory holds Git-tracked files, the Plan's ## Approval has left requested, 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/archive compacts 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 at 01a067a: eighty-five done entries held 89,554 of todo.md's 114,226 bytes — 78% — and the file had gone from 55 KB at v1.1.1 to 114 KB at v1.1.6, about 12 KB per release. archive already 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 plus ID, Status, Goal, Task and Next action. The prose is moved, not dropped: every non-blank line that is not a field line is appended to the achieved plan.md under ## 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 achieved plan.md to link is left exactly as it is, since a ## Todo Record heading 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.md is 65,329 bytes, down from 114,226.
  • hooks/archive backfills - ID: from an entry's heading (PR #88). Entries written before the - ID: field existed carried their ID in the heading only, where task get could 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-todo keys 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, and hooks/archive states the teardown together with its condition. The list never sets needs, because the check's exit code is a workflow verdict and folding housekeeping into it would make needs-user-input mean two things at once.
  • The Hooks workflow 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. A changes job now classifies the changed paths through tools/workflow-affected and smoke, release and evals read its answer through needs; todo-merge-audit stays unconditional. On the archive pull request that carries this release's own task, release and evals were skipped and nothing else was. concurrency also 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-test are literal rather than this repository's own directories (PR #91). The tolerance half of the name rule used the live TaskFlowDocs/*/ 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/reopen writes - Updated: when the entry has none (PR #88). A compacted entry carries five fields and Updated is 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's loadpairs dies on an absent field, so the documented next step task state <task> planning failed right after reopen reported success. Reproduced on a five-field fixture: reopen OK, then exit 1.
  • CONTRIBUTING.md and skills/taskflow/SKILL.md state 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-*.json wiring file is untouched, and the new code uses git, 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-slug is 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/archive does it, so task next, task state and reopen never 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 own commondir the same way hooks/task's worktree_kind tests 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 in CONTRIBUTING.md and RELEASE.md. The tagged commit's difference from 315472e — the last commit on main before this release began, CI run 39618744277, success with 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-test returns 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 39260513659 at 3ac9dab, PR #88 run 39423844137 at 931fe87, PR #89 run 39440741438 at 68dbc5c, PR #91 run 39618244116 at 9e1283d — all success. The suite grew from 73 sections at v1.1.6 to 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's findsec still 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 need skipped treated as passing, or every task-archive pull request would look stuck.
  • tools/fixture-compare and the new tools/workflow-affected are repository tooling, not shipped plugin runtime. They live outside hooks/ and are not covered by the every-hook-parses loop in the smoke suite; workflow-affected carries its own bash -n assertion inside that suite in place of it.