no_log on the create task is right: tootctl prints "New password: #{password}"
on stdout when it succeeds. It also suppresses the syslog record, so a failure
left nothing anywhere. The rescue bundle for run 33574056480 carries the
neighbouring unblock task with its whole Ruby script and no trace of this one.
The failure path is a different stream. report_errors goes through
fail_with_message, which raises Thor::Error, and Thor writes that to stderr,
after the create has already given up on generating anything. Quoting stderr
therefore costs no secret.
The task now records without judging, and an assert decides. It reads the
already-existing case from either stream rather than stderr alone, which is
hardening rather than the fix: the source shows Thor puts it on stderr, so the
old condition would have tolerated it and the account being taken was never
what failed here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mastodon moved its reserved-usernames list out of config/settings.yml into the
username_blocks table, seeded on every fresh install by
db/seeds/05_blocked_usernames.rb - 'administrator' included, along with admin,
root, owner, mod, staff and more. Shipped in v4.5.0 (2025-11-06); this repo is
exposed since 5a041982f pinned v4.6.2 on 2026-07-02, because the same-day
db:prepare migrate service seeds the fresh database.
The role's pre-create task still ran `sed -i '/- administrator/d'
config/settings.yml` - the old location, which at v4.6.2 holds no reserved list
at all. sed exits 0 on a non-matching pattern, so the task reported success
while doing nothing, and `tootctl accounts create administrator` died on the
UnreservedUsernameValidator with the output censored by no_log. That failed the
Instructions job in run 30884701334 (job 91914016159). Diagnosed against
upstream sources at both tags; run 29150751430 in the org repo is the control -
its image predated v4.5 (db:seed rc=0 one minute before a successful create,
impossible with the seed present), which is why the failure never showed there.
The sed becomes a rails-runner unblock at the new location, following the
stdin-fed pattern the no-inline-rails-runner lint prescribes (see
web-app-decidim/tasks/01_setup.yml): the Ruby lives in
files/ruby/unblock_admin_username.rb, reads the username via ENV.fetch, deletes
its username_blocks rows and prints the count; changed_when keys on that count.
The old `when: username == "administrator"` guard is gone - the seed also
blocks admin, root, owner and others, so any configured admin name can collide.
The create task's "taken" tolerance for re-runs is untouched.
Not covered: whatever waits behind the create - approve, plugins and the
playwright specs have not run with an existing admin since the pin.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The repo's async convention is fire and forget: ASYNC_POLL is 0 whenever
ASYNC_ENABLED is true, and the swarm test lane forces it true through
extra vars. A task dispatched that way returns a stub, so a changed_when
or failed_when reading its own register evaluates against nothing and can
never report changed and never fail. Several tasks carried a
"not ASYNC_ENABLED and" prefix in front of those conditions, added to
avoid touching undefined rc and stdout, which turned the guard off
entirely rather than adapting it.
That is how the nextcloud plugin configuration stayed broken unseen: the
batch reported ok after 0.8s while its delegated commands kept running,
the next task read the half written configuration, and no failure in any
of them could surface.
Every async in the repo was decided individually rather than swept. Where
nothing consumes the result, the async stays and a one line rationale now
records what does not read it. Where a later task, a handler or a browser
does consume it, either the async goes or the result is collected.
Collecting is what the new reaper does. It waits for a whole batch, then
reports every broken job at once instead of aborting on the first, so one
run shows whether a single item or the whole set is broken. Deciding what
counts as broken lives in a filter plugin, because the distinction is
subtle enough to deserve tests: a module reports failure in msg, a command
in rc and stdout, an idempotent API answers "already exists" and means
success, and a job that cannot be read at all comes back marked finished
with no outcome, which would otherwise read as green.
Timing cost is roughly one to two minutes on a deploy measured in hours.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add tests/lint/repository/test_task_file_numbering.py: numbered task
files use exactly two leading digits, a numbered *_core.yml must be
00_core.yml, per directory the numbers are unique/consecutive and start
at 01 (00 with a core file), numbered includes appear in ascending
order, and shared helpers live in tasks/utils/ instead of the numbered
chain.
Apply it across every role: rename NN_core.yml to 00_core.yml with a
consecutive down-shift of the siblings, move letter-suffix children into
a subdirectory named after their parent task, relocate helpers included
from more than one place into tasks/utils/, and update every reference
(include_tasks, path_absolute, .ansible-lint-ignore, requirement docs).
Legacy narration comments in the touched files are deleted; the upstream
xwiki.properties reference block opts out via nocheck. The unused
MAILU_IP6_PUBLIC var (only ever named in now-removed comments) is dropped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>