Commit Graph
14682 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 4.7 60d93e3986 chore(settings): allow raw.githubusercontent.com for WebFetch
Allows fetching upstream image Dockerfiles directly from GitHub raw
during triage; alphabetically sorted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 03:26:21 +02:00
kevinveenbirkenbachandClaude Opus 4.7 af4e4b6bea fix(web-app-peertube): cap plugin-install heap + retry on transient OOM
`npm run plugin:install` inherits the container-wide NODE_OPTIONS (~35%
of mem_limit, multi-GB) and pushes constrained CI runners over their
host RAM, getting SIGKILLed (rc=137) a few seconds in. Override
NODE_OPTIONS just for the install exec with a tight heap cap
(PEERTUBE_OIDC_PLUGIN_INSTALL_MAX_OLD_SPACE_MB, default 1024 MB) and
add 3 retries (15s delay) to absorb transient host memory pressure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 03:22:40 +02:00
kevinveenbirkenbachandClaude Opus 4.7 4cfe646110 fix(web-app-joomla): pin image tag + fail fast on git-tree refusal
The rolling `joomla:latest` tag has shipped a raw git source tree,
which makes `installation/joomla.php install` refuse to run with
"It looks like you are trying to run Joomla\! from our git repository."
That refusal exits rc=0, silently masking the failure; the next task
then trips on a missing configuration.php.

Pin to a stable release tag (5.4.5-php8.3-apache) and treat the
git-repository stdout as a hard failure so future tag drift surfaces
the real cause instead of a misleading downstream symptom.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 03:22:25 +02:00
kevinveenbirkenbachandClaude Opus 4.7 a3dda95461 fix(web-app-friendica): wait for bin/console before addon sync
The Friendica image copies /usr/src/friendica into the /var/www/html
volume via an entrypoint rsync. On fresh-purged deploys the addon sync
task raced that rsync and failed with `OCI runtime exec failed ...
"/var/www/html/bin/console": no such file or directory`. Add a polling
wait (60 retries / 2s) for the console binary so the role is resilient
to the rsync window regardless of image internals.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 03:22:06 +02:00
kevinveenbirkenbachandClaude Opus 4.7 76057fdc82 test(web-svc-cdn): add playwright e2e spec
Covers CSP header enforcement, canonical-domain surfacing, and landing
reachability. Passes against a reuse-kept deploy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 20:19:45 +02:00
kevinveenbirkenbachandClaude Opus 4.7 d95cc685d8 test(web-app-matrix): add playwright e2e spec
Covers CSP header enforcement, canonical-domain surfacing, the dashboard
→ Element OIDC flow for administrator and biber, and a multi-context
direct message exchange between administrator and biber.

The signIn helper walks Element's long-tail SSO states (rc_login rate
limit, rust_crypto "Skip verification", Synapse first-run consent page,
transient #/login bounce). The DM test joins biber before admin sends
the marker so the megolm key is shared for the live (not historical)
message. Full spec (4 tests) passes in ~17m against a reuse-kept deploy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 20:19:26 +02:00
kevinveenbirkenbachandClaude Opus 4.7 8e8122b165 test(web-app-bigbluebutton): add playwright e2e spec
Covers CSP header enforcement, canonical-domain surfacing, and the
dashboard → Greenlight OIDC login flow. Passes against a reuse-kept
deploy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 20:18:57 +02:00
kevinveenbirkenbachandClaude Opus 4.7 62a7356dcd chore(claude): broaden make allow shapes, per-domain WebFetch, deny git -C
Three related settings.json changes:

1. Replace narrow `Bash(*=* make *)` with three broader rules covering
   any prefix + make, plus composites with `> /tmp/*` redirect and
   `2>&1` stderr-merge. Needed so multi-env invocations with quoted
   multi-word values (e.g. `FOO=a BAR=b APPS="x y z" make target > /tmp/log 2>&1`)
   auto-allow without prompting — the prior single-env rule and the
   generic `* > /tmp/*` rules did not compose for this shape.

2. Replace inert `WebFetch(domain:*)` with explicit per-domain entries
   for `github.com` and `raw.githubusercontent.com`. Tested empirically:
   the wildcard form does not suppress the permission prompt, so a
   per-domain allowlist is the only mechanism that works.

3. Add `Bash(git -C *)` to deny. Blocks cross-repo escapes via the
   `-C <path>` flag, which otherwise lets the agent reach into other
   working trees under `additionalDirectories`. Operators needing a
   second repo must open a dedicated session there.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 20:15:30 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ce40ebd45b chore(settings): allow raw.githubusercontent.com for WebFetch
Allows fetching upstream image Dockerfiles directly from GitHub raw
during triage; alphabetically sorted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 19:43:00 +02:00
kevinveenbirkenbachandClaude Opus 4.7 d8a6560f3a docs(claude): forbid trailing & background operator in shell invocations
Add `&` to the explicit list of forbidden statement separators and
point operators at the Bash tool's run_in_background parameter as the
correct substitute.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 18:48:18 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9ffd118264 fix(skills): mirror .agents/skills to .claude/skills instead of symlink
The previous symlink approach left .claude/skills as a real empty dir in
some setups, which collides with the agent sandbox (denyWithinAllow on
.claude/skills expects a file path, not a directory) and breaks every
Bash invocation. Copy the installed skills tree into .claude/skills so
both locations are real, independent copies — no symlink involved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 18:32:20 +02:00
kevinveenbirkenbach a8df31d898 Merge branch 'main' into fix/free-disk-space/138 2026-04-18 18:14:07 +02:00
kevinveenbirkenbachandClaude Opus 4.7 433a4138dd feat(ci): recognize chore branch prefix
Treat chore/** branches like feature/fix/etc. for CI: trigger the
push pipeline on chore/** pushes and accept chore as a valid PR
branch prefix in full scope. This unblocks the chore/update-* PRs
that update.yml creates automatically, which previously failed
branch-prefix validation and never triggered the push pipeline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 17:11:28 +02:00
kevinveenbirkenbach 55a10a476b Merge pull request #15 from kevinveenbirkenbach/chore/update-docker-image-versions-20260418
chore(images): update Docker image versions
2026-04-18 17:00:06 +02:00
github-actions[bot] b2c203dd56 chore(images): update Docker image versions 2026-04-18 14:44:35 +00:00
kevinveenbirkenbachandClaude Opus 4.7 4f2dd95fe9 feat(ci): add CI_RUN_ON_MAIN gate, reorg actions docs
Add repository variable CI_RUN_ON_MAIN that gates whether pushes to
main trigger CI. When unset (default), pushes to main skip CI;
set to 'true' to enable. Pushes to other branches are unaffected.

Reorganize GitHub Actions docs into a dedicated tools/github/actions/
directory and consolidate every CI repository variable in a single
SPOT (configuration.md). Move actions.md to actions/workflows.md and
branch/configuration.md to actions/configuration.md, then update
every cross-reference. Trim README files to scope-only per
docs/contributing/artefact/files/docs/readme_md.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 16:31:52 +02:00
github-actions[bot] 05e3f352c7 chore(images): update Docker image versions 2026-04-18 13:23:45 +00:00
kevinveenbirkenbach 5d038faa6a Merge pull request #169 from kevinveenbirkenbach/fix/manjaro-pkgmgr-gpg/167
fix(install/arch): init pacman keyring before pacman -Syu (#167)
2026-04-18 15:19:48 +02:00
kevinveenbirkenbachandClaude Opus 4.7 c5190451bc fix(web-app-matrix): repair invalid Element config.json and dead-DNS hosts
`# nocheck: url` is not valid JSON — the markers leaked into the rendered
config.json and Element crashed with "Expected double-quoted property name
in JSON at position 536". Switch to the Jinja-comment form
`{# nocheck: url #}` which is still recognized by the URL reachability
checker (see tests/external/repository/test_urls_reachable.py) but is
stripped during template rendering.

Also revert the scalar-staging.*/matrix-client.* hostnames rewritten in
750c110f4 to non-existent `.element.io` variants. Those hosts had no DNS
records and the `nocheck` marker was silently hiding the breakage. Values
now match Element's upstream apps/web/config.sample.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 14:50:20 +02:00
kevinveenbirkenbach c6b0c2609d Merge branch 'main' into fix/users-applications.yml 2026-04-18 13:44:45 +02:00
kevinveenbirkenbach a5623e53e2 Merge branch 'main' into fix/free-disk-space/138 2026-04-18 13:05:17 +02:00
kevinveenbirkenbachandClaude Opus 4.7 b020cb77e6 chore(claude): restore ASCII-sorted order in permissions.allow
Swap two adjacent entries in `.claude/settings.json` `permissions.allow`
so `tests/lint/repository/test_claude_settings_sorted.py` passes:
`Bash(* > /tmp/* 2>&1)` must precede `Bash(* > /tmp/*)` because
ASCII space (0x20) < `)` (0x29).

Pre-existing breakage on main, committed with --no-verify (single-use
approval) so it can land independently of the unrelated Manjaro
keyring fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 12:34:23 +02:00
kevinveenbirkenbachandClaude Opus 4.7 6364b8949f fix(install/arch): init pacman keyring before pacman -Syu
Move `pacman-key --init` + `--populate` ahead of the first `pacman -Syu`
call in `build_and_install_arch`. The previous guard
`pacman-key --list-sigs` always returned 0 on a fresh Manjaro/Arch image,
so the keyring init never ran and the upgrade of `archlinux-keyring`
failed with `==> ERROR: There is no secret key available to sign with.`,
breaking the manjarolinux/base test-environment matrix entry.

Refs: https://github.com/infinito-nexus/core/issues/167

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 12:28:35 +02:00
kevinveenbirkenbachandClaude Opus 4.7 c86d3a531f chore(claude): restore ASCII-sorted order in permissions.allow
Swap two adjacent entries in `.claude/settings.json` `permissions.allow`
so `tests/lint/repository/test_claude_settings_sorted.py` passes:
`Bash(* > /tmp/* 2>&1)` must precede `Bash(* > /tmp/*)` because
ASCII space (0x20) < `)` (0x29).

Pre-existing breakage on main, committed with --no-verify (single-use
approval) so it can land independently of the unrelated Manjaro
keyring fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 12:12:00 +02:00
kevinveenbirkenbachandClaude Opus 4.7 1de1bf9b1d chore(claude): allow env-prefixed make, add nektosact domain
Add `Bash(*=* make *)` so `FOO=bar make <target>` invocations (notably
`make act-workflow` with ACT_WORKFLOW/ACT_MATRIX overrides) auto-allow
without prompting — mirrors the existing `PYTHONPATH=* python *` entry.
Add `*.nektosact.com` to sandbox network allowedDomains so `act` (the
local GitHub Actions runner invoked by `make act-workflow`) can reach
its update/catalog endpoint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 12:00:12 +02:00
kevinveenbirkenbachandClaude Opus 4.7 a7769f7b43 chore(claude): allow env-prefixed make, add nektosact domain
Add `Bash(*=* make *)` so `FOO=bar make <target>` invocations (notably
`make act-workflow` with ACT_WORKFLOW/ACT_MATRIX overrides) auto-allow
without prompting — mirrors the existing `PYTHONPATH=* python *` entry.
Add `*.nektosact.com` to sandbox network allowedDomains so `act` (the
local GitHub Actions runner invoked by `make act-workflow`) can reach
its update/catalog endpoint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 11:59:44 +02:00
kevinveenbirkenbachandClaude Opus 4.7 980ab3de5e chore(claude): grant gh cache write, generic /tmp redirect allow
Add ~/.cache/gh to sandbox allowWrite so gh CLI no longer needs an
XDG_CACHE_HOME=/tmp/... prefix, and replace the five XDG-prefixed allow
entries with two generic `* > /tmp/*` redirect entries that cover any
command capturing output into /tmp. Sync sandbox.md / settings.md /
security.md and normalize the allowWrite path enumerations.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 11:25:16 +02:00
kevinveenbirkenbachandClaude Opus 4.7 d74b2bf5f7 chore(claude): grant gh cache write, generic /tmp redirect allow
Add ~/.cache/gh to sandbox allowWrite so gh CLI no longer needs an
XDG_CACHE_HOME=/tmp/... prefix, and replace the five XDG-prefixed allow
entries with two generic `* > /tmp/*` redirect entries that cover any
command capturing output into /tmp. Sync sandbox.md / settings.md /
security.md and normalize the allowWrite path enumerations.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 11:23:53 +02:00
kevinveenbirkenbach 8c41f40c7a Merge branch 'main' into fix/free-disk-space/138 2026-04-18 10:54:26 +02:00
kevinveenbirkenbachandClaude Opus 4.7 cc6e77b33f fix(nextcloud): clear appstore-fetcher lastFailure before each install
The appstore-fetcher caches a 300s failure cooldown on any fetch
error. With retries:6/delay:30 the whole 180s retry window stayed
inside one cached-empty catalog, so plugins that transiently 404
('not found on the appstore') never recovered. Clearing
settings/appstore-fetcher-lastFailure before each attempt forces a
fresh catalog fetch on retry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 09:26:10 +02:00
kevinveenbirkenbachandClaude Opus 4.7 f9c40f70a9 fix(bigbluebutton): wait for psql + retry ALTER USER after initdb
Post-force-recreate, postgres runs initdb and starts/stops a transient
internal server. pg_isready passes on that transient state, but the
ALTER USER 0.3s later hits 'FATAL: the database system is shutting
down'. Added a SELECT 1 readiness probe and a retry loop on the
password sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 09:25:18 +02:00
kevinveenbirkenbachandClaude Opus 4.7 064e1a389f fix(discourse): add rebuild retry + keycloak logout in playwright
- handlers: retries:3/delay:60 so launcher rebuild self-heals on
  transient git/pull hiccups.
- playwright: also hit keycloak's /protocol/openid-connect/logout
  and clear cookies after /session/destroy; otherwise the admin
  persona silently re-SSOs and fails 'require a new sign-in'.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 09:23:57 +02:00
kevinveenbirkenbachandClaude Opus 4.7 316ae898c7 fix(sys-svc-container): clamp TCP MSS to PMTU in DinD deployer
Round-6 reuse-kept deploy validated. Without this, the Discourse
launcher rebuild spawned by the deployer's nested docker daemon
dies on 'Recv failure: Connection reset by peer' from github.com:
the rebuild container's bridge is 1500 MTU but the deployer's eth0
is automtu-picked (1400), and outer Docker swallows ICMP Frag
Needed, yielding a PMTU blackhole.

Idempotent iptables command (mangle FORWARD, SYN), since
ansible.builtin.iptables does not expose --clamp-mss-to-pmtu.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 09:20:08 +02:00
kevinveenbirkenbachandClaude Opus 4.7 b340a41bba chore(claude): allow *.nextcloud.com for NC appstore probing
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 09:17:16 +02:00
kevinveenbirkenbach da7a84ab7e Merge branch 'infinito-nexus:main' into fix/free-disk-space/138 2026-04-18 02:12:24 +02:00
kevinveenbirkenbach 1e0cab4952 Merge branch 'main' into fix/users-applications.yml 2026-04-18 01:18:24 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ada7c4b5ae fix(nextcloud): fail loudly on unsupported / unavailable plugins
Remove silent tolerance for `occ app:install` errors — both
"not compatible with this version of the server" and
"not found on the appstore" now propagate as task failures.

Rationale: these are config or upstream issues (typo, removed app,
NC-version mismatch), not transients. 6× 30s retries still absorb
real flakes; residual failures deserve to surface, not to ship a
"deployment" that silently lacks features the config lists.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 00:48:52 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9447075d54 revert(load_app): restore vars block for application_id + domain
The removal in 918c49add had no practical effect on matomo's resolution
path (role-vars `domain: "{{ lookup('domain', application_id) }}"` was
already honored via include_tasks vars `application_id: 'web-app-matomo'`),
but other roles rely on these two vars being set at include_role level.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 00:36:03 +02:00
kevinveenbirkenbachandClaude Opus 4.7 918c49add1 perf(lookups): cache applications_current_play + tag templar inputs
Speed up per-play lookups that previously re-read YAML and re-templated
against a cold Templar on every invocation:

- applications_current_play: cache the rendered applications map keyed
  on the current play's identity so the 28+ plugin install calls reuse
  one resolved result.
- runtime_lookup_data / load_app: memoize the raw applications load and
  reuse it from the templar path instead of rereading group_vars.
- templating: merge caller-supplied variables onto templar's existing
  available_variables (keeping ansible_facts / hostvars visible) and
  tag the input with TrustedAsTemplate so Ansible 2.19+ actually
  renders {{ ... }} instead of returning it unchanged.

Tests: add integration perf test + unit regression guarding the
available_variables merge restores prev state after the call.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 00:22:06 +02:00
kevinveenbirkenbachandClaude Opus 4.7 30151a95c0 fix(discourse): point OIDC logout redirect at web-svc-logout
Previously the OIDC RP-initiated logout redirect landed back on Discourse
itself (application_id), which only ends the local session and leaves
other SSO apps signed in. Route it through the central
`web-svc-logout` service instead, mirroring how Keycloak already wires
its FRONTCHANNEL_LOGOUT_URL, so OIDC logout terminates all cross-app
sessions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 00:17:51 +02:00
kevinveenbirkenbachandClaude Opus 4.7 16b14e458c style(nextcloud): reformat dynamic memory_limit/opcache expressions
Pure whitespace + comment adjustment: add spaces around the `| int`
filter pipes and pull the explanatory comment to its own line instead of
trailing. No functional change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 00:14:57 +02:00
kevinveenbirkenbachandClaude Opus 4.7 745a1330b7 feat(ipv6): centralize IPv6 toggle via NETWORK_IPV6_ENABLED
Introduce a single global flag in group_vars/all/10_networks.yml and wire
the previously hardcoded IPv6 settings in nextcloud, bigbluebutton, and
matrix through it. Matches the host-level `make disable-ipv6` policy so
containers no longer stall on AAAA-first lookups (e.g. Nextcloud's
DnsPinMiddleware) while keeping a single knob to flip when v6 is wanted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 00:10:20 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9e0a7778e0 chore(claude): generalize gh run download, gate update-config via ask
Replace the one-off literal allow entry for a specific `gh run download`
invocation with the `XDG_CACHE_HOME=* gh run download *` wildcard that
mirrors the existing `view`/`list` env-prefix precedents, and move the
`Skill(update-config*)` wildcard from `allow` to `ask` so settings-file
mutations (permission rules, hooks, sandbox scope) always prompt — the
skill can otherwise self-escalate the agent's own permission surface.
Document both entries in settings.md with matching Env-Prefix and
Privilege-Escalation rationales.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 23:50:34 +02:00
kevinveenbirkenbach e32fb87f95 chore(claude): allow ~/.ssh read so git push works on ask
Removes ~/.ssh from sandbox.filesystem.denyRead so SSH auth (keys, known_hosts, config) succeeds inside the sandbox. Bash(git push*) stays in ask, which becomes the effective control point for pushes.

Documents the trade-off:
- security.md: updates Assumption 9 and adds Assumption 10 covering why ~/.ssh is readable, the cost, and the mitigations (passphrase-protected keys, key rotation on incident, settings.local.json opt-out, SSH-agent-socket alternative).
- sandbox.md: removes ~/.ssh from the Read Restrictions table and links back to Assumption 10.
2026-04-17 23:20:15 +02:00
kevinveenbirkenbach 5b7abda98a Merge branch 'main' into fix/free-disk-space/138 2026-04-17 23:02:07 +02:00
kevinveenbirkenbach 6eeef0de30 docs(tools): move actions.md into tools/github/ and fix refs
Moves the GitHub Actions workflow catalog under tools/github/ alongside the other GitHub-scoped docs, and updates the internal workflow/artefact links plus the two external references in workflow.md and pipeline.md.
2026-04-17 23:00:25 +02:00
kevinveenbirkenbach 375119b414 docs(agents): clarify push/PR flow, target repo, run-id resolve
- pull-request.md: PRs must target canonical repo behind s.infinito.nexus/code; agent resolves redirect and compares against origin.
- push-trigger-pull.md: freeze <branch> once (propose current and wait if unnamed); forbid checkout/switch; handle non-fast-forward; fetch before diff; define distro-specific scope; capture <prev-run-id> to avoid stale-run watch; resolve <pr-number> via isDraft dispatch; null-guard.
- cheatsheet.md: push-trigger-pull prompt takes explicit <branch> placeholder.
2026-04-17 22:54:13 +02:00
kevinveenbirkenbach b984715627 docs(agents): clarify push/PR flow, target repo, run-id resolve
- pull-request.md: PRs must target canonical repo behind s.infinito.nexus/code; agent resolves redirect and compares against origin.
- push-trigger-pull.md: freeze <branch> once (propose current and wait if unnamed); forbid checkout/switch; handle non-fast-forward; fetch before diff; define distro-specific scope; capture <prev-run-id> to avoid stale-run watch; resolve <pr-number> via isDraft dispatch; null-guard.
- cheatsheet.md: push-trigger-pull prompt takes explicit <branch> placeholder.
2026-04-17 22:52:49 +02:00
kevinveenbirkenbach c4563fe500 test(web-app-dashboard): stabilize stretched-link hover check
expectStableCardHover cached the card bounding box once, then moved
the mouse to 3 cached coordinates while the card could still reflow
as SimpleIcons, logout.js, iframe.js and oidc.js streamed in. The
first hover point then landed off the settled card and the overlay
:hover poll timed out.

Wait for the card bbox to stabilize across three samples, scroll it
into view, let the network go idle, and recompute the hover point
inside expect.poll so an outdated tick retries against a fresh box.
Verified flake-free with --repeat-each=20 against the live dashboard.
2026-04-17 22:16:07 +02:00
kevinveenbirkenbach b1a11cbebc fix(web-app-joomla): tolerate installer folder removal failure
Joomla 5.x CLI installer occasionally fails to delete
/var/www/html/installation after a successful install (non-fatal
cleanup step). Treat "Unable to delete installation folder" as
success and force-remove the folder as root so subsequent runs see
a configured, non-stuck install state.
2026-04-17 22:15:50 +02:00