a90e06ae705f19fd6f050016a5d8c3f79f1c58eb
244
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a90e06ae70
|
Key the apt cache per PHP version and drop the instrumentation
CI / No Debug Code (pull_request) Successful in 26s
CI / Tests (PHP 8.1) (pull_request) Successful in 2m35s
CI / Tests (PHP 8.2) (pull_request) Successful in 2m35s
CI / Coding Standards & Static Analysis (pull_request) Successful in 4m1s
CI / Tests (PHP 8.3) (pull_request) Failing after 7m17s
CI / Build Plugin Zip (pull_request) Skipped
Run 536 proved the cache works -- ACTIONS_CACHE_URL is now populated, no GHES warning, "Cache saved with key: apt-php-builder-deps-ARM64-v1" -- and in doing so showed the key was wrong. The jobs that saved it were 8.1 and 8.2. They take the ondrej PPA path, a handful of runtime packages, while 8.3 takes php-builder and its ~70 -dev packages. One shared key therefore lets whichever job finishes first decide what every other job restores, and 8.1 is always first, at ~30s against 8.3's several minutes. 8.3 would have restored a few runtime .debs it has no use for and then downloaded all 70 anyway. My "the dep list is the same for every PHP version" comment was true only among the builder versions. Keyed per version now, so each path caches what it actually installs. That also picks up a small win on 8.1 and 8.2 rather than only avoiding harm. Both temporary steps are gone. Report restored .debs was there to show the cache URL arriving and the restore landing; both are established, so it goes. Keep downloaded .debs stays -- it is not instrumentation, it disables Ubuntu's docker-clean, without which there are no .debs left to cache. Refs #178. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
d5f6ebf0b5
|
Move actions/cache to v4 and probe the cache URL
thatguygriff/infra#1 turned the runner cache server on, so actions/cache has an endpoint for the first time. v4 rather than v3 because v4.2+ can speak the cache service v2 API, which is what the runner serves. The instrumentation step now also prints ACTIONS_CACHE_URL, so a single run shows the endpoint arriving and the cache being read in one log rather than needing a separate probe. Refs #178. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
4b4b2453ae
|
Cache the .debs php-builder installs, to measure whether it helps
Experiment for #178. On self-hosted runners setup-php installs PHP 8.3+ through php-builder, whose install.sh apt-installs ~70 -dev packages before unpacking the build. The build tarball itself is only 19MB, so that apt work is the whole cost, not the download. Ubuntu's image drops the .debs after install, so every job fetches them from the archive again. This keeps them and restores them through the cache server, which lives in the cluster, so a WAN download becomes a local one. The dependency list does not vary by PHP version, so a single key serves 8.3 and 8.5 and the quality and build jobs alike. Only the .debs are cached. /var/lib/apt/lists is deliberately left alone, since a stale index is how apt starts 404ing mid-install, and reducing flakiness is the entire point. The Report restored .debs step is temporary instrumentation to show whether the cache is actually being read. Baseline to beat, from run 526: 8.1 30s, 8.2 53s, 8.3 454s, quality 151s, 8.5 733s and a hard failure. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
7f10769330 |
Merge pull request 'Fold PHPCS and PHPStan into one job' (#179) from ci/consolidate-quality-job-and-php-85 into main
CI / Tests (PHP 8.2) (push) Canceled after 1s
CI / Tests (PHP 8.1) (push) Successful in 34s
CI / No Debug Code (push) Successful in 27s
CI / Build Plugin Zip (push) Skipped
CI / Coding Standards & Static Analysis (push) Successful in 10s
CI / Tests (PHP 8.3) (push) Canceled after 15s
Reviewed-on: #179 |
||
|
|
f6481d4a3f
|
Hold PHP 8.5 until the runner serves a cache
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards & Static Analysis (pull_request) Successful in 3m37s
CI / Tests (PHP 8.3) (pull_request) Successful in 13m2s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 1m21s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m22s
8.5 lands on setup-php's php-builder path, same as 8.3, so it pays for ~70 apt -dev packages on every run. It timed out in run 526 and passed in 527 on identical code, which is a coin toss, and merging it would mean intermittent red for a version nothing ships on yet. The code is fine on 8.5 — verified locally on 8.5.9: 915 tests, PHPStan and PHPCS clean, check-platform-reqs satisfied. This is purely about the runners, so 8.5 comes back once #178 is fixed and the matrix is cheap again. This PR is now just the PHPCS/PHPStan consolidation. Refs #178. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
43b903c1c8
|
Revert the v4 cache retry: the gate stays shut, so it is the runner
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m3s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards & Static Analysis (pull_request) Successful in 3m27s
CI / Tests (PHP 8.3) (pull_request) Successful in 6m47s
CI / Tests (PHP 8.5) (pull_request) Failing after 12m17s
CI / Build Plugin Zip (pull_request) Skipped
Tested and the hypothesis is dead. actions/cache@v4 resolved properly (SHA 0057852) and printed the same GHES warning as v3, so the action version was not what was closing the gate. That points at the runner rather than the action. Gitea's docs say the runner patches the GHES check out of the action's bundle only when it recognises it, and that a bundle it does not recognise "is left alone". No patching for either v3 or v4, and no ACTIONS_CACHE_URL to patch it towards, is what you get when cache is simply off in the runner config. So the fix is cache.enabled in act_runner's config.yaml, with host set to an address job containers can reach. Nothing in this repository unblocks it, and inert steps are what let the Composer cache rot unnoticed, so the experiment comes out again. Both halves — the apt cache and the move to v4 — should land together once the runner serves a cache. Refs #178. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
d5eb2764a3
|
Retry the apt cache on actions/cache@v4
CI / Tests (PHP 8.1) (pull_request) Successful in 55s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards & Static Analysis (pull_request) Successful in 3m15s
CI / Tests (PHP 8.3) (pull_request) Successful in 4m4s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m5s
CI / Tests (PHP 8.5) (pull_request) Failing after 12m14s
CI / Build Plugin Zip (pull_request) Skipped
My last conclusion was wrong. The GHES warning is not evidence that the runner has no cache server; it is actions/cache v3 disabling itself. v3's isGhes() reads GITHUB_SERVER_URL, and anything that is not github.com reads as GitHub Enterprise, so on Gitea it always trips and the action returns before touching the cache. That explains the 0.2s save perfectly well without any runner setting being off. Gitea's runner 3.0.0 release notes say every runner starts its own cache server and that the runner "patches action bundles at load time to open the GHES gate and read the cache endpoint from ACTIONS_CACHE_URL", with actions/cache supported unforked. Our runners report v3.0.0, so the server should be there and the gate should be open — but the patching evidently does not reach a v3 bundle. So this reapplies the apt cache and moves every actions/cache to v4. If the hypothesis holds the GHES warning disappears, the save step actually takes time, and a second run restores the .debs. The Composer cache gets the bump too, since it has been silently doing nothing for the same reason. Refs #178. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
dd31afcd06
|
Revert the apt cache experiment: the cache service is switched off
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m2s
CI / Tests (PHP 8.5) (pull_request) Successful in 3m55s
CI / Tests (PHP 8.2) (pull_request) Successful in 6m0s
CI / Tests (PHP 8.3) (pull_request) Successful in 4m59s
CI / Coding Standards & Static Analysis (pull_request) Failing after 12m16s
CI / Build Plugin Zip (pull_request) Skipped
Measured on run 527 and it cannot work. Every actions/cache step on these runners prints Cache action is only supported on GHES version >= 3.5 ... check with GHES admin if Actions cache service is enabled or not and then no-ops. The save step finishes in 0.19-0.31s, which is not a few hundred megabytes of .debs going anywhere. setup-php timings were unchanged against the run 526 baseline, within the usual variance: 8.3 454s then 148s, 8.5 733s then 446s, both noise rather than signal. The same warning appears on the Composer cache this workflow has carried all along, including run 523 and earlier, so that step has never cached anything either. Worth fixing, but in the runner config rather than here. Leaving dead steps in the workflow is how the Composer cache went years without anyone noticing it did nothing, so the experiment comes out until act_runner has its cache service enabled. It is in the history when that happens. Detail in #178. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
85c7a01939
|
Cache the .debs php-builder installs, to measure whether it helps
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m7s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m56s
CI / Coding Standards & Static Analysis (pull_request) Successful in 3m35s
CI / Tests (PHP 8.5) (pull_request) Successful in 12m53s
CI / Build Plugin Zip (pull_request) Skipped
Experiment for #178. On self-hosted runners setup-php installs PHP 8.3+ through php-builder, whose install.sh apt-installs ~70 -dev packages before unpacking the build. The build tarball itself is only 19MB, so that apt work is the whole cost, not the download. Ubuntu's image drops the .debs after install, so every job fetches them from the archive again. This keeps them and restores them through the cache server, which lives in the cluster, so a WAN download becomes a local one. The dependency list does not vary by PHP version, so a single key serves 8.3 and 8.5 and the quality and build jobs alike. Only the .debs are cached. /var/lib/apt/lists is deliberately left alone, since a stale index is how apt starts 404ing mid-install, and reducing flakiness is the entire point. The Report restored .debs step is temporary instrumentation to show whether the cache is actually being read. Baseline to beat, from run 526: 8.1 30s, 8.2 53s, 8.3 454s, quality 151s, 8.5 733s and a hard failure. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
bc046ec2a1
|
Fold PHPCS and PHPStan into one job and test PHP 8.5
CI / Tests (PHP 8.1) (pull_request) Successful in 57s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m17s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards & Static Analysis (pull_request) Successful in 3m26s
CI / Tests (PHP 8.3) (pull_request) Successful in 13m1s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.5) (pull_request) Failing after 12m16s
The two jobs were identical up to their final step, each paying for its own Setup PHP. That step is the flaky one (#178), so running it twice to reach two short commands was two chances for a run to fall over instead of one. They are now steps in a single Coding Standards & Static Analysis job. The one thing given up is that PHPCS failing now stops the job before PHPStan reports, where before the two ran in parallel and both spoke. That seemed a fair trade for halving the exposure, and the fix for a PHPCS failure rarely depends on knowing PHPStan's verdict at the same time. PHP 8.5 joins the test matrix. composer.json already allows it at >=8.1 and the suite passes on 8.5.9 locally: 915 tests, PHPStan and PHPCS clean, check-platform-reqs satisfied. 8.4 is deliberately not added, only 8.5. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
b814ae34b4 |
Merge pull request 'Stop authenticating setup-php against the GitHub API' (#177) from ci/drop-github-token-from-setup-php into main
CI / Coding Standards (push) Successful in 3m7s
CI / Tests (PHP 8.1) (push) Successful in 2m11s
CI / Tests (PHP 8.2) (push) Successful in 1m6s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 6m42s
CI / Tests (PHP 8.3) (push) Successful in 12m55s
CI / Build Plugin Zip (push) Successful in 2m45s
Reviewed-on: #177 |
||
|
|
9071a3f70f
|
Stop authenticating setup-php against the GitHub API
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.1) (pull_request) Successful in 56s
CI / Tests (PHP 8.2) (pull_request) Successful in 57s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m56s
CI / Coding Standards (pull_request) Successful in 7m5s
CI / Build Plugin Zip (pull_request) Skipped
CI / PHPStan (pull_request) Successful in 8m5s
The GitHub API rate limit was the wrong diagnosis, so this reverts the 1Password-backed token added in #175 along with the mirrored composite action, leaving the workflows as they were. Timing every Setup PHP step across runs 454-523 rules the rate limit out. PHP 8.1 and 8.2 install in 26-41 seconds, 12 for 12, never once failing. PHP 8.3 has never finished in under 143 seconds and ranges up to 1273, with two outright failures. Those jobs share a fan-out, and so an egress address and a rate limit bucket, with the 8.1 and 8.2 jobs that are never touched. A throttle could not sort itself by PHP version that way. Run 523, the first to carry the token, is the direct refutation: the token resolved and verified, and Setup PHP still took 749 seconds on kallone and 408 on eris. The 8.3 penalty also predates the whole story, sitting at ~145 seconds back on 30 July. What is left is a slow path specific to 8.3 on these arm64 runners, whose long tail sometimes crosses the step timeout and reports the unhelpful "Could not setup PHP 8.3". That is worth fixing on its own terms rather than behind a token that was never in the path. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks |
||
|
|
170d7e6c21 |
Merge pull request 'Authenticate setup-php against the GitHub API' (#175) from ci/github-token-for-setup-php into main
CI / Tests (PHP 8.2) (push) Successful in 57s
CI / Tests (PHP 8.1) (push) Successful in 1m0s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 3m10s
CI / PHPStan (push) Successful in 7m19s
CI / Tests (PHP 8.3) (push) Successful in 12m53s
CI / Build Plugin Zip (push) Successful in 2m45s
Reviewed-on: #175 |
||
|
|
aa3dd13775 |
Merge pull request 'Bump version to 1.5.3' (#176) from release/bump-1.5.3 into main
CI / PHPStan (push) Successful in 3m6s
CI / Tests (PHP 8.1) (push) Successful in 1m9s
CI / Tests (PHP 8.2) (push) Successful in 1m10s
CI / Tests (PHP 8.3) (push) Successful in 7m52s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Failing after 12m16s
CI / Build Plugin Zip (push) Skipped
Reviewed-on: #176 |
||
|
|
164c8ebf97 | Bump version to 1.5.3 and open changelog section | ||
|
|
1291af0b72
|
Authenticate setup-php against the GitHub API
CI / PHPStan (pull_request) Successful in 6m52s
CI / Tests (PHP 8.1) (pull_request) Successful in 6m0s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m11s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m55s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 7m6s
CI / Build Plugin Zip (pull_request) Skipped
setup-php resolves its tools through the GitHub API, unauthenticated at 60 requests an hour per source address. A CI fan-out across the fleet exhausts that bucket, and the step then retries for several minutes before reporting only "Could not setup PHP 8.3". It reads as a hang rather than a throttle, and it took out both a main CI run and a release build. Each cluster has its own egress address and so its own bucket, which is why the same job passed on one runner and failed on another in the same minute. The token comes from 1Password through the Connect instance in whichever cluster picked up the job, matching the pattern in thatguygriff/infra. That repository's composite action is not reachable from here, so it is mirrored locally. It stays a step output rather than being exported to the job environment, to keep it away from the package scripts composer install runs. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0133tYSQoZhoKebKZV8o2GPs |
||
|
|
78083fc96c |
Merge pull request 'Let a studio choose its default payment method, and disconnect Stripe again' (#174) from feature/173-default-payment-method into main
CI / PHPStan (push) Successful in 8m6s
CI / Tests (PHP 8.1) (push) Successful in 1m6s
CI / Tests (PHP 8.2) (push) Successful in 54s
CI / Tests (PHP 8.3) (push) Successful in 6m48s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 5m55s
CI / Build Plugin Zip (push) Successful in 2m51s
Release / Build and Publish Release (push) Successful in 16m35s
Release / Open next-version bump PR (push) Successful in 4s
Reviewed-on: #174v1.5.2 |
||
|
|
c077a653fb
|
Let a studio pick its default payment method and disconnect Stripe
CI / No Debug Code (pull_request) Successful in 5s
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / Tests (PHP 8.1) (pull_request) Successful in 56s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / PHPStan (pull_request) Successful in 3m48s
CI / Tests (PHP 8.3) (pull_request) Successful in 5m31s
CI / Build Plugin Zip (pull_request) Skipped
Stripe configuration was a one-way door. Keys could be entered but never removed, and entering them moved every student onto card billing at once, so there was no way to have Stripe live and satisfy yourself that card payments worked before committing the studio to them. Two settings-page changes open both directions: Default payment method (`us_default_payment_method`) is now an explicit choice between card and e-transfer for students with no per-student override, rather than something inferred from whether keys exist. Card remains the default, so a site that adds keys and changes nothing else behaves as before. BillingMethodResolver still degrades a card default to e-transfer while Stripe is unconfigured — there is nothing to charge a card with — and `comp` is deliberately not offerable studio-wide, since it would silently stop billing everybody; an unrecognised stored value reads back as card. Holding the default on e-transfer with Stripe live is the staged-rollout path: move individual students to card on their detail page, watch real charges land, then flip the studio over. Clear Stripe configuration deletes the publishable key, secret key and webhook signing secret and returns the mode to test, so a re-configuration later cannot inherit live. Currency, HST, e-transfer and registration settings are untouched, as are recorded payments. The button only appears when some Stripe value is stored, and reuses the page's existing nonce and `manage_billing` check. `composer test` (915), `composer lint` and `composer cs` all pass. Options only — no schema change, so no version bump. Closes #173 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WyktWmwNRgMYuwe5eBuPZm |
||
|
|
f9e222be29 |
Merge pull request 'Bump version to 1.5.2' (#172) from release/bump-1.5.2 into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 46s
CI / Tests (PHP 8.2) (push) Successful in 1m4s
CI / Tests (PHP 8.3) (push) Successful in 2m43s
CI / Coding Standards (push) Successful in 3m40s
CI / PHPStan (push) Successful in 3m43s
CI / Build Plugin Zip (push) Successful in 2m51s
Reviewed-on: #172 |
||
|
|
b950e35e5a | Bump version to 1.5.2 and open changelog section | ||
|
|
b220de48c5 |
Merge pull request 'Let a guardian enrol every child in the same group class' (#171) from fix/group-multi-student-enrolment into main
CI / No Debug Code (push) Successful in 4s
CI / Tests (PHP 8.1) (push) Successful in 1m4s
CI / Tests (PHP 8.2) (push) Successful in 1m10s
CI / Coding Standards (push) Successful in 3m35s
CI / PHPStan (push) Successful in 3m52s
CI / Tests (PHP 8.3) (push) Successful in 21m42s
CI / Build Plugin Zip (push) Successful in 14m57s
Release / Build and Publish Release (push) Successful in 3m11s
Release / Open next-version bump PR (push) Successful in 5s
Reviewed-on: #171v1.5.1 |
||
|
|
8017dbb9ff
|
Let a guardian enrol every child in the same group class
CI / PHPStan (pull_request) Successful in 2m55s
CI / No Debug Code (pull_request) Successful in 26s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m5s
CI / Coding Standards (pull_request) Successful in 3m49s
CI / Tests (PHP 8.3) (pull_request) Successful in 6m9s
CI / Build Plugin Zip (pull_request) Skipped
The group-class page matched enrolments to the account instead of to the student: the lookup it built from GET /enrollments was keyed by offering id alone, so the first household enrolment marked the class as "yours" and took the Enrol button away from everyone else on the account. A parent could enrol one child and was then offered nothing but Withdraw. The server was never the constraint — hasActiveEnrollment() checks the (offering, student) pair and GET /enrollments deliberately returns the whole household — so the fix is to stop discarding student_id on the way in. Active enrolments are now grouped per class as a list, each student gets their own "… is enrolled in this class." line and their own named Withdraw button, and the Enrol button stays (as "Enrol another student") while anyone the account may enrol is still out. The enrolment form offers only the students not yet enrolled. When exactly one of them is left the picker collapses, and that case needed care: an omitted student_id reads as "enrol the account holder" server-side, so a hidden field carries the id rather than posting nothing and signing up the parent instead of the last child. Closes #170 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Jy9UPhmpLUAfimsyecHN2Z |
||
|
|
c73b10d779 |
Merge pull request 'Bump version to 1.5.1' (#169) from release/bump-1.5.1 into main
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 55s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m52s
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.3) (push) Successful in 2m47s
CI / Build Plugin Zip (push) Successful in 2m51s
Reviewed-on: #169 |
||
|
|
7ea8d653ee | Bump version to 1.5.1 and open changelog section | ||
|
|
1e4e21e8d3 |
Merge pull request 'Show a recurring lesson's policy acceptances and intake answers on every week of it' (#168) from fix/167-series-policy-acceptances into main
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / No Debug Code (push) Successful in 1s
CI / PHPStan (push) Successful in 2m54s
CI / Coding Standards (push) Successful in 2m58s
CI / Tests (PHP 8.3) (push) Successful in 2m43s
Release / Build and Publish Release (push) Successful in 2m58s
Release / Open next-version bump PR (push) Successful in 4s
CI / Build Plugin Zip (push) Successful in 2m46s
Reviewed-on: #168v1.5.0 |
||
|
|
df3462a8b3
|
Show a series' policy acceptances on every occurrence
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m54s
CI / Coding Standards (pull_request) Successful in 2m59s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m43s
CI / Build Plugin Zip (pull_request) Skipped
A weekly booking reserves a series of lessons, but the student answers the intake and ticks the policy boxes once — so BookingEndpoint records both against the anchor lesson alone. The admin detail view looked them up by whichever lesson id was being viewed, so every occurrence after the first showed no answers and no acceptances at all. LessonDetail now takes the Lesson rather than a bare id and resolves the registration to `series_id ?? id`, so each occurrence reads the anchor's records. This is the same seam PaymentService already uses to find a series lesson's payment on the anchor. Nothing was ever missing from the database, so existing bookings read correctly with no migration and no schema change. Closes #167 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
748478f2f1 |
Merge pull request 'Let the account holder edit their own details on the profile page' (#166) from feature/165-editable-own-profile into main
CI / Tests (PHP 8.1) (push) Successful in 46s
CI / Tests (PHP 8.2) (push) Successful in 55s
CI / No Debug Code (push) Successful in 3s
CI / PHPStan (push) Successful in 2m57s
CI / Coding Standards (push) Successful in 3m3s
CI / Tests (PHP 8.3) (push) Successful in 2m48s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #166 |
||
|
|
f97b8a4576
|
Let the account holder edit their own profile details
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 2m57s
CI / Coding Standards (pull_request) Successful in 3m3s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
The Profile block is headed "Your profile", but the one person on it you could not change was yourself: your name, your birth year, and whether you take lessons yourself were fixed at whatever signup recorded, and correcting any of them meant asking a studio admin. A "Your details" section now opens the page, saved through the same nonce-checked template_redirect post/redirect/get path the child rows use: - Your name, written to display_name and nickname together, for the reason updateChild() does — UserName reads the nickname first, and leaving it behind would put the account's email address back on every screen that names a person. - "I take lessons myself", the positive of us_guardian_only. This makes good on the claim already in bookableStudents() and the feature doc that a guardian-only account can put itself right from the profile page. - Your birth year, held to the same normaliseBirthYear() rule as every other student. The email is shown but not editable: it is the account's user_login as well as its address, so changing it stays a studio-side job. The birth-year field deliberately carries no `required` attribute. It is asked of a student only, and this page loads no JavaScript, so a browser-enforced `required` would leave a guardian who books solely for other people unable to submit the form at all; handleSelf() enforces it against the checkbox instead. Unticking the box does not clear a stored birth year — it says who books, not "forget what is on file". Closes #165 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
325a86f247 |
Merge pull request 'Ask some registration questions of students only, and require them of students without requiring them of the account holder' (#164) from feature/student-scoped-registration-questions into main
CI / Tests (PHP 8.2) (push) Successful in 57s
CI / Build Plugin Zip (push) Successful in 2m49s
CI / Tests (PHP 8.1) (push) Successful in 58s
CI / No Debug Code (push) Successful in 3s
CI / Coding Standards (push) Successful in 2m50s
CI / PHPStan (push) Successful in 2m59s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
Reviewed-on: #164 |
||
|
|
434fe801ba
|
Ask some registration questions of students only
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / Tests (PHP 8.1) (pull_request) Successful in 58s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 2m53s
CI / PHPStan (pull_request) Successful in 3m0s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
Every account-signup question was asked of everybody who registered, on the same terms: "school and grade" had to be put to an adult signing themselves up, and a question a studio needed answered for each student could only be made required by demanding it of everyone. A question now carries an audience — everyone, or only the students someone registers on behalf of — and its own required flag for each side, so optional for you and required for every student you enrol is expressible. Both settings are account-scope only: an offering asks its questions once, about the student being booked, so there is no second audience to differ from, and an offering question mirrors its single "required" into both columns. Every caller reads askedOfSelf()/isRequiredForSelf()/isRequiredForChild() rather than the raw flags, so a students-only question can neither block the account holder nor have an answer filed against them by a crafted post. The family screen, which only ever adds a student, is held to the students' rule. is_required_child arrives from dbDelta defaulting to 0, which would quietly stop every existing required question being required of the students a guardian registers — the case it most likely existed for. A one-time backfill copies is_required across, guarded by its own option so a question later made optional for students stays that way. Closes #163 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
84378e856b |
Merge pull request 'Bump version to 1.4.2' (#162) from release/bump-1.4.2 into main
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m53s
CI / Coding Standards (push) Successful in 2m58s
CI / Build Plugin Zip (push) Successful in 2m45s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
Reviewed-on: #162 |
||
|
|
a2cece750b | Bump version to 1.4.2 and open changelog section | ||
|
|
5d98aedfa5 |
Merge pull request 'Editable policy name, one-page signup with adult birth year, group classes in upcoming lessons, and booking cleanup on user delete' (#161) from feature/demo-followups-2026-07-30 into main
CI / PHPStan (push) Successful in 2m53s
CI / Coding Standards (push) Successful in 3m2s
Release / Build and Publish Release (push) Successful in 2m59s
Release / Open next-version bump PR (push) Successful in 4s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
CI / Build Plugin Zip (push) Successful in 2m46s
CI / Tests (PHP 8.1) (push) Successful in 44s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #161v1.4.1 |
||
|
|
8fd7bf983d
|
Name people by their name, not their email address
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / Tests (PHP 8.1) (pull_request) Successful in 59s
CI / Coding Standards (pull_request) Successful in 2m51s
CI / PHPStan (pull_request) Successful in 3m1s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Skipped
Anywhere the plugin named a person it could show their email instead — "Managed by [email protected]" in the students table, the same under Booked by, instructor names on the class pages. WordPress defaults a new account's `nickname` to its `user_login`, and signup uses the email address as the login. So every self-registered account carried its own address as its nickname, and UserName::format() fell straight through to it. The name they typed was in `display_name` all along. Accounts created by a guardian were never affected — GuardianService::createChild() sets `nickname` outright, which is exactly why children read correctly and their parents did not. UserName::format() now walks nickname then display name, skipping either when it is really the login or the email, so existing accounts read correctly with nothing to migrate. An identifier still never reaches the screen: an account with nothing but its address on file falls back to the id, as before. Signup also sets `nickname` at insert, so new accounts are right at the source rather than relying on the fallback. Tests: composer test (866), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
122f7a0f53
|
Show "Booked by" in the Account section of a student's detail page
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m48s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Tests (PHP 8.2) (pull_request) Successful in 57s
CI / Tests (PHP 8.1) (pull_request) Successful in 59s
CI / Coding Standards (pull_request) Successful in 3m1s
CI / Build Plugin Zip (pull_request) Skipped
The parent/guardian was only named further down under Profile, where it reads as background rather than as an account fact, and only when there was one — so a page with no such line was ambiguous between "books for themselves" and "the lookup found nothing". It now sits in the Account table beside display name and email, as the guardian's name linked to their own detail page, and always renders: a student who books for themselves says so outright. No email address — theirs is one click away on their own page, and repeating it here only makes the row harder to scan. The Profile section keeps only the note explaining the placeholder email, which is a different point. Tests: composer test (863), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
c9a1205fc0
|
Never drop an enrolled class from upcoming lessons; delete a guardian's children with them; pin the panel's line spacing
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / PHPStan (pull_request) Successful in 2m53s
CI / Coding Standards (pull_request) Successful in 2m58s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m47s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 1m1s
CI / No Debug Code (pull_request) Successful in 3s
Three fixes from testing the branch. A group class was only listed when its schedule resolved to exact datetimes, which needs a class time *and* a duration — both optional on the offering form, and the schedule note exists precisely so a studio can write "Tuesdays 4:00pm" instead. A class configured that way vanished from the list, which is the one thing this feature must never do. So Offering::sessionStarts() splits "when does it meet" from "how long does it run" (sessionWindows() is that plus the duration, unchanged), and SessionSchedule degrades instead of disappearing: dated rows with an open end when there is no duration, and a single row carrying Offering::scheduleLabel() when there is no time to derive dates from. Only a class whose last day has passed drops out. Deleting a guardian now deletes the children linked to them, releasing each one's lessons and enrolments first. A child account is login-less and exists only so the guardian has somebody to book for; without the guardian nobody can reach it, book for it, or be billed for it, so it was left stranded on the roster still holding slots. A `handled` set makes the re-entrant delete_user each child deletion fires a no-op, and stops a circular link recursing. The upcoming panel never stated its own line-height, so a theme setting line-height: 0 above it — the usual icon-font reset — was inherited straight through. Below 1 that produces both reported symptoms at once: stacked lines overlap, and the status pill's background is shorter than the text in it. Pinned at the same id-level specificity as the rest. Tests: composer test (863), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
cb347ffca0
|
Demo follow-ups: editable policy name, one-page signup, group classes in upcoming lessons, deletion cleanup
CI / Tests (PHP 8.1) (pull_request) Successful in 1m0s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m0s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 3m8s
CI / Build Plugin Zip (pull_request) Skipped
CI / PHPStan (pull_request) Successful in 2m49s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m44s
Five items from the latest demo pass: - A policy's title can be edited from the Policies screen. Only the title moves; the slug is what the gates resolve policies by, so a rename can never detach a policy from acceptances already recorded against it. - Signup is one page again. The studio's registration questions move from a second step behind "Next" onto the main form, in an "About you" panel above the students being added, and that panel also asks an adult student for their birth year (the same us_birth_year meta a child's uses). register.js disables and hides the whole panel for a pure guardian, since the questions describe a student. - The password is re-scored on submit, not only as it is typed. zxcvbn's dictionary arrives after page load, so a password typed straight away was never scored at all and the first the student heard of it was the server rejecting the whole form. - Group-class sessions appear alongside lessons wherever upcoming lessons are listed: the [us_scheduler] panel (students and instructors) and the admin student detail page. GroupClass\SessionSchedule derives them from Offering::sessionWindows(), the same derivation the billing scan uses. They carry kind = 'group_class' and no Cancel action - a session is one date in a term, not a booked slot. - Deleting a user releases what the account was holding: each upcoming lesson is cancelled, its slot freed for rebooking, its pending payment voided, and active class enrolments cancelled. Past lessons and paid history are left alone. Tests: composer test (851), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
258468093b |
Merge pull request 'Bump version to 1.4.1' (#160) from release/bump-1.4.1 into main
CI / PHPStan (push) Successful in 2m47s
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.1) (push) Successful in 48s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
CI / Build Plugin Zip (push) Successful in 2m48s
Reviewed-on: #160 |
||
|
|
3a4b25a711 | Bump version to 1.4.1 and open changelog section | ||
|
|
969d864106 |
Merge pull request 'Ask who the signup is for, and ask each student the studio's questions' (#159) from feature/145-registering-for-choice into main
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m54s
CI / PHPStan (push) Successful in 2m55s
CI / Tests (PHP 8.3) (push) Successful in 2m44s
CI / Tests (PHP 8.1) (push) Successful in 53s
Release / Build and Publish Release (push) Successful in 3m1s
Release / Open next-version bump PR (push) Successful in 4s
CI / Build Plugin Zip (push) Successful in 2m53s
Reviewed-on: #159v1.4.0 |
||
|
|
69179b75c9
|
Ask the account holder the studio's questions when they are a student too
CI / Tests (PHP 8.2) (pull_request) Successful in 43s
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m50s
CI / Coding Standards (pull_request) Successful in 3m19s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m47s
CI / Build Plugin Zip (pull_request) Skipped
Under "both" the questions were collected per student only, so someone registering themselves alongside their children was never asked their own instrument, level or anything else — despite being able to book lessons. The account-scope questions describe a student, and under "both" the account holder is one. Their answers are recorded against their own user id, not shared with a child's, and recorded after the children so a rollback that deletes the account cannot leave answers pointing at a user that no longer exists. A pure guardian is unchanged: they are not a student, so anything posted for them is still ignored. Validation became two passes rather than one so the message can say whose answers are missing — with one pass, "both" had to blame "each student" for the account holder's own blank field. In the form, the two questions turn out to be independent: whether student blocks are in play, and whether the account holder answers for themselves. "Both" is the case that needs its own answer to each, so sync() now tracks them separately, and step two comes back into play under "both". Verified in a headless browser: 21 checks across all three choices, now including that "both" enables the account holder's own question panel and offers Next rather than the early submit. Closes #146 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
4e5382e259
|
Ask who the signup is for as a three-way choice
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 2m53s
CI / Coding Standards (pull_request) Successful in 2m57s
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
Replaces the single "I'm registering as a parent or guardian" tick with "Just myself" / "On behalf of one or more students" / "Both". Radios, not checkboxes as the feedback put it: the three answers are mutually exclusive, and "both" only means anything as a third choice alongside the other two. The tick could only ever say whether there were children to add. It could not say whether the account holder was a student, so bookableStudents() always offered them their own name and any guardian could book themselves a lesson nobody meant to sell. "On behalf of" now records us_guardian_only and leaves them out of the picker. That flag is stored as the negative on purpose. Every account predating this choice is a bookable student, and absence has to keep meaning exactly that, or the picker would quietly stop offering people themselves on upgrade. setGuardianOnly() clears the key rather than writing 0, so "not set" stays the single spelling of "yes, a student". A guardian-only account with nobody linked to it is still offered itself — an empty picker is no way to book at all, and they can put the account right from the profile page. An unrecognised or absent value reads as "just myself": the choice that collects the least and grants the least. A missing radio must never be taken as "register these children". Bumps to 1.4.0. The account holder's own questions stay out of play whenever students are being added, "both" included — asking them there is #146. Verified the form in a headless browser across all three choices: which blocks show, which fields carry `required`, whether the account holder's question panel is disabled, which submit is offered, and that switching back to "just myself" leaves no hidden required field blocking submit. Closes #145 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
28046e0fd1 |
Merge pull request 'Return to a bookable calendar after a booking is confirmed' (#158) from fix/143-return-to-bookable into main
CI / Tests (PHP 8.1) (push) Successful in 44s
CI / No Debug Code (push) Successful in 1s
CI / Build Plugin Zip (push) Successful in 2m49s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / PHPStan (push) Successful in 2m55s
CI / Coding Standards (push) Successful in 2m59s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
Reviewed-on: #158 |
||
|
|
699e479805
|
Return to a bookable calendar after a booking is confirmed
CI / Tests (PHP 8.3) (pull_request) Successful in 2m44s
CI / Build Plugin Zip (pull_request) Skipped
CI / Coding Standards (pull_request) Successful in 3m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m46s
showConfirmation() hid the slot list and put the confirmation in its place,
which is a dead end: a student wanting a second lesson had nothing to click
and no way back short of reloading the page. Enrolling in a group class did
the same thing.
The confirmation is now a dismissible notice above the calendar. The
calendar is reloaded first — so the slot just taken is already gone and the
upcoming-lessons panel is current — and the notice is shown over it, which
is why loadSlots() had to start returning its promise. "It worked" and "book
another" are the same screen.
The notice clears when dismissed, when another slot's form is opened, and on
any reload of the calendar. group-classes.js gets the identical treatment.
It is built from DOM nodes rather than innerHTML because the message can
carry a studio's e-transfer address, and it is toggled with the `hidden`
attribute rather than an inline display — an inline style would outrank the
stylesheet's display:flex and stack the notice's parts. `hidden` needs the
!important guard for the same reason the upcoming-lessons panel does: the
div{display:block} theme reset outranks the UA sheet.
The slotList/list `display = 'block'` lines went with it. Nothing hides
those any more, so restoring them each load only implied otherwise.
Verified in a headless browser against a stubbed REST API: booking twice in
a row without a reload, the booked slot leaving the calendar, the upcoming
panel updating, dismissal, the notice clearing when the next form opens, and
the notice staying hidden under div{display:block}.
Closes #143
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
1b42d20541 |
Merge pull request 'Add an account block showing who is signed in' (#156) from feature/142-account-block into main
CI / Build Plugin Zip (push) Successful in 2m48s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / PHPStan (push) Successful in 2m53s
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.3) (push) Successful in 2m44s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #156 |
||
|
|
ab5212282d
|
Show only the name and email, not who the account books for
CI / Tests (PHP 8.2) (pull_request) Successful in 41s
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m48s
CI / Coding Standards (pull_request) Successful in 3m1s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m44s
CI / Build Plugin Zip (pull_request) Skipped
The block reports who is signed in and nothing more. Dropping the "Booking for …" line takes GuardianService with it — it was the only reason the page had a dependency at all, so AccountPage now constructs with no arguments. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
6e3affb1cb
|
Add an account block showing who is signed in
[us_account], or the Account block: the signed-in visitor's name, their email, a Sign out link, and — only when the account books for someone besides itself — the students it books for. A parent's first question on seeing "signed in as Grace" is whether this is the account their children's lessons are on. Two decisions worth naming. Signed out with no login page chosen, the block renders nothing. Its whole subject is the person signed in, which a stranger is not, and a bare "you are not signed in" in a site header is noise with no way to act on it. With a login page chosen it offers a Sign in link instead. The editor preview is populated regardless, so the block is never an invisible box to the person placing it. Signing out returns to the chosen login page, or to the current page when there is none. A block meant for a header should not also navigate someone somewhere when they use it; the login page wins when configured, because the page they were on may well be members-only. The name comes from UserName::format(), so the block never exposes a username the way display_name can. Also brings docs/features/editor-blocks.md back in step: it still described "four shortcodes" and had never listed the family block. Closes #142 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
7875cb1bf7 |
Merge pull request 'Fix main: two signup fixtures use a now-rejected password' (#157) from fix/green-main into main
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.1) (push) Successful in 57s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m51s
CI / Tests (PHP 8.3) (push) Successful in 2m46s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #157 |
||
|
|
04cba9702c
|
Fix main: two signup fixtures use a now-rejected password
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m46s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 3m6s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m49s
CI / Build Plugin Zip (pull_request) Skipped
#154 and #155 each passed on their own branch and broke on landing together. #154 added two guardian-signup tests using 'password123' as their fixture; #155 then added PasswordPolicy, which rejects exactly that. Neither branch ever saw the other's change, because #155 was cut from main before #154 merged. Both tests now use the same policy-clearing fixture as the rest of the file. The deliberate 'password123' in the rejected-passwords provider stays — that one is the point. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
d554e35d80 |
Merge pull request 'Validate signup email and password strength' (#155) from feature/150-signup-credential-validation into main
CI / Tests (PHP 8.2) (push) Failing after 43s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m55s
CI / Coding Standards (push) Successful in 2m56s
CI / Tests (PHP 8.3) (push) Failing after 2m44s
CI / Build Plugin Zip (push) Skipped
CI / Tests (PHP 8.1) (push) Failing after 50s
Reviewed-on: #155 |