Release notes
What changed in each published release of cotctl, newest first — with the migration steps you need when something breaks.
Check which version you're on:
cotctl --version
And upgrade to the latest one:
npm install -g @cotctl/cli@latest
Read the breaking changes before upgrading. Every release lists them first, and each one tells you exactly what to change in your YAML or in your pipeline. Releases with no breaking-changes section are safe to take as-is.
{/ releases:start — the cotctl release job inserts each new release right below this line. Newest first. /}
0.10.0 — 2026-08-21
⚠ Breaking changes
--allow-script-bots is now required on every apply path.
Applying a YAML that declares a stage of a script-executing bot type — PBScript, CCJS or ESMCode — now fails before anything is created or modified. Until this release the gate only existed on cotctl workflows apply; through the other paths such a bot applied silently and ran arbitrary JavaScript at runtime.
It now covers bots apply, slas apply, schedules apply, routines apply, and both apply -f and apply --dir — which previously had no way to pass the flag at all.
- What to do: re-run the same command with
--allow-script-botsto opt in explicitly. Pipelines that apply these bot types will start failing until you add it. The error message names each offending stage and its type. - Careful in CI: the refusal doesn't use one exit code.
bots applyandroutines applyexit 2;slas apply,schedules apply,workflows applyandapply -f/apply --direxit 1. If your script branches on the exit code, pin it to the command you actually invoke.
webhooks apply rejects a populated context outside the task trigger.
Scoping by survey / group / taskGroup only means something for create-edit-delete-task. On any other trigger the backend accepts it and then matches no event at all, so every delivery is dropped without a warning.
- What to do: change the trigger to
create-edit-delete-task, or removecontext. An emptycontext: {}is still accepted everywhere, so clearing a stale value keeps working.
A PBScript stage whose data.code isn't in your routine catalogue is rejected.
The code is now resolved against the live catalogue at apply time, with a suggestion when a close match exists. Before, the backend accepted any string and failed in production instead.
- What to do: fix the typo, or apply the routine first.
apply --diralready ordersRoutinebeforeSlaandSchedulefor exactly this reason.
Duplicate stage.key values inside one bot are rejected.
- What to do: give each stage a distinct key. Repeated keys made
stage.nextandbot.startambiguous and broke the stage-identity fix below.
permissionsV2 on a Survey is validated against the permission catalogue.
The field takes permission strings, never AccessRole names — passing a role name used to produce an opaque HTTP 500.
- What to do: replace role names with permission codes. Matching is case-sensitive. Use
--skip-remote-validationif the divergence is intentional.
Added
applyretries rate limits on its own. AnHTTP 429now backs off and retries up to 3 times, honouringRetry-Afterwhen the backend sends it. Largeapply --dirbatches no longer abort halfway and need a manual re-run.- Exit code
3for a partial apply. When a Workflow apply fails after creating some resources, it leaves orphaned records behind.apply --dirandworkflows apply -fnow exit3so CI can tell "needs manual cleanup" apart from an ordinary failure.cotctl apply -fdoesn't subscribe to the signal and still exits1— pin your CI branch to the command you invoke. validate --dirunderstandsJobTitleandUser. Both are now schema-checked offline instead of falling through as an unrecognized kind. Seven kinds are recognized — still fewer than the twelveapply --dirhandles, soRoutine,Sla,Schedule,BotandWebhookare not covered yet.--dry-runresolves references inside the same batch. A permission code, an AccessRole → JobTitle or a JobTitle → User reference defined in another document of the same run no longer reports a false failure.scaffoldaccepts state names as you type them.--states "Aprobada" "En compra"keeps the display name and derives thecodeslug from it; you no longer have to pre-slugify by hand and lose the label.- Non-blocking warnings for three documented data traps that previously only surfaced at runtime.
Fixed
- SLA state references resolve to the right id. An SLA whose
start.states[]/end.states[]used state codes resolved to the wrong record and was rejected withHTTP 500: SMStates not found. Codes now work as documented, and remain the recommended form. If you write a raw ObjectId there it must be theSMState._id. - Bot and SLA stages keep their identity across applies. Stage ids were reassigned on every apply, producing a permanent false diff in
--dry-runthat never converged. surveyssearch no longer returns the whole catalogue when the search term sanitises to an empty string.workflows applycan clearrequiredSurveyagain, and the dry-run preview now shows changes tonext[].canChange,next[].requiredSurveyandnext[].bots— they were being applied without appearing in the preview.- Multi-document files are dispatched per document. A YAML mixing several
kinds was previously dispatched by the first document's kind alone. - The bundled AI skills stated the wrong
apply --dirorder. Six of them had drifted and listedSurveyafterWorkflow— the inversion that orphans records when a state machine fails to create. If you author YAML with an assistant, this is worth re-reading.
Docs
Reference pages updated for subfilter / subfilterValue on properties, dataType[] when a stage invokes a routine, surveyTriggers preserve-vs-delete semantics, the validate exec-hook contract, and the transitions[].requiredSurvey clearing semantics.
Older releases. Versions before 0.10.0 are not documented here. If you're on one of them, upgrade to the latest and read the section above before your next apply.