Repository navigation
Releases: smykov/teamwright
Release list
teamwright 0.13.1
Added
/teamwright:update— the plugin and the project's kit in one step. A plugin update alone changed nothing in the project: the hooks, roles, process docs and scripts there are the kitapplywrote, and the owner had to find the new copy and run the installer from it.tw-install.py updateupdates the marketplace and the plugin for the project's channel (teamwright@teamwright, orteamwright@teamwright-nextwhen the project enables it) throughclaude plugin, finds the copy Claude Code installed for this project (installed_plugins.json: this project's install, else local, else the user's), and runsplan— or, with--apply,apply— from that copy, not from the one the session loaded;--no-fetchskips the plugin update. The command shows the plan, applies on the owner's yes, commits the kit files and says to restart Claude Code. Tests intests/test-tw-update.sh.
Fixed
- A worktree is no longer held by another task's
IN_PROGRESS. The rule "one taskIN_PROGRESSper working tree" counted every record on the branch, and a branch cut from main carries the other tasks' records as they were: one taskIN_PROGRESSin the main tree made everytw/<ID>worktree whose task had finished its analysis wait for it (wip_waiting) and endidle- with/teamwright:parallel auto, every worktree at once. In a per-task worktree the rule, and the "uncommitted changes belong to one active task" check, now look at the worktree's own task only; the main tree is unchanged. Tests intests/test-tw-worktrees.sh.
Security
/teamwright:parallel autosessions may not push, merge or callgh: the defaultTEAMWRIGHT_CLAUDE_ARGSadd--disallowedTools "Bash(git push:*) Bash(git merge:*) Bash(gh:*)"; merging stays with the main session./teamwright:parallelpre-approvesgit -C <path> rebaseonly, not anygit -Corgit rebasecommand.SECURITY.mdlists what runs without a prompt: the commands skills pre-approve, the project config that runs as code (config.yml,progress.json), headless parallel sessions, autonomous CI runs.- CI: actions pinned by commit SHA, the gitleaks download checked against its SHA-256, and a check that no name from the
PRIVATE_NAMES_REsecret appears in the tree or recent commit messages. The autonomous-run example indocs/usage.mdpins its actions too and warns that task records and issues are model input.
Changed
/teamwright:updateandtw-parallel.pyuse one variable for the Claude Code binary,TW_CLAUDE.- Documentation brought up to date: the command and hook counts and lists (README,
docs/README.md,CLAUDE.md,CONTRIBUTING.md),parallel autoin the commands table, the process settings in the glossary anddocs/usage.md, requirements and/reload-pluginsin the quickstart,stage-protocol.mdin the reading order, the project status, thetw-parallel.pyvariables indocs/process/plugin.md; this changelog states its format.
Full changelog: CHANGELOG.md
teamwright 0.13.0
Added
/teamwright:parallel auto: the orchestrator runs tasks in parallel itself. Until now parallel work meant the owner opened a session per worktree; the orchestrator could not even call/teamwright:parallel(it was owner-only), and red-tests or implement held one taskIN_PROGRESSper tree while nine analysed tasks waited. Nowparallelis open to the model, andautoanalyses the next batch in the main tree, plans disjoint seams, starts the worktrees, prepares their index, launches their stages headless, watches, merges each approved task (rebase, gate in the worktree, fast-forward) and starts the next — owner decisions are collected, not asked. A stage session that leaves analysed tasks waiting withbatch.parallelabove 1 goes toparallel auto(stage-protocol.md§3).tw-parallel.py prepare <ids>— the context tool's index per worktree: Serena's.serena/cachecopied from the main tree, thenserena project indexrefreshes what differs (TW_CONTEXT_INDEX_CMDoverrides the command). A worktree that cannot index is reported and not launched.tw-parallel.py launch <ids>/run <id>— in the worktree, ask the router; whileheadless.run, runclaude -p <headless.prompt>(one stage per session, a fresh context) and ask again; stop atCODE_COMPLETE, an owner decision (escalatewith the reason),TEAMWRIGHT_PAUSE, two stages that moved nothing (stuck) orTW_PARALLEL_MAX_STAGES(12). At mostbatch.parallelat once. State and log:.teamwright/parallel/<ID>.json,.log;statusshows them. Command:TW_CLAUDE(defaultclaude) withTEAMWRIGHT_CLAUDE_ARGS(defaultacceptEdits, the tools a stage needs, the autonomous-run note). Tests intests/test-tw-parallel.sh..teamwright/parallel/,.serena/cache/and.serena/logs/are gitignored by the installer, so a worktree's copied index does not look like uncommitted work.
Full changelog: CHANGELOG.md
teamwright 0.12.2
Fixed
- Stage sessions check the context tool before any role. The check that the configured context tool (Serena, a code graph, docs) is connected was step 0 of
/teamwright:flow; when the lifecycle moved into the stage commands it was not carried over, so/teamwright:analyze,red-tests,implement,review,verifyand/teamwright:routerspawned roles without it, and the roles read code with grep. Nowstage-protocol.md§0 (and every stage command and the router) checks that the provider fromtools.contextis in the session and answers one cheap call, re-indexes a stale index, and spawns no role until it does: interactive — the owner restarts the server or answerscontinueto use the built-in search; headless — the session ends withOutcome: escalate./teamwright:flowdoes the same instead of falling back silently. - Every role is told which code search to use. Each role's spawn prompt starts with a code-search line taken from
tools.context(Serena:find_symbol,find_referencing_symbols,get_symbols_overview,search_for_pattern; grep and reads only for non-code text or where the tool misses) — role files edited by hand keep their own tools section, and the prompt still carries it. stages.<stage>.batchsays what it is./teamwright:settingsnow states that a stage's batch is taken one after another in one session, not at the same time (batch.parallel), and that red-tests and implement keep one taskIN_PROGRESSper working tree.
Full changelog: CHANGELOG.md
teamwright 0.12.1
Fixed
/teamwright:settingsoffers every key. It offered only the keys whose default differs from the recommendation, in one question block of at most four questions with the five stage models first — so the stage batches,batch.parallel,test_guardandrouter.selectwere never offered, and most keys never fit. Now the skill shows the whole table, asks which groups to change, then asks up to four keys per call for each chosen group; the stage models are one question with ready sets.
Changed
- Neutral examples in comments, tests and the changelog (
T-12,task/T-12-run3). No behaviour changes.
Full changelog: CHANGELOG.md
teamwright 0.12.0
Added
- Stages: the lifecycle runs one stage per session when you want it to. Until now
/teamwright:flowdrove a task through analysis, red tests, implementation, gate, review and verification in one session: an autonomous job that was cut lost the whole task, and the context grew with every role. The lifecycle is now five stages — analyze (readyTODO,ANALYSIS,VERIFY_FAIL→IN_PROGRESS), red-tests, implement (green tests, the test-writer's failure-branch bite andtw-mutants,tw-gate --for-task+tw-gate <area>, technical-writer, commit,IN_PROGRESS → NEEDS_REVIEW; a failed check is fixed in the same session), review, verify — and push (DRAIN) stays a separate owner decision. New commands:/teamwright:analyze,/teamwright:red-tests,/teamwright:implement,/teamwright:review,/teamwright:verifyrun a named stage and first ask the router, refusing with the right command when the task is at another stage (/teamwright:implement T-12on a task without red tests →/teamwright:red-tests T-12);/teamwright:routerasks the router and runs exactly one stage, then stops at the barrier./teamwright:flowstays, as a loop of router steps in one session; its lifecycle text moved into the stage commands, and its resume heuristic into the router. - The
stage:task field (red-tests | implement, the sub-stage ofIN_PROGRESS), written by the stage that finished its work and committed with it. The router checks it against the evidence (## Testsfilled = the red tests are written; analysis complete): a field behind the evidence wins (red-testswith the tests filled — a send-back to the tests), a field ahead of it loses (implementwithout tests → red-tests), with a warning; a task without the field (started by an older flow) is routed by the evidence.IN_PROGRESSmay now cross a session boundary when its stage is recorded and the tree is clean (lifecycle.md,sessions.md,AGENTS.md). Task template: astage: ""line. - Stage gate (analysis gate, check 4): entering
IN_PROGRESSsets the stage in the same edit (red-testsfromTODO,ANALYSIS,VERIFY_FAIL; a send-back may setimplement), the field only movesred-tests → implement(back tored-testsfrom implement, or when review or verification return the task), is never removed from anIN_PROGRESStask,stage: implementneeds## Testsfilled, andIN_PROGRESS → NEEDS_REVIEWneedsstage: implement, the tests and — for a task that changes code (_task.code_changed: a commit naming the task, or a tracked uncommitted change, touches a code file) — the### Failure branchestable: only the implement stage hands a task to review. Shell rewrites ofstage:in a record are refused likestatus:, recognised by the values too. Tests intests/test-analysis-gate.sh. - Router: stages, per-stage batches and thresholds, candidates, models, headless output (
scripts/tw-next.py). Every task-bound decision carriesstage; the output addsbatch(the router's default) andbatch_cap,candidatesfor the chosen stage — every waiting task in router order with priority, effective priority, how many tasks it unblocks,depends_on, seam paths and the areas they fall in, size hints, the candidates itoverlaps(tw-parallel.py's seam rule) and those it depends on — so a session picks its batch without reading task files;stages(per stage: waiting, due, why),work_available(the one predicate for "what runs" and "nothing to do"),model,command,selectandheadless(run,prompt,model, a note) for one stage per CI job.--stage S [--task IDS]answers a stage command (request: ok, batch, candidates, hint; an explicit stage ignoresmin). New settings, validated by the installer:process.stages.<stage>.batch(1–10, default 1),.min(1–10, default 1) and.max_wait_hours(0–168, default 0) — a stage that is not due (fewer thanminwaiting, no P0/P1 task, the oldest younger thanmax_wait_hours) moves behind the due work, and runs when nothing else is due;process.models.<stage>(opus,sonnet,haiku,fableor aclaude-…id; unset = the role files'model:), passed as the agent tool'smodelon the stage's role spawns;process.router.select: script | agent(defaultscript: the script selects and counts, the session picks the batch from the candidates;agent: the session reads the task records itself — more tokens, for records the script cannot read). Defaults behave as before stages. Tests intests/test-tw-next.sh(stage field vs evidence, legacy tasks, resume rules, thresholds, batches, candidates, requests, models, headless) andtests/test-installer.sh. - The journal records the stage and the chosen batch:
outcomes.jsonlgetsstage(from aStage: analyze|red-tests|implement|review|verify|flowline),batchandbatch_reason(fromBatch: T-3 T-7 - <why>). Tests intests/test-journal.sh. - Tokens per stage.
StopandSubagentStoplines inoutcomes.jsonlcarrytokens: the model usage of the agent's transcript so far, per model (input,output,cache_read,cache_write) — the session's own lines fromtranscript_path(sidechains left out), a subagent's fromagent_transcript_path; a message the transcript repeats per content block is counted once, and the transcript is read incrementally (offset under.teamwright/logs/.tokens/).teamwright-metricsadds per stage (journal.stages, a Per stage table in Markdown): sessions grouped by the stage they declared, duration, tool calls of every agent, outcomes, tasks taken, tokens (per session, per task, per model) and, with the new--prices <json>($ per million tokens per model regex), a cost; a session that sent no tokens isno data, never 0./teamwright:improvereads it as a signal (model or batch per stage).docs/process/metrics.md,gates.md(journal). Tests intests/test-journal.sh,tests/test-metrics.sh. - Review gate check 7: one reviewed task spawns at most two new tasks (
lifecycle.md§6 — until now only a line in the review skill). A newdocs/tasks/<ID>.mdcreated while the session's latest spawn by its main agent is acode-reviewerfor task R counts toward R's review (the reviewer's rca task and the orchestrator's follow-up task alike; a later spawn of another role closes the window); the third is refused with the exit — add to an existing record, one umbrella task, or ask the owner. Gate-first warns. LimitTEAMWRIGHT_REVIEW_NEW_TASKS(2); ledger.teamwright/sessions/<R>.new-tasks.jsonl.gates.md, the review skill. Tests intests/test-review-gate.sh. - The recommended models per stage and stage thresholds are offered.
tw-next.py --jsonreportsrecommended(_config.PROCESS_RECOMMENDED, now withstages: batch 1, min 1, max_wait_hours 0 per stage, beside the models analyze/implement/review/verifyopus, red-testssonnet). adopt and init ask about the models and thresholds in their settings block,/teamwright:settingsshows the recommended column from it and offers the keys still at a different default,/teamwright:statusnames them. Tests intests/test-tw-next.sh. tests/test-usage-example.sh: the autonomous-run workflow indocs/usage.mdis parsed (PyYAML, or a subset parser where it is missing), its route step is run against a real project (tw-next.py --json, theheadlessandreasonfields it reads,run=falsewith nothing to do), everysteps.route.outputs.*another step reads is printed, the dispatched file is the one the docs name, and theclaudeflags exist when the CLI is installed. It found a real error:run: echo "router: …"is not valid YAML — the step now passes the reason throughenv(also no template expansion inside the shell).- Release channels: stable and experimental. The branch
nextis published as a second marketplace,teamwright-next(/plugin marketplace add smykov/teamwright#next,/plugin install teamwright@teamwright-next); it carries no version, so every pushed commit is an update.scripts/check-versions.py(inmake lint, andmake release-check) applies the rule by the marketplace name, not the branch:teamwright— the versions agree and have a changelog section (unchanged),teamwright-<channel>— no version anywhere. The installer records a channel kit's version as the 12-hex commit Claude Code computes (from the git checkout, orinstalled_plugins.jsonfor an installed copy), and enables its channel in.claude/settings.jsononly when no other channel of teamwright is enabled there or insettings.local.json— two enabled copies with one name do not mix. README (Channels), CONTRIBUTING (Branches and channels),docs/usage.md. Tests:tests/test-release-check.sh,tests/test-installer.sh.
Changed
resumemeans unfinished work in the tree: tracked changes, or a session that endedinterrupted. A current task on a clean tree no longer forcesresume: its stage is recorded and committed, and it is routed to that stage (continue). InIN_PROGRESSthe router's role follows the stage (test-writer at red-tests, developer at implement).tests/test-tw-next.shupdated.docs/process/sessions.md§8 (stages, the stage field, selection, batches, thresholds, models, the stage-command protocol, autonomous runs), §5 (model per stage);lifecycle.md,gates.md,plugin.md,AGENTS.md, the flow, parallel, settings and status skills,design-decisions.md,docs/usage.md(an example workflow, one stage per job) and the README describe it.tw-next.pyno longer prints aBrokenPipeErrorwhen its reader stops early (| grep -q).
Fixed
- Router: only a linked worktree is a per-task tree. An autonomous run checks out the whole project on a task branch (
task/T-12-run3); the router took the branch name for a parallel worktree of T-12, refused verify for it ("merge it from the main tree") and d...
teamwright 0.11.0
Added
-
scripts/tw-gate <area> [--fresh], installed into projects: the full gate once per state of the code. In an autonomous run a small task took about 90 minutes, and its ten-minute area gate ran five times on the same code — twice by the developer (it cut the output withtail -8and had to run it again to see the result), then by the flow's Gate step, the code-reviewer and the task-validator. tw-gate runs what the role files name as the gate (the area'sgenerate_command, then itsgate_commandthroughscripts/tw-run, or the top-levelgate_commandas written), writes the whole output to.teamwright/logs/gate/<area>-<time>-<commit>.log($TEAMWRIGHT_LOG_DIRrespected), records the run in<area>.jsonandruns.jsonl(area, head, clean, tree, exit, duration, times, log, command), and prints PASS/FAIL, the exit code, the duration, the last 30 lines of a failure and the log path; its exit code is the gate's. A PASS is reused when the working tree has the same content (a git tree id of HEAD plus uncommitted and untracked changes, ignored files left out), root and command, and the tree did not change during the run:PASS (cached: same commit <sha>, <when>, log <path>). So the developer's PASS before the commit answers the flow, the reviewer and the validator once that content is committed. A FAIL is never reused;--freshalways runs. Tests:tests/test-tw-gate.sh(also under bash 3.2). -
scripts/tw-mutants <area> <ID> --tree <copy>, installed into projects: the task's mutant catalog, in one runner start. Mutants are kept in the repository asdocs/tasks/<ID>.mutants/: one<NN>-<slug>.mutantfile each —guarantee:(the Acceptance/DoD line it breaks,-for none),file:,added_by:, optionaltest:andequivalent:, and the exact replacement between--- old/--- new/--- end— plustest-command, the command that runs only the task's tests. tw-mutants refuses the working tree, checks every entry against the throwaway copy (old text exactly once), and runs a baseline and every mutant in onescripts/tw-run <area>start with the copy as the project root (forrunner: docker, the copy is what the container mounts). One line per mutant (killed,SURVIVED … breaks: <guarantee>,survived, follow-up,equivalent), a report in.teamwright/logs/mutants/; exit 0 when no survivor breaks a guarantee, 1 when one does, 2 for a stale or broken entry, 3 for a red baseline. Tests:tests/test-tw-mutants.sh(with a fake docker that counts starts and checks the mount). -
tw-gate and tw-mutants outlive Claude Code's 10-minute Bash limit. In an autonomous run the gate was cut by the limit and started again; agents also killed the test container to restart, or polled the log in
until grep …; sleeploops. Both scripts now start the real run detached — its own session and process group (scripts/hooks/_job.sh, Python'sstart_new_session, bash 3.2 and macOS included), output to the log, a job directory keyed like the cache under.teamwright/logs/{gate,mutants}/jobs/— and wait in the foreground up toTW_WAIT_SECONDS(default 540). Done in time: the normal result and exit code. Not yet:RUNNING: gate <area> (pid N), started <when>, log <path> - run the same command again to keep waiting, exit 75. The same command again attaches to that run instead of starting another; a result that finished while nobody waited is printed by the next same call; a run of the same area (or task) on other content is waited for first;--freshattaches to a running run rather than starting a second; a run killed without a result (pid gone) is reported, cleaned up and started again; a killed caller leaves the run going;scripts/tw-gate --stop <area>ends one. Role files, AGENTS.md and the flow: never wrap them in sleep loops or background calls — onRUNNINGcall the same command again; other long test commands run once throughscripts/tw-runwith output to a file. Tests: fake slow gates and catalogs with tiny wait budgets (attach, uncollected FAIL,--fresh, killed run, other content, killed caller,--stop). -
scripts/tw-gate --for-task <ID>/--for-diff <base>and--top: gate the areas a task changed. In one project the validator ran the server suite for a CI-only change. The task's changed files (its own commits — on a branch, those since it left the default branch; everywhere, commits whose subject names the id first, so a commit that only mentions the task in its body does not count — plus uncommitted and untracked) map to the areas whosesource_dirs,test_dirsorworkdirhold them; a file outside every area means the top-level gate (--top, every area when there is none), except records, docs and Markdown, CI configuration and tool settings, which need no local gate ($TEAMWRIGHT_NO_GATE_RE). The flow's Gate step, the code-reviewer and the task-validator run exactly the listed gates. -
Throwaway copies get generated code and a warm build cache. In one project every clean copy for mutants ran
build_runnerfor ~5 minutes. New area keys:generated: [paths](the outputs ofgenerate_command) and optionalgenerate_inputs: [paths]— tw-mutants copies the outputs from the working tree when the copy's inputs are unchanged, and otherwise runsgenerate_commandin the copy inside the same runner start (exit 4 when it fails);copy_env: {VAR: value}— applied byscripts/tw-rununderTW_RUN_COPY=1,{cache}=.teamwright/cache/<area>/copyof the main checkout (/tw-copy-cachein docker), e.g.CARGO_TARGET_DIR: "{cache}/target", so a copy neither builds from cold nor tramples the working tree's target dir;TW_CACHE_ROOTmakes a copy's docker runs reuse the main checkout's package caches.scripts/tw-mutants <area> --tree <copy> --preparedoes only this step for tests run by hand. The installer validates the keys. Tests intest-tw-mutants.shandtest-installer.sh. -
scripts/tw-test <area> [tests ...]and the test guard: tests only through the wrapper. In one project's autonomous run the test-writer, after fixing one test, ran the same ~4-minute suite four times in a row: twice with the output cut by| grep … | tail -15(the failures were lost), once with-p no:logging, which removed thecaplogfixture and produced 25 false errors, once right — ~12 minutes, and with one worker instead of the configuredPYTEST_WORKERS; the developer ran the gate script directly twice. The role files already said "never cut output"; agents ignored it, so the tooling enforces it.scripts/tw-testruns the area's newtest_select_command({tests}= the selectors, shell-quoted; workers stay in the config string, e.g.pytest -q -n ${PYTEST_WORKERS:-4} {tests}), elsetest_commandwith the selectors appended, throughscripts/tw-run; the whole output goes to.teamwright/logs/test/, the screen gets PASS/FAIL, exit code, duration, the counts (pytest, cargo test, go test, flutter/dart test, jest, vitest), the failed tests with their first error line (at most 30) or the last 15 lines, and the log path. A repeat on the same area files (tw-gate's area-scoped key), command and selectors answers from the cache — a FAIL too: the answer to a failure is its log;--freshreruns. Detached like tw-gate (RUNNING, exit 75, the same call attaches);--tree <copy>runs in a throwaway copy. The new PreToolUse hookscripts/hooks/test-guard.sh(Bash) refuses test runners and gates called directly — built-in patterns (pytest,python -m pytest,make test|check,flutter|dart|go|cargo test,npm|pnpm|yarn test,vitest,jest,phpunit,gradlew test,mvn test, …, also behinduv run/npx,sh -c,docker runand insidescripts/tw-run), every area'sgate_command/test_command/test_select_commandand the top-levelgate_command(e.g. a project'stool/gate.sh), and the new top-leveltest_runner_patterns— and test or gate output piped intogrep/tail/head/sed/awk, wrappers included; the message names the command to run instead (scripts/tw-test server tests/pipeline,scripts/tw-gate server) and says to read the log. It has its own switch, because gate-first only warns:process.test_guard: block | warn | off(defaultblock, also in gate-first),TW_TEST_GUARDin the environment Claude Code starts with wins; the area's lint, codegen, API and UI test commands throughscripts/tw-runstay allowed. The installer validates the new keys. Roles: developer, test-writer, code-reviewer and task-validator,AGENTS.md,gates.md(new section Test guard),tools.md,plugin.md, the settings, adopt and init skills and the README carry one rule — tests only viatw-test, gates viatw-gate, mutations viatw-mutants; the test-writer's wrong implementations and the validator's hand-run tests in a copy usetw-test --tree. Tests:tests/test-tw-test.sh,tests/test-test-guard.sh,tests/test-installer.sh. A config command whose head is a script that only wraps another one (tool/gate.sh sh -c '…') is not itself a signature — its inner command is checked, so lint, formatting and migrations through the same wrapper still run. -
pre-push warns about a CI-skip marker on the pushed tip (
[skip ci],[ci skip],[no ci],[skip actions],***NO_CI***,skip-checks: true): CI decides by the tip's message, so the code under it and a pull request opened from it go unchecked — one project lost ~8 minutes on it. A warning, not a refusal. AGENTS.md, the flow's push step anddocs/usage.mdnow say: never[skip ci]in a commit message, least of all on the last commit before a push; record-only pushes are skipped by the CI configuration (paths-ignore,scripts/tw-ci-changes).
Changed
process.batch.parallelup to 10 (was 1–4): an owner running more parallel worktree sessions (and per-worktree tool slots, e.g. Serena) on one project hit the cap. Default stays 1 (off).- **Roles run the gate ...
teamwright 0.10.0
Added
- The router and the progress report read tasks from per-task worktrees (#12). Before,
tw-nextknew only/teamwright:parallel'stw/<ID>branches, and read even those from the branch; a task worked in a hand-made worktree on its own branch kept its main-tree record, so the main checkout showed itTODO, offered tostartit again orresumea task already merged there, andtw-progresslisted it as an unassigned area because its analysis existed only on the branch. Now a worktree whose branch names a task —tw/<ID>ortask/<ID>, any suffix after a separator (task/T-12-parser), or$TEAMWRIGHT_TASK_BRANCH_REwith the id in group 1 — supplies that task's state from its own copy of the record (status, analysis, review, verify and validation rounds), markedin_worktreeandbranch. The main tree skips it, offersmerge(naming the branch and path) once it isCODE_COMPLETEor later, the human output listsin worktrees:, andparallel.worktreesin the JSON gives path, branch and status. A session in such a worktree sees only its own task. Tests:tests/test-tw-worktrees.sh.
Full changelog: CHANGELOG.md
teamwright 0.9.0
Changed
- The test-writer breaks each Acceptance failure branch before review (#11). Its first spawn runs before the implementation exists, so the end-to-end test of a failure branch was first mutated by the validator after deploy; in one project
toothless-testwas the most frequent blocking finding (14 of 59) and 4 of 5VERIFY_FAILs were test gaps, each costing a merge, a deploy, a re-analysis and another review. Now/teamwright:flowspawns the test-writer a second time withmode: failure-branch biteonce the developer's tests are green, before the gate: one mutation per failure branch on a throwaway copy that carries the uncommitted implementation, the test that guards it run there (forsurface: api | boththe end-to-end test verification will run; for a UI test, the state its wait reaches only after the branch ran), a surviving mutation means the test is rewritten. The result is a### Failure branchestable under## Tests; the code-reviewer reads it (a missing row or a survivor istoothless-test) and does not re-run it, the task-validator re-runs it with its own mutations. Role files (test-writer, code-reviewer, task-validator), the task template, the flow skill (step 4 and resume),lifecycle.md,roles.md,gates.mdand the README describe it. Projects pick it up after/plugin update:/teamwright:statusreports the changed files, andtw-install.py apply(or/teamwright:adoptagain) updates the ones the project did not edit.
Full changelog: CHANGELOG.md
teamwright 0.8.6
Fixed
tw-processcounted no ratified tails on macOS. It finds the commits that settail_code: ratifiedwithgit log -G, and the pattern used\s, a GNU extension that git on macOS (POSIX regex) does not know; the count was always 0 there. The pattern is[[:space:]]*now. Found by the macOS CI job once the bash 3.2 failures were out of the way.
Full changelog: CHANGELOG.md
teamwright 0.8.5
Fixed
- pre-push refused every push on bash 3.2 (the macOS default). With no remote-tracking branches yet — a first push, or a new remote — the hook expanded an empty array under
set -u, which bash before 4.4 treats as an unbound variable:delivered[@]: unbound variable, and the push was refused. The empty case now expands to nothing, as elsewhere in the hooks. The bash 3.2 CI job, which runs the existing pre-push tests in abash:3.2container, was failing on this. - The demo died on macOS (
from→: unbound variable): bash 3.2 takes the bytes of a non-ASCII character after$nameas part of the name.examples/demo.shwrites${from}→${to}, andmake lintnow fails on$namebefore a non-ASCII character in any shell file (scripts/check-sh-names.py) — Linux CI could not see it otherwise. - Tests:
ls | grepcounts replaced withfind(shellcheck SC2010);test-tw-ci-changes.shimportsimportlib.machineryexplicitly, which newer Python no longer pulls in throughimportlib.util.
Full changelog: CHANGELOG.md