The lockfile resolved 5.0.6, which carries unbounded intermediate arrays, unbounded expansion length and exponential-time expansion of consecutive empty groups. It reaches the tree through eslint via minimatch, so an override pins it rather than declaring a dependency this lint-only manifest does not have.
npm audit goes from 7 high severity vulnerabilities to none, and npm run lint still exits 0 with the same 179 pre-existing warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shellcheck SC2016 flagged the single-quoted 'bash -c' body. The fd form
of flock keeps the guard in the current shell, so ${MAKE:-make} expands
normally and the lock is released before the test run starts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both docker_root_free_bytes tests compared the result against a second
live disk_usage() call, so any concurrent write between the two samples
failed them — reliably under the parallel `make test` targets, never in
isolation. Mock the call and assert which path was measured, which is
what the tests are about.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Parallel `make test` targets each auto-ran `compose-up`, which rendered
into a fixed Corefile.tmp; the first replace() stole the file from the
others and surfaced as "envsubst did not produce an output file".
mkstemp per process plus a flock'd re-check in the test wrapper so only
one target brings the stack up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The gate probed the partner with container inspect on a bare name, which only resolves under compose co-location. In swarm GitLab sits on another node, the probe fails, and failed_when: false turns that into a silent skip: the deploy reports success while client_id/client_secret are never written, so the per-user Connect button leads nowhere. Nothing else writes those two values.
Mirrors integration_zammad: resolve node and container id first, wait for the partner endpoint, then run both execs against the resolved ids. Gate on IS_STACK_HOST like every other addon. The include had to become relative, or the stack-host lint cannot see the gate through a templated path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mastodon keeps no logout on the surface the persona lands on, so the helper gave up after two steps. Its trace carries three /settings/preferences links and no sign_out anywhere, and the universal-logout service that would inject a fallback stays passive because Mastodon authenticates natively rather than through oauth2-proxy.
The step runs only after both existing ones fail, follows the first visible same-origin settings link, and repeats the two searches there. Relative hrefs only, so it cannot leave the app; no /account match, which would hit Mastodon's own /accounts/ profile URLs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
clickOidcLoginLink only ever matched accessible names, so a role rendering its own login form beside the SSO entry won the match and the persona submitted the native form instead of reaching Keycloak. GitLab's sign-in button was clicked while form[action=/users/auth/openid_connect] and data-testid=oidc-login-button sat unused next to it; Mastodon's entry is named 'Login or Register', which no login regex matches.
The provider locator runs first with a 5s window and falls through to the existing strict/loose path, so roles without OIDC markup are unaffected. It requires the full protocol name with a separator rather than a bare 'openid' substring: the only href of that shape anywhere in roles/ is Mastodon's own login link.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The probe sends HEAD, and socialhome answers 405 there while GET / and GET /healthz both return 200, so the role was healthy and the swarm deploy still failed on three nodes after 25 retries. web-app-prometheus declares the same code for the same reason.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A Sign-in button matched ACCOUNT_MENU_NAME_RE, so inAppLogout clicked it as a menu trigger, and a username field matched the auth marker's data-testid rule, so an unauthenticated login page counted as an authenticated surface. The second one is why a broken auth chain surfaced as 'no in-app logout control reachable'.
Traces from CI run 31195451847 show the sign-in button clicked at CALL@123 and CALL@137, the marker returning true at ret@85, and the match resolving to input#user_login at @145 with no POST to /users/sign_in anywhere in the network log.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
extend_inventory._host_topology writes svc-swarm-node and
svc-storage-nfs-server into devices.yml unconditionally — without them
there is no swarm and no NFS export, so the throwaway cluster cannot
exist. Deriving the disable set from dynamic_services put their service
keys node and nfs-server on that same list, and the deploy aborted on
the provider-role branch of the consistency guard.
That branch never reads shared, so the force-shared fix cannot reach it:
with clean host_vars the guard still returns these two conflicts, and it
raises on any non-empty list.
Keep both keys instead of relaxing the guard — the guard is reporting a
real contradiction between the disable set and the lab topology, and the
disable set is the wrong side. roundtrip drives the same swarm-zombie
and extend_inventory path, so it needs the same keep-keys.
The keys are copied from _host_topology by hand; deriving them from it
would remove the duplication, at the cost of a helper this does not need
yet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The swarm drill provisions a fresh inventory each round: _provision runs
the services disabler, which writes enabled: false, shared: false for
every service in `disable=`. _force_shared_db then ran four seconds
later and flipped shared back to true, because it only ever looked at
shared and never at enabled.
Its key set comes from every flagged top-level key of the svc-db-* roles,
so it covers svc-db-mariadb's own consumer entries tor and
container_backup, both of which sit on the disable list. The result was
enabled=False, shared=True, and the deploy aborted on the consistency
guard in services_disabler.
Guard the flip on enabled instead of narrowing the key set: the key set
would only drop today's two keys, while postgres, redis, memcached,
qdrant, typesense and ldap are all equally on the live disable list.
`is not False` keeps flipping entries that carry no enabled key at all.
derive_includes mirrors force_shared_db by contract, so it gets the same
condition; otherwise the dependency walk sees a different topology than
the deploy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>