Repository navigation
Releases
v0.3.52
Upgrading
Upgrading starts with one command. mcptask_runner update --self replaces the binary. The bundled .claude/ assets did not change, so nothing on that account asks for a bare update in the projects — the sections below say whether anything else does. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.52 carries this binary; no wrapper changes.
Waiting on a background command no longer freezes the card or kills the run
An agent that started a long command in the background, usually bin/ci, and
then waited for it to finish had nothing running as far as the runner could
tell. After three minutes the card said FROZEN ("No stream activity"). After
twenty minutes the runner killed the agent for inactivity, before a suite that
long could finish, and the task started over
(#15123).
- A background command is now running until it ends. Claude Code announces
such a command as backgrounded. The runner keeps it on the card and in the
watchdog's view until the notification that it finished, so the wait is shown
as the command running, with the same time limits as the same command run in
the foreground. - A call that failed is still closed at once. An error result means nothing
was left running.
Nothing to do on upgrade.
Changelog
Other
- 1158a7ea3659806113f68f57721794d64372d004 [#15112] The whole gate before a merge: gem drift, install day and release notes on every PR (#27)
- 53438bb68c6350136b3ae3d099b75372d197c5d8 [#15123] A background command runs until its notification, not its result
- b74715d4815be348cb1f6b450e5e2804f6541eb8 docs(release): v0.3.51 shipped, the next-tag notes start empty
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOr brew install jchsoft/tap/mcptask_runner, scoop install mcptask_runner,
or npx @mcptask/cli. Verify any manual download against checksums.txt.
v0.3.51
Upgrading
Upgrading takes two commands. mcptask_runner update --self replaces the binary; then a bare mcptask_runner update in every project on the host, because the bundled .claude/ assets changed (baseline_permissions.json, skills/attach-pasted-image/SKILL.md, skills/mcptask-write/SKILL.md, skills/memory-search/SKILL.md, skills/pr/SKILL.md). Run it while no runner is working that checkout. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.51 carries this binary; no wrapper changes.
Every variable the MCP configuration names is asked for, and named on the card
A project's MCP file can authenticate a server of its own with a variable — jchsoft8's
.mcp.json sends Bearer ${LLMMN_TOKEN} to LLM Memory Notes. On a machine without that
variable the coding CLI sent an empty credential, the server refused it, and the run
ended host_refused after it had been paid for
(#14973).
- The runner checks before it spends a run. Before every round, the first one
included, it reads the variables the selected CLI's own MCP file names —${NAME}in
.mcp.json,bearer_token_env_varandenv_http_headersin.codex/config.toml,
{env:NAME}inopencode.json— and when one is not set in its own environment it
starts nothing. The card says, for example, "LLMMN_TOKENnamed in.mcp.json
(server llmmn-production) is not set on this machine — runmcptask_runner update
here to enter it", and the runner waits and re-checks like the other conditions a
person fixes at the machine. No bug piece is filed, and no other variable's value is
ever used in its place. The mcptask.online token keeps its own path: a missing one
is still a refusal to start. initand a baremcptask_runner updateask for each one that is unset, on a
terminal, naming the project, the file, the server and who issues the value, and
read it without echo. macOS and Linux store it as one export file per variable in
~/.mcptask_env.d(the scheduled job's launcher sources it); Windows stores it as a
user environment variable. An existing file is never overwritten. Without a terminal
nothing is asked, and the missing names are the red line at the end.- A waiting runner picks the value up by itself. The re-check reads the variable's
own export file (on Windows, the user environment) and goes on — no restart.
Upgrading
Run a bare mcptask_runner update once per project on each host, from a terminal, to
be asked for any variable that machine is missing. On Windows, if a scheduled run
started afterwards does not see a value entered there, sign out and back in once.
A project's own steps before and after every task
A project can now declare what its agents do before a task and after it — consult a
knowledge store, read a decisions log, record what they learned — and the runner puts
that text into every task's prompt as numbered steps, for claude, codex and opencode
alike (#14985).
- Two optional keys in
config/mcptask_runner.yml:work_steps.before(after the
task is fetched, before any code is read) andwork_steps.after(after the suite is
green on the re-run, before the push). Both reach every mode that works a task, in
the first prompt and in the continuation of an interrupted one. - The runner relays and does not interpret. The text goes in verbatim; the runner
adds nothing, knows nothing about what it names, and never checks the step was done.
A step the agent cannot carry out is said in the work log and does not cost the task.
An absent slot leaves no trace: a project that declares nothing gets the same prompt
as before. initasks, at a terminal, with an example. No answer means no slot;update
leaves an existing declaration alone.- The Claude Code prompt no longer says to skip
/memory-search. A project whose
CLAUDE.md said "search the notes first" got the opposite from the runner. What a
project consults is now its ownwork_stepstext.
An example, not a default
A jchsoft project that keeps LLM Memory Notes might write the following. The runner
ships nothing like it; it is only what a project MAY write. It names the REST search
the memory-search skill runs, because that call works from a headless run's main
session too:
work_steps:
before: |
Search LLM Memory Notes (memory_identifier from CLAUDE.md) for the area this task
touches: curl -fsS -X POST https://llm-memory.com/api/v1/search
-H "Authorization: Bearer $LLMMN_TOKEN" -H "Content-Type: application/json"
-d '{"memory_identifier":"<id>","query":"<topic>","limit":3}'
Read what is relevant before you change any code.
after: |
Store a note in LLM Memory Notes about what this task decided or discovered that
the next task will need — not a summary of the diff.Upgrading
Nothing to do. To add steps to an installed project, write the keys into
config/mcptask_runner.yml (a | block takes several lines); they apply from the
next task.
The scheduled run sees the tools your terminal sees
The scheduled job starts through a bash -l launcher, and a login bash reads neither
~/.zshrc nor (on Debian and Ubuntu) the interactive part of ~/.bashrc — exactly
where rbenv, asdf and mise activate install themselves. On a zsh host the coding CLI
therefore found the system Ruby, and agents spent dozens of commands working around it
(#14976).
- The run measures your login shell's PATH. At its start it asks the login shell
named in the user database (dsclon macOS,getent passwdon Linux — not$SHELL,
which launchd and systemd often leave unset), interactive and in the project
directory, and puts in front whatever the job's PATH lacks. One log line names what
was added; a measurement that fails or takes over 15 seconds is said in the log and
the run continues on the launcher's PATH. fish and nu are not measured, and the log
says so. Windows is not measured: PATH there comes from the user environment. - The launcher sources
~/.mcptask_env.ditself. Variables stored there no longer
depend on which rc file the login shell reads; before, onlyMCPTASK_TOKENreached the
job, because the job file carried it. - Harness note. The fix acts on the environment of the coding CLI's process. A CLI
that runs each agent command through a login shell of its own lets macOS's
path_helperput the system directories back in front of the shims; the shims are
then on PATH but behind/usr/bin. Claude Code reuses the PATH it was started with.
Upgrading
Run mcptask_runner update in each scheduled project. It adds the sourcing block to the
launcher and sets its shebang back to #!/bin/bash -l, including a launcher hand-edited
to #!/bin/zsh -l as a workaround. No reload of the job is needed: the launcher is
read at every start. init and update print in red when this host's login shell
cannot be measured, with the reason.
An install refused at the push recovers once write access is granted
On 2026-11-09 a new developer's install of jchsoft8 met a GitHub account that could
read the repository and not push to it. The person fixed that within minutes, pressed
the card's re-check button, and the runner still did not recover: the install's task
had used both of its tries on the refusal, the pull request had to be opened and
merged by hand, and the queue waited the whole time
(#14918).
- A refused push is re-checked by a push. The child names a probe that asks for
the access that was refused:git push --dry-run origin HEADfor a refused push,
which writes nothing.git ls-remote origin, the example it used to be given,
passes on read access alone, before anything is fixed. - The machine's refusal does not use up the install's tries. A run that ended on
a host condition, the git host or origin refusing this machine included, is waited
out and does not count against the task. A task refused again after its probe
passed is not waited on that probe a second time, because that probe cannot see
the refusal. It waits for a person instead of paying for a run every wait. - The re-check button leaves the card when it is pressed. The card's line says
"Re-checking now — Start was pressed on the dashboard", and the button comes back
only if the re-check finds the problem still there. Before, it came straight back
and the press looked like it had done nothing. - A press, or
initrun again, tries the install's task again. On a task the
runner had stopped trying, and on a merge the git host refused, the press gives
the task its tries back. The card now says what was tried and what the last try
ended with, not "had 2 runs and no pull request came of them". - An open install pull request in a mode that merges gets merged. One that
nobody has tried to merge, such as one a person opened by hand or one a run left
behind, gets a run that merges it. It no longer waits for a person. - A base branch that requires a status gets it from
bin/ci. jchsoft8'smain,
for example, requiressignoff, which only a greenbin/ciposts, and the
install's task runs no tests otherwise. When the host refuses the merge for a
required status and the project has abin/ci, the task runs it once on its
branch and merges again. It fixes nothingbin/cireports: a red run leaves the
pull request open, and the card quotes it with the host's words.
Nothing to do on upgrade. A runner that is waiting on an install today picks the new
behaviour up after a restart.
A pull request that fell behind its base is upd...
v0.3.50
Upgrading
Upgrading takes two commands. mcptask_runner update --self replaces the binary; then a bare mcptask_runner update in every project on the host, because the bundled .claude/ assets changed (skills/mcptask-read/SKILL.md). Run it while no runner is working that checkout. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.50 carries this binary; wrapper changes in its CHANGELOG.
Re-check now: the card checks a host problem at once
On 2026-11-05 a runner waited out a host_refused whose probe was
gh signoff --help, backing off 5, 10, 20, then 30 minutes between probes. The
person installed the missing extension three minutes after a probe; the next one
was due past the end of the work window, so the runner ended its day without
noticing the fix (#14804).
- A
runner_starton a runner that is not paused now means "re-check
now" while the runner waits for a host condition:host_refused,
login_required, a CLI that cannot start or is unusable,
mcptask_unreachable,triage_tools_unreachable,git_remote_auth, a spent
usage window, the install pin's waits, a missing remote, a dirty branch
blocking an urgent bug, and the wait for the quota endpoint to answer. The
wait ends within a minute and the condition's own re-check runs. Before, the
runner logged "not paused — nothing to do" and waited on. - The card gets a Re-check now button for it. The dashboard drew Start only
for a paused runner, so a card waiting on a host condition had no button at
all (#14818). The snapshot now
carries arecheckmember — what the runner waits for, while a press would
cut the wait short,nullotherwise — and the card shows the button exactly
then. Additive;schema_versionstays 3. - A failed re-check does not reset the back-off. The wait after it is the
one the condition was going to wait next, so ahost_refusedprobe that
fails after a press waits 20 or 30 minutes, not 5; pressing again is how to
look sooner. Repeated presses coalesce into one re-check. - Start on a paused runner still lifts the pause, as before.
- The card's remedy says so: every host condition's remedy now ends with
"Pressing the re-check button on this card checks at once instead of at the
end of the wait." A foregroundinitrun, which does not wait, does not get
the sentence.
Ship order: the button itself is mcptask.online's half,
#14819. Deploy it before this
tag, or the remedy names a button the card does not have yet. Nothing to
configure on the runner's side.
Each token variable gets its own export file
On 2026-11-06 install in a fresh clone of a project asked for credentials
"exported as MCPTASK_TODR_TOKEN", and nobody could tell why. The project
gitignores .mcp.json but commits a .codex/config.toml naming that
variable, and the runner takes the name from whichever of the project's MCP
files declares the server (#14822).
- The prompt names the file when the variable is not
MCPTASK_TOKEN:
"That name is the one .codex/config.toml gives the token; change it there to
use another." - The export file is named after its variable:
~/.mcptask_env.d/mcptask_kamr_tokenforMCPTASK_KAMR_TOKEN. Every
variable used to be written tomcptask_token, so on a host with two
projects naming different variables, the second install took the first
project's variable away.MCPTASK_TOKENstays inmcptask_token. - An older export of the same variable is removed when a token is written:
amcptask_tokenthat exportsMCPTASK_KAMR_TOKENwould otherwise be
sourced after the new file and put its older token back. The install says
which file it removed. A hand-written file in that directory that also
exports the variable is left alone and named in a warning.
Nothing to configure. A host whose variable is not MCPTASK_TOKEN gets the new
file on its next install that writes a token.
A runner may now sit retrying instead of ending its day
Until now a runner that works the queue all day (pr_only, workflow,
today_auto_squash, daily, queue_manual, queue_auto_squash) ended its
day on the first error it had no name for, on any task that ended in error
("A task failed, stopping") and on a panic in its own code; daily paused half
an hour and tried again. Such a runner now ends only for a fixed list of
reasons — the work window, the daily budget, a stop, a refused account,
configuration it refuses to start with, a checkout it cannot vouch for, and a
few more. Everything else is reported once, shown on the card and retried, within
caps (Story 14788: tasks
#14802–#14808).
It does not mean the runner never stops: it means it stops for a reason it can
name, and says it on the card.
What the card shows
unhandled_situation: "Hit a situation the runner does not handle yet:
<the error, sanitized and cut short>. Reported as bug #N. Re-trying at
HH:MM." The full text, the stack and the last log lines are in the bug piece
and the run log. Instead ofReported as bug #N.the card can sayAlready reported as #N at HH:MM.,Already reported as #N, still open — this occurrence was added to it.,Reported as bug #N — a regression of #M, which was closed.orCould not be reported: …. When the work window closes before
the next try, the line says so instead ofRe-trying at. After four tries, or
two hours with no round going through, it adds once a day "No work has been
done since HH:MM: it keeps coming back." — and one more message goes on the
bug piece. The card clears once a round goes through. A more specific problem
already on the card (a refused bug report, a spent usage window, …) keeps its
place.model_launches_held: the same failure came back three times today, each
after a paid run of the coding CLI, so no more are launched for it — held
until tomorrow, a new runner binary, or Re-check now on the card.run_ended_real_damage(a tombstone, the run has ended non-zero): after a
panic the runner could not confirm that the coding CLI it was running is gone,
or could not read the checkout. Look at the checkout on that machine — the bug
piece says what was found — and start the runner again.- Three new members inside
attention, for the card to render on its own:
retry_at(when the runner next re-checks or retries, RFC 3339 UTC — for the
catch-all and for every other host-condition wait that has a next check;
nullwhen the work window closes first or nothing is waited),
bug_piece_id(the bug piece the situation was reported as) andattempts
(how often it failed today, across restarts). The last two are set for
unhandled_situationandmodel_launches_heldandnullelsewhere. All
three are additive and nullable;schema_versionstays 3. Until
mcptask.online renders them (a countdown in the viewer's time zone, a link to
the bug), the message says the same in words, so nothing is lost on a card
that ignores them.
What the runner does
- One bug piece per situation, across machines. A situation is recognised
by where it happened, not by its wording: a panic by the function and file,
an error by its type and the step it failed in, a task that ended in error by
its reason and the start of its message with numbers, paths and ids folded.
No task and no project in the key, so the same bug met on ten tasks is one
piece and two different crashes are two. The piece is namedRunner unhandled: …with an[fp:xxxxxxxx]tag; before filing, the runner looks
for the tag in the Errors epic — an open piece gets a message (machine, runner
version, coding CLI, how often, when) instead of a second piece, a closed one
gets a new piece marked as its regression. When the run's own report already
filed the failure, nothing more is filed. - Then the short wait (
short_wait_minutes), never past the end of the
work window, and the round runs again — a task that ended in error is picked
again, as a resume. An outage of mcptask.online is still its own condition
(mcptask_unreachable), and a report it swallowed is filed at the next try. - A panic in the runner's own loop is retried on a clean slate. The coding
CLI it was running is killed — its whole process group — and confirmed gone,
and the checkout is read (branch, HEAD, uncommitted changes, into the bug
piece) and never cleaned. If either cannot be confirmed, the run ends with
run_ended_real_damage(above).
What the retries may cost
- A task that fails twice in a day in a way the runner does not handle is
set aside for the rest of the day (skip-list reasonunhandled_failure, which
no clock revives) and the runner goes straight on with other work. A pinned
urgent bug has its pin suspended for the day as well, which is filed. - The same failure three times in a day, each after a paid run, holds model
launches for it (model_launches_held, above). - A step that launches no model (git, a read of mcptask.online) waits
5, 15, 30 and then 60 minutes between tries, never past the end of the work
window. - Failures that belong to the machine are one wait, never counted against a
task: a host unable to start the CLI at all islaunch_failed, a hard usage
cap (You have reached your specified API usage limits) israte_limited
until the end of the work window, a provider refusing the account is
`harness_login_...
v0.3.49
Upgrading
Upgrading takes two commands. mcptask_runner update --self replaces the binary; then a bare mcptask_runner update in every project on the host, because the bundled .claude/ assets changed (helpers/check_test_lock, skills/test-runner/SKILL.md). Run it while no runner is working that checkout. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.49 carries this binary; no wrapper changes.
init asks which project this is
A project whose CLAUDE.md does not declare account_code and
project_relative_id used to install cleanly and then die in its first triage,
filed as stream_ended (#14521).
- On a terminal,
initnow asks and appends a## mcptask.onlineblock to the
file. The project is picked from what the server lists. - A scripted install on such a project is now refused until it passes
--accountand--project-id(env:MCPTASK_INSTALL_ACCOUNT,
MCPTASK_INSTALL_PROJECT_ID). A project that already declares both notices
nothing. runrefuses the same gap by name, on the card as
project_identity_missing, before any child starts.- An attempt that cannot build its prompt is now filed as
not_started.
init switches the job on
An install used to end with a job written and three commands printed, so the
runner did not run until somebody typed them, and the first evidence that a host
worked came the next morning.
- On a terminal,
initnow ends with a question that says what would happen right
now. A yes files a task that gets the files the install wrote onto the base branch,
pins it, runs it right away in front of you, at any hour, and only then switches
the job on. Nothing the install wrote is committed during the install. - How that task ended is on your screen the moment it ended, ahead of anything the
job does, in one sentence that names the pull request or the branch and the task: at
the end of the run, on the task as a message, and on your screen. "Complete at 100 %"
is said only when mcptask.online says so. If the message could not be written on the
task, the screen says that too. The screen gets one line while it works, and the whole
of its output is in the install's log.--verboseand--ignore-quotaoninitreach
that run. - That run does not answer to the daily budget or to
respect_working_hours. It is
a minute or two of your own install, not a working day, and the log says so in one
line. A later background pickup of the same task keeps the normal gate. - That run never waits for a person. A CLI that is not signed in or cannot start, a
machine refusing the work, a spent usage window, an overloaded API, an account the
server refuses and a stop from the dashboard all end it at once with that reason. It
runs one child.initexits non-zero when the task failed or a basic check refused
it, on a terminal too, with the job on and the task still pinned for a re-run of
initor the job's next start; a re-run files no second task. - While the task is pending, the checkout works from its branch, because that is
where.mcp.json, the skills and theCLAUDE.mdblock are until the merge. The runner
puts it there itself when the task's child ends, whatever the child did, andrunand
initdo the same before their preflight. A branch a merge has deleted fast-forwards
the base branch fromorigininstead, so that merging is what un-sticks it. A tree with
somebody's uncommitted changes is not moved:runrefuses, on the card as
install_checkout_refusedwhen it can reach one, andinitkeeps the job as it is,
both naming the pull request or the branch and what to do. With no task pending,
nothing here runs at all. - Ctrl-C stops the run through its process group. The task stays pinned, the job is not
switched on, andmcptask_runner init --schedulecarries on. A runner already working
in the project holds the lock, so no run is started, and the screen says it takes the
task at its next round and that the conclusion will appear on the task. - A background run that picks the task up later says and writes the same conclusion, and
does not write one already there. - Inside the working day the job then goes straight to work, on the queue unless the
queue is held, and the summary says which. Outside it the job is on and first starts
at its next scheduled time: the end of the workday stays the one hard stop for the job. - The runner works that task first, with no triage, on the
smarttier and with no
tests and no CI. It starts nothing else until the files are on the base branch,
because a task cut from it would run without.mcp.jsonand the skills. - What the task does depends on the mode and on whether the project has a remote:
- A remote,
today_auto_squash: branch, pull request, merge. - A remote,
pr_onlyorworkflow: branch, pull request. A person must merge
it before the runner takes any other work, and the card says so as
install_pr_unmerged. - No remote,
today_auto_squash: branch, commit, merged into the base branch. - No remote,
pr_onlyorworkflow: branch, commit. A person must merge the
branch, and the card says so asinstall_branch_unmerged, with thegit merge
command. A squash merge counts.
- A remote,
- That merge does not ask the host's CI, and does not wait for it. This is a
deliberate exception to "nothing merges without green CI", for the runner's own
files only; every other task still answers to CI. It merges on three basic checks
instead, made in Go: the branch changes only the files the install listed, none holds
a literal credential, and every JSON file parses. A failed check merges nothing and
leaves the pull request or the branch for a person, with the file and the reason on
the card (statusbasic_check_failed). A host that refuses the merge, for a protected
branch or a required review, ends the same way, in the host's words. - A project with no remote now takes no further task, in any mode that opens pull
requests, and that includes a project with no remote that was installed long ago and
used to pay for runs that failed at their last step. The card saysno_git_remote
with the remedy,git remote add origin <url>, and the runner goes on by itself when
one appears. Aworkflowday on such a project used to refuse to start, and now waits
the same way. A run pinned to one piece and an urgent bug are not held. - A directory that is not a git repository gets a question at the start of
init, at a
terminal. A repository with no commit gets one on a yes: empty if everything in it is
the install's own, pushed if there is a remote. If it holds other filesinitasks
before committing them, respecting.gitignore; a no, or no terminal, keeps the job
off, and git's own refusal is the error in git's words. A repositoryinitcreates
starts onmain, and one with no commit and no remote that git calledmasteris
renamed tomainbefore its first commit, because the regular prompts say
git checkout main. - Two runs with nothing to show for them end in
install_task_failed, and the task is
then finished by hand. A host or server that cannot be asked is
install_state_unknown. - A run on an account other than the built-in default now reaches its own account.
WithMCPTASK_ACCOUNTunset the runner used to ask the default account's paths for
its profile, its budget and its pieces, whateverCLAUDE.mddeclared, so such a project
ended on a 404 at its first question.CLAUDE.mdis read now, after the environment. - The job stays off, with the reason, when a repository with commits has changes from
before the install, whenrunitself would refuse, when the project has an origin
this build has no git host for, or when an urgent bug is pinned. A server that refuses
the task endsinitnon-zero, with the job off. - Scripts change nothing unless they add
--start. initnow adds.claude/settings.local.jsonto.gitignore. It is one
machine's approvals, and it should never have been a candidate for a commit. It
addstmp/mcptask_runner/and the runner's logs underlog/too, unless the project
already ignores them, so that the runner's own state never shows up as a change.- The printed "run it once now" hint was false outside the working day since
v0.3.3x, because the launcher passes--if-due. It now says so.
The install's task logs its progress without a refusal
In all seven trial runs, the progress gate refused the install task's first progress
log (#14660). Each child had a TODO
list and had closed its item, but it sent the close and the log together in one batch
of calls. The gate reads the plan from the session's record, and that close was not
on the record yet. The retry a turn later went through, so the run worked, with one
refusal in its log every time.
- Every progress step in the install's prompt now says the log goes out once the plan
call has answered, never in the same batch as it. The progress rules say why. - The gate is unchanged, and so is the prompt of every other task.
init stops asking what it can find out
- The Epic question is gone.
initreads the project's Errors Epic from
mcptask.online and writes the first one, saying which. A project with none, a
server too old to say, no token or a failed read keep the project root, with a
line saying why, and that never stops an install.--epic-idstill overrides it,
and a filledbug_destinationsection stays unless--force. An Epic is never
created. - A script with a token now makes one read of the project list at the end of an
...
v0.3.48
Upgrading
Upgrading starts with one command. mcptask_runner update --self replaces the binary. The bundled .claude/ assets did not change, so nothing on that account asks for a bare update in the projects — the sections below say whether anything else does. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.48 carries this binary; no wrapper changes.
init asks which coding CLI to use (#14405)
On a terminal, a project with no harness: key now gets a list of profiles to choose from, with nothing
preselected, instead of a refusal. That covers the bare
bundle exec rake mcptask_runner:install and npx @mcptask/cli init. Without a
terminal init still refuses, and the refusal now also names
bundle exec rake "mcptask_runner:install[--cli <name>]", because the bare
mcptask_runner is not on PATH until a first install has succeeded. Nothing to
do on an installed host.
Changelog
Other
- fcfe0a0e13e275c51a707e19f4c7006d669f3a1b [#14145] A day that reaches its window has not left over the host
- f8cbfa19072573abb26d6ca8673b5ab2d55c14b6 [#14405] init asks which coding CLI to use when it runs on a terminal
- 3a6f5eab9159def88f13262648c31dc9513e0941 docs(release): v0.3.47 shipped, the next-tag notes start empty
- ea0353c92c1a4d447568d4108eb8a20a3a41993c lint: satisfy errcheck in golangci-lint 2.15 on a bare channel receive
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOr brew install jchsoft/tap/mcptask_runner, scoop install mcptask_runner,
or npx @mcptask/cli. Verify any manual download against checksums.txt.
v0.3.47
Upgrading
Upgrading starts with one command. mcptask_runner update --self replaces the binary. The bundled .claude/ assets did not change, so nothing on that account asks for a bare update in the projects — the sections below say whether anything else does. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.47 carries this binary; no wrapper changes.
workflow reads reviews on GitLab and Bitbucket too (#14053)
The workflow mode used to say "review handling is unavailable" on GitLab and
Bitbucket and work tasks only. It now works in human review notes on those hosts
as on GitHub. A note counts as a bot's when the host marks its author (GitLab
bot, Bitbucket type: app_user) or the account is named like one
(project_<id>_bot_<hash>, group_<id>_bot_<hash>, [bot], -bot, bot-).
A CI account with a plain name is not recognised and its notes count as a
person's. README and docs/catalog.yml now set pr_only, workflow and
today_auto_squash side by side. No action needed.
A machine that refuses the work is waited out, not ended on
A child that cannot finish because this machine refused something it needs — the
dev database not running, an empty SSH agent, a git host refusing this machine —
now says whose fault it is: "scope": "host" or "scope": "task" next to
"reason": "host_refused", and for the host a "probe", one cheap command
that exits 0 once the machine is fixed
(#14051).
task: the piece is set aside for the day like a failed task, and the run
goes on. Until now anyhost_refusedended the day (A task failed, stopping), which is how three runners went quiet at 08:00 on 2026-10-21.host:host_refusedgoes on the runner card with the child's words and the
remedy, and the runner waits. It re-runs only the probe, backing off from
short_wait_minutestolong_wait_minutes, clears the card once it passes,
and retries the task. Without a usable probe the task is retried after every
long wait. A secondhost_refusedon another task the same day is the host's
whatever the child said.
The probe runs without a shell, in the project directory, with a 30-second
timeout; one that needs shell syntax, acts on the runner, or names a command
that is not installed is refused and logged (… which the runner will not run). Nothing to configure. What to check on upgrade: a runner whose card shows
host_refused is waiting for a person at that machine — fix what the card
names and leave it running. The prompt changes (the failure option asks for the
scope and the probe), so prompt goldens moved; the codex and opencode vocabulary
goldens did not.
A read of the runner's own source is no longer refused (#14072)
The self-operation guard (Story 13557) refused a read-only grep for "kill" in
a directory under the runner's own name — on 2026-10-22 a task exploring the old
gem's source on Karel's Mac mini was stopped and set aside indefinitely
(#14072). A stop verb (kill,
pkill, killall, launchctl kickstart|kill|bootout|…, systemctl … restart,
shutdown, reboot) now counts only as the command a shell actually runs —
past sudo, env, nohup, timeout, xargs and leading VAR=value, and
inside bash -c, sh -c, eval and find -exec scripts — never inside a
quoted pattern, a commit message, an echo or a heredoc. Real restarts of the
runner (launchctl kickstart … online.mcptask.runner-…, pkill -f mcptask_runner, pgrep … | xargs kill, kill $PPID) are refused exactly as
before, by the detector and by the Claude Code pre-tool gate alike.
What to do on upgrade: a task set aside by this false positive (its card and the
task note say "refusing to let the task operate on the runner's own job" over a
grep/rg/sed) is picked again once its description changes, or after you
clear the runner's skip list for that project and restart it.
Changelog
Other
- b67d6cadef071e49d337b51eb50950dcc5f6c669 [#14051] host_refused names its scope and a probe; a host cause is waited out on the card
- 7d585515f05efa4531a51aedcbb5f87f7b2afdb2 [#14053] workflow reads reviews on GitLab and Bitbucket: bot detection, three modes documented
- 973684023e7f118336c440dd2fc31473ac426386 [#14072] A stop verb counts only in command position: reading the runner's source for "kill" is not refused
- f2d8761b01fe4a8457f69a647a84adfbad6f8ceb [#14072] Tokenize the command as written: xargs flags keep their case, the command word is compared lower-cased
- 4e7fab8193dd05a835964d668277fedd49aaea87 docs(release): v0.3.46 shipped, the next-tag notes start empty
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOr brew install jchsoft/tap/mcptask_runner, scoop install mcptask_runner,
or npx @mcptask/cli. Verify any manual download against checksums.txt.
v0.3.46
Upgrading
Upgrading starts with one command. mcptask_runner update --self replaces the binary. The bundled .claude/ assets did not change, so nothing on that account asks for a bare update in the projects — the sections below say whether anything else does. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.46 carries this binary; no wrapper changes.
- The API token no longer appears in the
/cableURL. The EventStream
connection now sends it as anAuthorization: Bearerheader, so it stays out
of the server's access logs. Needs a mcptask.online that accepts the header
(live since #13961); upgrade every runner, then the server drops?token=.
today waits on an empty queue instead of exiting
today now does on an empty queue what today_auto_squash already did: it
waits — short_wait_minutes at first, long_wait_minutes once the queue has
stayed empty for a while — with WAITING on the runner card, and an assignment
to the runner's user on the dashboard ends the wait at once, so a task assigned
at 11:00 is started at 11:00 rather than at the next scheduled run
(#14033). The run still ends
where it did: at end_of_workday_hour (Past end of workday in the log), when
the day's hours are spent (Daily quota exceeded while waiting for work), on a
failed task, or when it is stopped. workflow waits the same way since
#14032 — see below.
The line a waiting mode writes when it wakes past the hour now reads Past end of workday (HH:MM) after the wait, stopping … in every waiting mode, instead of
Now past end of workday …, so one phrase finds every end-of-day stop in a log.
What to check on upgrade: a host that runs today without
end_of_workday_hour used to exit on the first empty queue, and now idles
until its daily hours run out — which, with nothing to work, may be never that
day. A today job that should stop at a fixed time needs work_window set
(mcptask_runner init --until HH:MM writes it), and anything that started
today and waited for it to exit, a script or a chained job, will now wait
until the end of the workday.
The today mode is now called pr_only; today still works
today said when, not what: the mode works the queue and ends the day as a set
of open pull requests, which is what separates it from workflow (PRs and their
reviews) and today_auto_squash (PRs merged once CI passes)
(#14054). The canonical name is
now pr_only: mcptask_runner run pr_only, init --mode pr_only, and the
prompt in init offers it under that name.
Nothing has to be changed on upgrade. today stays an accepted spelling —
on the command line, in init --mode, in the launchers and plists already
installed — and it is not an error. A run started as run today says once, on
stderr, that today is the old name. A day-state handover written by an older
binary under today is still resumed. update does not rewrite installed
launchers; re-run init --force to get pr_only in them. today_auto_squash
keeps its name. The runner's card and snapshot never carried the mode name, so
the dashboard needs no change; the name appears in the run log and the
invalid-mode error, which now lists pr_only in today's place.
Whatever stops the runner is on its card; a CLI that cannot start is waited out
A coding CLI that cannot start — it exits before writing a single stream line,
launch_failed — no longer ends the day's work as a failed task
(#14009). It is handled the way a
missing login is: the card carries attention with code launch_failed, the
CLI's own words and a remedy naming the machine, the runner re-checks after
every short wait and tries again, and it clears the card once a child has
started. No kickstart is needed after the binary is put back.
The startup preflight is split by who can fix the refusal:
- Configuration — the
harnesskey, a binary the profile cannot find, a
launcher.commandthat contradicts the profile, a settings file the profile
refuses — still refuses to start, but now publishes the refusal to the card
first (harness_not_configured,harness_binary_missing,
harness_settings_refused) whenever the token allows it. - A person at the machine — a profile's usability probe
(binary.preflight, e.g.codex login status) that fails in words that are
not a login — is no longer a refusal. The run starts, says so in the log, and
the work loop waits it out withharness_unusableon the card.
Every other way a run ends now leaves its reason on the card too: the frame
before the tombstone carries the ending as its message (window closed, budget
spent, queue empty, stop requested), and the endings a person has to look at
carry attention as well — day_ended_task_failed,
day_ended_quota_unanswered, runner_crashed, account_refused,
bug_reports_unfiled. A card that retires with attention on it now lingers
12 hours instead of 60 seconds, so the reason is still there when somebody
looks. attention.code is an open set on the wire; a card that renders
message and remedy generically needs no change.
Nothing to do on upgrade.
workflow is a scheduled mode: tasks, and the review comments on your pull requests
workflow is now the third scheduled mode, between pr_only and
today_auto_squash, and init offers it as answer 3 (none moved to 4)
(#14032). It works the queue and
waits on an empty one exactly like pr_only, and ends the day the same way. In
between — after every task, and on every round that finds the queue empty — it
lists the open pull requests of the account the git host's credentials belong
to, and one carrying a human comment or change request newer than its head
commit gets one child run: the reviews prompt pinned to that pull request,
which fixes, pushes, runs CI and then replies under each note it worked in
(Addressed in <sha> / Not addressed: <reason>). Bots are ignored; the pull
request's own author is NOT — the runner works under your account, so your
comment on "your" pull request is the review — and the runner's own replies are
told apart by the line _— mcptask_runner_ every pr comment note ends with.
Nothing is launched when nothing qualifies, and nothing
is ever merged. A round that fails is not retried that day until a new push.
What to check on upgrade:
- Anything that ran
workflowby hand gets a different mode. The old
two-phase run — a paid child per open pull request until one said
no_reviews, then the queue until it was empty — is gone.workflownow
waits on an empty queue until the end of the workday, likepr_only, and
its outcome is one list of verdicts rather than{"reviews": …, "tasks": …}. - GitHub only, for now. On GitLab and Bitbucket the runner cannot yet tell a
bot's note from a person's (#14053):workflowworks tasks there and says on
the card and in the log that review handling is unavailable. - A project whose git host cannot be resolved refuses
workflow(no
git_host:and no recognisable origin) — naming why, instead of a day of
tasks with every review silently unread. - New:
mcptask_runner pr comment <n> [--reply-to <id>] --body …on all three
hosts; the card's
snapshot gainspr_number(null outside a review round,schema_version
unchanged — the dashboard may render it whenever it likes); review rounds are
remembered for the day intmp/mcptask_runner/pr_rounds.json. - The
reviewandreviewsprompts gained the same reply step. pr reviewson GitHub now includes the pull request's conversation
comments (a third read,issues/<n>/comments), as plain notes with neither
a state nor a path — what GitLab and Bitbucket already returned. Areviews
run therefore also sees a review written only in the conversation.
Changelog
Other
- 9c74f14b46d7c0eb93880d157758e0d31c6d6005 [#13964] Close the rejected dial's response body and group the websocket import
- 7827c473d6fc5a0514c0fe0287a6018f1d42de25 [#13964] EventStream sends the API token in an Authorization header, not in the /cable URL
- 75bd3ccdb016edbdcc614a147a746ce47c100e44 [#13984] The story-merge stress day ends on the merge, not on a three-second clock
- 1b543daa2c4ba926ee595d40959077238c7d3f19 [#14009] Anything that stops the runner is on its card; a CLI that cannot start is waited out
- ed7518224c4eb755e96e80a6b263fa2f0995f02e [#14032] Fix the two lint findings #14054 left on batch-1021
- f7901d0189a3f3ef296a7b6317d56c51eb18bd69 [#14032] workflow is a scheduled mode: tasks, and new human review notes on your open PRs
- 7a25dbba145ae356354657fb36b597f20f60d145 [#14032] workflow sees GitHub conversation comments, and the PR author's own
- e875c88ff3c14aee08d0b32c81bd926043c5c114 [#14033] today waits on an empty queue like today_auto_squash
- 8310dafcdba544bb6582ec9fbce4040b452558b6 [#14050] prhost lists the current user's open PRs with author and head commit time
- 4ea7308930362768c6142a76cfcd319a6cd6cc7c [#14054] Check the legacy-name notice write and keep Consume's doc comment on Consume
- aa3899cc2e10a31d3dd4aeee14a3f4ff84756992 [#14054] The today mode is now pr_only; today stays an alias
- 2452e23459127827bb94b72dd48b7f0024f972ee docs(release): v0.3.45 shipped, the next-tag notes start empty
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOr brew install jchsoft/tap/mcptask_runner, scoop install mcptask_runner,
or npx @mcptask/cli. Verify any manual download against checksums.txt.
v0.3.45
Upgrading
Upgrading starts with one command. mcptask_runner update --self replaces the binary. The bundled .claude/ assets did not change, so nothing on that account asks for a bare update in the projects — the sections below say whether anything else does. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.45 carries this binary; no wrapper changes.
Stopping the runner reaches a piece request that hangs
When the runner is stopped from inside its own process, a request it has made
about one piece is now cancelled too: reading whom a picked task is assigned
to, reading a set-aside task's description, or posting a note on a task.
Before, such a request ran out its own thirty-second deadline first, the same
gap the quota requests had until
#13835
(#13845). An assignee read cut
this way is logged as the run was asked to stop before whom it is assigned to was read and files no assignee_unreadable bug piece, because nothing is wrong
with the piece or the server. That process-internal stop is the one the stress
harness uses; a signal stopped the process directly before this change and
still does, so production runs are unaffected. Nothing to do on upgrade.
Stopping the runner reaches a story-branch read that hangs
The same applies now to the piece reads behind story branches: planning which
branch a task goes onto, and reading a Story to see whether it is finished,
after a task or in the sweep between tasks
(#13891). A read cut this way is
logged as The run was asked to stop before … and files no
story_branch_plan_failed, story_left_unmerged or story_sweep_failed bug
piece. A task whose plan was cut is not set aside as a failure: the loop ends
with stop_requested, and the next run plans it again. A sweep cut this way
does not mark the Story as tried, and a set-aside task whose morning check was
cut is not marked as checked, so the next process that day asks again. As
above, only the process-internal stop is affected. Nothing to do on upgrade.
Changelog
Other
- 6bcc91feeefe07b27f12b15127c95cca99d94c5d Give the stress day a 20-minute budget instead of 12
- 46039c9268a15a7269dbd4cb79e4c5c6e3d08c1a [#13845] The loop's stop reaches a piece call that hangs
- 6fe6d2e9d93cfa6c97d1a7af80460ff7a237cc08 [#13891] The loop's stop reaches a story-branch read that hangs
- 058a46c612ef96a1b8a8db5a7ba408a7bf709fef docs(release): v0.3.44 shipped, the next-tag notes start empty
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOr brew install jchsoft/tap/mcptask_runner, scoop install mcptask_runner,
or npx @mcptask/cli. Verify any manual download against checksums.txt.
v0.3.44
Upgrading
Upgrading starts with one command. mcptask_runner update --self replaces the binary. The bundled .claude/ assets did not change, so nothing on that account asks for a bare update in the projects — the sections below say whether anything else does. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.44 carries this binary; no wrapper changes.
A CLI that was never signed in is waited out too (#13842)
v0.3.43 waited out an EXPIRED Claude Code login. A CLI with no credentials at
all (Not logged in · Please run /login, after a logout, on a fresh machine or
after a deleted keychain item) was still the terminal not_authenticated: the
runner ended its day and the card showed nothing. Now it is the same
harness_login_required, with the same attention card, remedy, wait,
re-check and resume. That covers the startup probe as well as a refused triage
or task.
Codex gets the same treatment for its recorded never-signed-in wording: the
startup codex login status that says Not logged in no longer refuses the
run, and the runner waits for codex login on that machine, re-checking with
the same free command. An expired Codex login and every OpenCode login have no
recorded wording yet and end the run as before.
not_authenticated stays, now only for a credential no login cures: an API key
the provider refuses (Codex, OpenCode). Its message says so: "credentials were
refused", fix the key.
What an operator has to do: when the card asks for a login, open a
regular Terminal on that machine and run claude then /login, or
codex login. The runner resumes by itself; no restart.
For the web team: nothing new on the wire. It is the same attention code
harness_login_required; only its message now reads "is not signed in (its
login is missing or has expired)".
A quota endpoint that does not answer no longer ends the working day
Before each task and between tasks, the runner asks mcptask.online how much of
today's budget is spent. If one of those requests timed out, the runner treated
the missing answer as a spent budget. It logged Daily quota exceeded (quota endpoint did not answer, fail-closed) and stopped work for the day, even when
the time-out happened in the middle of the morning. The noon stress sweep caught
this (#13836).
What the runner does now. A missing answer still blocks new work. The runner
does not guess the budget and does not start a task on one it cannot read. It
logs The quota endpoint did not answer — no new work starts on a budget nobody can read, waits the configured short wait (short_wait_minutes), and asks
again. On the card it shows as waiting for the quota endpoint to answer. Once
the endpoint answers, the runner logs Quota endpoint answering again after N unanswered poll(s) and carries on with the same day. If the outage lasts until
the end of the work window, the runner logs that the endpoint is still not
answering at the end of the window, and the day ends normally. Modes without a
work window keep waiting until the endpoint answers or the run is stopped. These
cases keep their old behaviour:
- an expired trial still ends the run by name;
- a runner with no token or base URL configured still does not wait;
- the mid-task check still lets the running task finish.
A budget the server reports as spent still ends the day exactly as before.
Nothing to do on upgrade.
Stopping the runner reaches a quota request that hangs
When the runner is stopped from inside its own process, any quota request
waiting for an answer is now cancelled. Before, it ran out its own ten-second
deadline first. That process-internal stop is the one the stress harness uses.
In the harness, three such requests in a row kept the loop alive past the 30
seconds it is given to stop
(#13835). A signal stopped the
process directly before this change and still does, so production runs are
unaffected. Nothing to do on upgrade.
Stop and Start from the dashboard (#13840)
The runner now answers two commands on its dashboard socket, for the Stop /
Start buttons on its card. runner_stop with mode now kills the running CLI
at once. The piece stays in progress with its branch and working tree untouched
(the same recovery as Ctrl-C), and the test lock is handed back. Mode
after_task lets the running task finish. Either way the runner stays up and
launches nothing until runner_start, which resumes the same piece. The pause
is held in memory only, so a restart clears it. See "Stopping and starting from
the dashboard" in the README.
What an operator has to do: nothing. The buttons appear once the web half
(#13841) ships, and it shows them
only to runners at this version or later.
For the web team: the commands are
{"event":"runner_stop","session_id":"…","mode":"now"|"after_task"} and
{"event":"runner_start","session_id":"…"} on runner_sessions:<account_code>,
filtered by session_id. Every snapshot carries the new member pause: null,
"after_task" or "paused". It is additive, so schema_version stays 3 and the
Rails side needs no new accepted version. This tag is the minimum runner version
for the buttons.
Changelog
Other
- 4ce3f964319639510bac3688cbc3c4e97d3d03ff [#13835][#13836] An unanswered quota poll is waited out, and the stop reaches it
- a082468e0e1e0ed0e7aeb84923c47dcb9630448e [#13836] Build the focused day's violations with slices.Concat
- 0dc76da36f59ba9aed9cb3a3b1904615ac9cd0fc [#13840] Stop and start the coding CLI from the dashboard
- a5ec620808c2b09b1802e53d85d10dbb8a364491 [#13842] A CLI that was never signed in is waited out like an expired login
- 4468137d1c5bda7a48c048ff44ee363474b58161 docs(release): v0.3.43 shipped, the next-tag notes start empty
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOr brew install jchsoft/tap/mcptask_runner, scoop install mcptask_runner,
or npx @mcptask/cli. Verify any manual download against checksums.txt.
v0.3.43
Upgrading
Upgrading starts with one command. mcptask_runner update --self replaces the binary. The bundled .claude/ assets did not change, so nothing on that account asks for a bare update in the projects — the sections below say whether anything else does. A runner idling in a wait keeps the old binary until its next task or a restart (launchctl kill SIGTERM, wait for the job to stop, then kickstart on macOS), so restart it.
The wrapper gem mcptask-rails-runner 0.3.43 carries this binary; no wrapper changes.
Windows: the coding CLI runs in a Job Object (#13740)
On Windows the kill path used taskkill /T, which walks parent links from the
child's pid. A grandchild whose parent had already exited has no such link, so
it survived the kill, kept the child's stdout open and outlived the run — the
Windows half of #13730.
The child is now created suspended, put in a Job Object of its own
(JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) and only then resumed, so nothing it
starts can be outside the job. The hard kill is TerminateJobObject, and the
kill is not done while the job still counts active processes; a job that cannot
be emptied is filed as the same process_group_survived_kill anomaly as on
macOS and Linux. Because the runner holds the job's only handle, a runner that
dies takes its child's whole tree with it.
Nothing to do on upgrade. A child that cannot be placed in a job is not run:
the attempt fails with placing process N in a process group of its own, which
names the Windows call that refused.
The stress day no longer demands a refusal of a command that was never sent (#13809)
The noon stress sweep failed with seed 1992539297 on three self-operation findings at
once: a launchctl kill that nothing refused, a triage refusal pinned on no piece, and
task #990042 never set aside. The runner was not at fault. The child was the last one
of the day, a triage that had been dealt kill_the_runner, and the 90 s wall ended the
day after its init line, before it fetched the piece or issued the call. The manifest
records the vector a child was dealt as soon as the child starts, and the harness read
that record as the vector having been played.
Each self-operation child now leaves a marker in the state directory just before its
shell call goes onto the wire. The harness judges only the children that left one. A
child whose call went out is still held to the refusal, the attribution and the
set-aside, so a detector that misses a command still turns the day red. The
production detector and the triage set-aside path are unchanged.
Operator action: none. This is a change to the test harness only.
An expired CLI login is waited out, not a lost day (#13827)
When Claude Code's login expires (Failed to authenticate: OAuth session expired and could not be refreshed), the runner used to read the refused triage as a
triage that gave up. It logged a retry it never made ("attempt 2/2"), defaulted
an empty model to genius, failed the round for a missing task_id and ended the
day, and the dashboard card showed nothing.
Now the refusal is named harness_login_required. The runner keeps running in
status waiting, and the card's new attention member names the machine and
the fix. After every short wait the runner re-checks with one tiny request on
the cheapest tier (about 0.001 USD), and it resumes by itself once the CLI
answers. It also probes once at startup. The work window and a stop end the
wait as they end any other.
What an operator has to do: when the card says the login has expired, open a
regular Terminal on that machine (not an embedded or sandboxed session), run
claude, then /login. A login done inside an embedded session can miss the
keychain item that the launchd job reads. See "An expired CLI login" in the
README.
For the web team: attention is additive and schema_version stays 3. It
is {"code","message","remedy","since"} while a condition stands and null
otherwise, as agreed in
#13828.
Also fixed: the triage retry line no longer promises a retry on its last attempt.
Changelog
Other
- 2718f550e9f5466ecadf3b94ecc4c8bbf9310b2f [#13740] On Windows the child runs in a Job Object, so an orphaned grandchild is killed with it
- ddaa109972b1f0691ced6fd0136f3c3d479019de [#13809] A self-operation child cut off before its call owes no refusal
- 5f1983e264a8990d8fdc0e94de83d7e57b45a3b8 [#13827] An expired harness login is waited out with the remedy on the card
- be85a77f0f413f4ba3ed2c3ad52a2540b40e87c2 [#13827] Document the attention member in the snapshot contract
- cca602fd4f26c7e267a497a808c5fa0efa3ef384 docs(release): v0.3.42 shipped, the next-tag notes start empty
curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | shOr brew install jchsoft/tap/mcptask_runner, scoop install mcptask_runner,
or npx @mcptask/cli. Verify any manual download against checksums.txt.