Commit Graph
265 Commits
Author SHA1 Message Date
thatguygriffandClaude Opus 5 908b7fcd1f Update the coding-standards tooling off three security advisories
CI / Coding Standards (pull_request) Successful in 16s
CI / No Debug Code (pull_request) Successful in 4s
CI / Tests (PHP 8.3) (pull_request) Successful in 21s
CI / Tests (PHP 8.5) (pull_request) Successful in 22s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Static Analysis (pull_request) Successful in 57s
CI / Build Plugin Zip (pull_request) Skipped
composer audit reported three advisories against the PHPCS stack, two rated
high:

  squizlabs/php_codesniffer  3.13.5 -> 3.13.6  CVE-2026-67434, OS command injection
  wp-coding-standards/wpcs   3.3.0  -> 3.4.1   CVE-2026-45293, arbitrary code execution
  phpcsstandards/phpcsutils  1.2.2  -> 1.2.3   CVE-2026-65954, arbitrary code execution

All three are dev-only and none ship in the plugin — bin/build-zip.sh installs
--no-dev — but they execute against repository content on every CI run.

The existing ^3.7 and ^3.0 constraints already allowed the fixed releases, so
composer.json is unchanged. phpcsextra and the codesniffer installer come
along as transitive dependencies. Five packages move, none are added or
removed, and the updated standards report no new violations.

composer audit is now clean.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:50:34 -03:00
thatguygriffandClaude Opus 5 b17adf02ff Pin the Composer platform to PHP 8.1
CI / Tests (PHP 8.2) (pull_request) Successful in 26s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 29s
CI / Tests (PHP 8.5) (pull_request) Successful in 30s
CI / Coding Standards (pull_request) Successful in 36s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Static Analysis (pull_request) Successful in 49s
CI / Build Plugin Zip (pull_request) Skipped
composer.json declares "php": ">=8.1", but resolution ran against whatever
PHP the machine doing the update happened to have. Running composer update
on 8.5 could therefore produce a lock containing a package that requires 8.2
or newer, which installs fine locally and fails on the 8.1 leg of the test
matrix — a lock that is only discovered to be wrong in CI.

config.platform.php makes resolution always target the declared minimum, so
a lock that resolves at all is a lock that installs everywhere the plugin
claims to run.

No package versions change: nothing currently locked requires a PHP newer
than 8.1, so this only records platform-overrides and the new content hash.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:46:11 -03:00
thatguygriff a23490ec80 Merge pull request 'CI: run jobs in shared ci-php images, commit composer.lock' (#191) from feature/187-run-jobs-in-ci-images into main
CI / Coding Standards (push) Successful in 16s
CI / No Debug Code (push) Successful in 5s
CI / Tests (PHP 8.3) (push) Successful in 25s
CI / Tests (PHP 8.5) (push) Successful in 31s
CI / Static Analysis (push) Successful in 37s
CI / Tests (PHP 8.2) (push) Successful in 35s
CI / Tests (PHP 8.1) (push) Successful in 39s
CI / Build Plugin Zip (push) Successful in 9s
Reviewed-on: #191
2026-08-25 01:41:19 +00:00
thatguygriffandClaude Opus 5 1552bf4b5f Move the CI image definitions to the shared ci-php repository
CI / Coding Standards (pull_request) Successful in 14s
CI / Tests (PHP 8.1) (pull_request) Successful in 30s
CI / Tests (PHP 8.2) (pull_request) Successful in 30s
CI / Tests (PHP 8.3) (pull_request) Successful in 29s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.5) (pull_request) Successful in 21s
CI / Static Analysis (pull_request) Successful in 37s
CI / Build Plugin Zip (pull_request) Skipped
The image definition and its publishing workflow were only here because this
was the first project to need them. They are project-agnostic, so they now
live in Unsupervised/ci-php and any PHP project can consume the same tags.
The registry path is unchanged — Gitea container images are named
owner/image, not after the repository that pushes them — so nothing here
needs repointing.

Split the combined quality job into separate phpcs and phpstan jobs. They
were folded together to halve the number of toolchain installs per run; with
the toolchain in the image that saves nothing, and separating them means a
coding-standards failure no longer hides the static analysis result.

Rewrite the workflow comments to describe what the jobs do rather than the
setup step they replaced, and cut docs/ci.md down to what is specific to this
project — the image contents and publishing are documented in ci-php.

Closes #187

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:39:08 -03:00
thatguygriffandClaude Opus 5 f8762e1095 Correct the registry auth docs: the Actions token does not work
CI Images / Build CI image (PHP 8.2) (pull_request) Successful in 3s
CI Images / Build CI image (PHP 8.5) (pull_request) Successful in 8s
CI / Tests (PHP 8.3) (pull_request) Successful in 30s
CI Images / Build CI image (PHP 8.3) (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 23s
CI / Tests (PHP 8.1) (pull_request) Successful in 28s
CI / No Debug Code (pull_request) Successful in 4s
CI / Coding Standards & Static Analysis (pull_request) Successful in 43s
CI / Tests (PHP 8.5) (pull_request) Successful in 23s
CI / Build Plugin Zip (pull_request) Skipped
CI Images / Build CI image (PHP 8.1) (pull_request) Successful in 1m3s
docs/ci.md and the workflow comment both described secrets.GITHUB_TOKEN as
the working credential with REGISTRY_TOKEN as a fallback. That is backwards:
the task token is rejected by Gitea's container registry (go-gitea/gitea#23642)
and the first publish attempt failed on exactly that. REGISTRY_TOKEN is
required.

Part of #187

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:35:10 -03:00
thatguygriffandClaude Opus 5 c9d18fec74 Commit composer.lock so CI and releases install a fixed dependency set
CI / Tests (PHP 8.2) (pull_request) Successful in 25s
CI / Tests (PHP 8.1) (pull_request) Successful in 26s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 32s
CI / Coding Standards & Static Analysis (pull_request) Successful in 42s
CI / Tests (PHP 8.5) (pull_request) Successful in 32s
CI / Build Plugin Zip (pull_request) Skipped
composer.lock was gitignored, so `composer install` resolved the graph afresh
on every job. CI could quietly start testing different dependency versions
than it did the day before, and two builds of the same tag could ship
different vendor trees — the run that produces the zip is the same run that
picks the versions.

Track the lock, and make the two places that consume it use it:

- the Composer cache keys hash composer.lock rather than composer.json, since
  the lock is now what actually fingerprints the dependency set;
- bin/build-zip.sh stages the lock alongside composer.json, so the --no-dev
  production install resolves to the locked versions. It already deleted both
  from the staging directory before zipping, so the shipped plugin is
  unchanged.

The current lock installs cleanly on 8.1: nothing in it requires a PHP newer
than the >=8.1 composer.json declares.

Closes #187

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:33:04 -03:00
thatguygriffandClaude Opus 5 ab609898d6 Run CI and release jobs inside the prebuilt CI images
CI / Coding Standards & Static Analysis (pull_request) Successful in 37s
CI / Tests (PHP 8.1) (pull_request) Successful in 21s
CI / Tests (PHP 8.2) (pull_request) Successful in 20s
CI / Tests (PHP 8.3) (pull_request) Successful in 19s
CI / Tests (PHP 8.5) (pull_request) Successful in 19s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Skipped
Replaces setup-php with `container:` in every job that needs PHP. The images
published by ci-images.yml already carry PHP, Composer, the intl and zip
extensions and the CLI tools the workflow scripts shell out to, so nothing
installs PHP at job time any more.

That removes the apt path #178 was about: a ~145s floor on 8.3 against ~35s
on 8.1/8.2, with a tail that twice ran past the step timeout and failed the
run. All three copies of the `Keep downloaded .debs` / `Cache apt packages`
workaround go with it.

no-debug and bump-version stay on the runner image — neither needs PHP, and
no-debug wants the runner's GNU `grep --include`.

The Composer cache moves to /composer/cache, which is COMPOSER_HOME in the
image, and release.yml gains the cache step it never had.

Also corrects the image size in docs/ci.md to the 67MB measured from the
published manifest; the ~120MB in the original was an estimate.

Closes #187

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:25:02 -03:00
thatguygriff 4bc8e80837 Merge pull request 'CI: install GNU tar in the CI image so actions/cache works' (#190) from fix/187-ci-image-gnu-tar into main
CI Images / Build CI image (PHP 8.3) (push) Successful in 1m14s
CI Images / Build CI image (PHP 8.2) (push) Successful in 1m30s
CI Images / Build CI image (PHP 8.1) (push) Successful in 1m31s
CI Images / Build CI image (PHP 8.5) (push) Successful in 1m54s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.5) (push) Successful in 3m0s
CI / Tests (PHP 8.1) (push) Successful in 6m0s
CI / Tests (PHP 8.3) (push) Successful in 6m1s
CI / Coding Standards & Static Analysis (push) Failing after 17m19s
CI / Build Plugin Zip (push) Skipped
Reviewed-on: #190
2026-08-25 01:23:12 +00:00
thatguygriffandClaude Opus 5 28f586d207 Install GNU tar in the CI image so actions/cache works
CI Images / Build CI image (PHP 8.1) (pull_request) Successful in 48s
CI / Tests (PHP 8.1) (pull_request) Successful in 55s
CI Images / Build CI image (PHP 8.2) (pull_request) Successful in 1m46s
CI Images / Build CI image (PHP 8.3) (pull_request) Successful in 1m48s
CI Images / Build CI image (PHP 8.5) (pull_request) Successful in 2m26s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m50s
CI / Tests (PHP 8.5) (pull_request) Successful in 2m58s
CI / Coding Standards & Static Analysis (pull_request) Failing after 17m18s
CI / Build Plugin Zip (pull_request) Skipped
actions/cache shells out to `tar --posix -P`. Alpine's busybox tar rejects
both flags, so the cache step would fail in every job that runs inside these
images — which is all of them once ci.yml switches over.

coreutils does not cover this: tar is its own Alpine package. Add it, add
zstd (which actions/cache prefers over gzip when present), and assert GNU
tar in the image's smoke test so a future base-image change cannot quietly
drop it again.

Part of #187

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 22:15:40 -03:00
thatguygriff e097b7a0bf Merge pull request 'CI: publish prebuilt CI images to the Gitea container registry' (#188) from feature/187-ci-container-images into main
CI Images / Build CI image (PHP 8.1) (push) Failing after 5s
CI Images / Build CI image (PHP 8.2) (push) Failing after 7s
CI Images / Build CI image (PHP 8.3) (push) Failing after 6s
CI Images / Build CI image (PHP 8.5) (push) Failing after 17s
CI / No Debug Code (push) Successful in 9s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 2m47s
CI / Coding Standards & Static Analysis (push) Successful in 3m41s
CI / Tests (PHP 8.1) (push) Successful in 5m59s
CI / Tests (PHP 8.5) (push) Successful in 7m5s
CI / Build Plugin Zip (push) Successful in 6m0s
Reviewed-on: #188
2026-08-25 01:05:33 +00:00
thatguygriffandClaude Opus 5 572aaf5b49 Publish prebuilt CI images to the Gitea container registry
CI Images / Build CI image (PHP 8.2) (pull_request) Successful in 1m14s
CI Images / Build CI image (PHP 8.1) (pull_request) Successful in 1m26s
CI Images / Build CI image (PHP 8.3) (pull_request) Successful in 2m3s
CI Images / Build CI image (PHP 8.5) (pull_request) Successful in 2m9s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m7s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 3m8s
CI / Tests (PHP 8.1) (pull_request) Successful in 5m55s
CI / Tests (PHP 8.5) (pull_request) Successful in 6m21s
CI / Coding Standards & Static Analysis (pull_request) Successful in 18m26s
CI / Build Plugin Zip (pull_request) Skipped
setup-php installs PHP 8.3+ from apt on these arm64 runners: a ~145s floor
against ~35s for 8.1/8.2, with a tail that twice ran past the step timeout
and failed the run (#178). Caching the .debs softened it without removing
the apt step, and 8.5 has the same problem.

Add a per-version CI image built on php:<version>-cli-alpine and a workflow
that publishes it to git.unsupervised.ca/unsupervised/ci-php:<version>. The
org is public, so the packages pull anonymously.

The image carries bash and nodejs because act_runner runs JavaScript actions
inside the job container, GNU coreutils/grep/sed because the workflow scripts
use `tac` and `grep --include`, and curl/jq/git/zip for release.yml and
bin/build-zip.sh. Composer 2 and the intl and zip extensions round it out.

Nothing consumes the images yet — ci.yml and release.yml switch over in a
follow-up, because a job cannot run in an image that has not been published.

Part of #187

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9acV1mHktGAb1uyvNmrR2
2026-08-24 21:55:28 -03:00
thatguygriff 116394f2ff Merge pull request 'Bump version to 1.5.5' (#189) from release/bump-1.5.5 into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 53s
CI / Tests (PHP 8.5) (push) Successful in 3m28s
CI / Tests (PHP 8.3) (push) Successful in 5m46s
CI / Tests (PHP 8.2) (push) Successful in 6m14s
CI / Coding Standards & Static Analysis (push) Failing after 12m16s
CI / Build Plugin Zip (push) Skipped
Reviewed-on: #189
2026-08-25 00:50:11 +00:00
Release Bot dda8386c1f Bump version to 1.5.5 and open changelog section 2026-08-24 23:50:02 +00:00
thatguygriff 7278309bf5 Merge pull request 'Let the studio register the students who cannot register themselves' (#186) from fix/185-book-for-child-and-vet-group-enrolment into main
CI / Coding Standards & Static Analysis (push) Successful in 13m23s
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / Tests (PHP 8.2) (push) Successful in 1m7s
CI / Tests (PHP 8.3) (push) Successful in 6m59s
CI / Tests (PHP 8.5) (push) Successful in 12m55s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 5m53s
Release / Build and Publish Release (push) Successful in 8m5s
Release / Open next-version bump PR (push) Successful in 5s
Reviewed-on: #186
v1.5.4
2026-08-24 22:03:11 +00:00
thatguygriffandClaude Opus 5 5ce42f0003 Let the studio register the students who cannot register themselves
CI / No Debug Code (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m3s
CI / Tests (PHP 8.5) (pull_request) Successful in 2m48s
CI / Tests (PHP 8.3) (pull_request) Successful in 3m24s
CI / Coding Standards & Static Analysis (pull_request) Successful in 8m21s
CI / Build Plugin Zip (pull_request) Skipped
The Book a lesson for a student panel built its picker from the us_student
role but vetted the submission with the book_lesson capability. ChildLoginGate
and RegistrationLoginGate withhold that capability from accounts that keep the
role, so the panel offered every guardian-managed child and every unapproved
signup and then refused them — with a message claiming no student had been
chosen, and a form cleared of all five fields.

Withholding book_lesson stops those accounts registering in their own name. It
was never meant to stop the studio acting for them, which is what the panel is
for, and for a child is the only route to a lesson besides their guardian.

Guard the student role instead, via a new RoleManager::isStudent() shared with
every picker and guard on the staff side so the two cannot drift apart again.
Group enrolment gets the same predicate: addDirect() and grantAccess() vetted
their posted ids not at all, and would enrol an instructor, an administrator,
or an account deleted since the page was drawn — raising a real payment against
them for a priced class.

Keep a refused booking's fields as submitted, reading the form through one
LessonController::submittedBooking() so what gets booked and what is shown
again cannot disagree about a field name. A booking that succeeds still leaves
an empty form, so the next one does not inherit it.

Closes #185

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XunBYk2sFEc1oL14sUiuBU
2026-08-24 18:42:59 -03:00
thatguygriff 76530878b5 Merge pull request 'Bump version to 1.5.4' (#184) from release/bump-1.5.4 into main
CI / Tests (PHP 8.1) (push) Successful in 58s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.5) (push) Successful in 4m20s
CI / Coding Standards & Static Analysis (push) Successful in 5m21s
CI / Tests (PHP 8.2) (push) Successful in 6m4s
CI / Tests (PHP 8.3) (push) Successful in 12m56s
CI / Build Plugin Zip (push) Successful in 2m39s
Reviewed-on: #184
2026-08-24 17:59:40 +00:00
Release Bot 6031a75012 Bump version to 1.5.4 and open changelog section 2026-08-24 17:56:38 +00:00
thatguygriff 41843e5253 Merge pull request 'Let the studio book lessons and record intake collected elsewhere' (#183) from feature/182-admin-booking-and-intake-recording into main
Release / Build and Publish Release (push) Successful in 4m43s
Release / Open next-version bump PR (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / Tests (PHP 8.2) (push) Successful in 56s
CI / Tests (PHP 8.3) (push) Successful in 4m41s
CI / Tests (PHP 8.5) (push) Successful in 3m6s
CI / No Debug Code (push) Successful in 4s
CI / Coding Standards & Static Analysis (push) Successful in 8m23s
CI / Build Plugin Zip (push) Successful in 2m39s
Reviewed-on: #183
v1.5.3
2026-08-24 17:50:46 +00:00
thatguygriffandClaude Opus 5 8c21a3fa9d Let the studio book lessons and record intake collected elsewhere
CI / Tests (PHP 8.1) (pull_request) Successful in 6m39s
CI / Tests (PHP 8.2) (pull_request) Successful in 57s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m59s
CI / Tests (PHP 8.5) (pull_request) Successful in 3m31s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards & Static Analysis (pull_request) Successful in 3m28s
CI / Build Plugin Zip (pull_request) Skipped
Two related gaps, closed together because the second is created by the first.

A private lesson could only be booked by the student or their guardian, so a
booking taken over the phone had no way in — where group classes have had "Add
students directly" all along. "Book a lesson for a student" is now a panel on
Scheduler and My Lessons: student, open time, lesson type, with weekly term
reservations and a no-charge option for make-up lessons. The booking core is
extracted to Booking\LessonBooker and shared with POST /bookings, so the two
paths cannot drift on offering rules, slot claiming, or billing.

That leaves a registration with no intake answers and no policy acceptances,
because nobody was at a keyboard to give them — already true of every directly
added group-class student. Ticking the boxes on a student's behalf would be an
audit trail that says something untrue, so instead the answers are collected
another way and recorded afterwards, from a lesson's or an enrolment's detail
page. Every recording must say how it was collected, which is stamped on each
row along with who typed it and shown in a new "How it was given" column: a
policy ticked online and one transcribed from paper must never look alike.

Only staff-made registrations qualify (us_lessons.booked_by,
us_group_enrollments.enrolled_by) — one the student made already holds their
own answers. Only what is still missing can be recorded, re-checked at write
time, so a stale or double-posted form cannot duplicate or overwrite. No IP is
stored for a transcription, and accepted_by stays the student while recorded_by
names the staff member.

Intake is now generic over Registration\IntakeSubject, which Lesson and
Enrollment both implement; LessonDetail became Registration\IntakeAudit and is
shared by both detail views rather than duplicated.

Closes #182

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QfHt6CyJHz6KkA4RuaS7WK
2026-08-24 14:06:16 -03:00
thatguygriff 8a34ec41e9 Merge pull request 'Cache the packages php-builder installs, and restore PHP 8.5' (#181) from ci/cache-php-builder-deps into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.2) (push) Successful in 2m27s
CI / Tests (PHP 8.1) (push) Successful in 2m33s
CI / Tests (PHP 8.3) (push) Successful in 2m55s
CI / Coding Standards & Static Analysis (push) Successful in 3m26s
CI / Tests (PHP 8.5) (push) Successful in 7m59s
CI / Build Plugin Zip (push) Successful in 6m59s
Reviewed-on: #181
2026-08-20 19:18:46 +00:00
thatguygriffandClaude Opus 5 37c8d2b39e Put PHP 8.5 back in the matrix
CI / No Debug Code (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 55s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m15s
CI / Tests (PHP 8.5) (pull_request) Successful in 6m41s
CI / Tests (PHP 8.3) (pull_request) Failing after 12m21s
CI / Coding Standards & Static Analysis (pull_request) Successful in 13m48s
CI / Build Plugin Zip (pull_request) Skipped
I misread the instruction to hold 8.5 as "take it out until the cache
lands" and removed it in f6481d4, so #179 merged without it. It was meant
to stay where it was. Restoring it.

8.5 takes the same php-builder path as 8.3, so the apt cache in this
branch is exactly what it needs, and the per-version key means it caches
its own ~70 -dev packages rather than sharing with the ondrej-path jobs.

composer test passes on 8.5.9 locally: 915 tests, 2592 assertions.

Refs #178.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks
2026-08-20 16:04:52 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 15:57:27 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 15:56:37 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 15:56:37 -03:00
thatguygriff 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
2026-08-20 18:55:16 +00:00
thatguygriffandClaude Opus 5 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
2026-08-20 15:33:03 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 15:07:18 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 15:04:33 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 14:59:39 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 14:44:54 -03:00
thatguygriffandClaude Opus 5 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
2026-08-20 14:29:30 -03:00
thatguygriff 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
2026-08-20 17:25:22 +00:00
thatguygriffandClaude Opus 5 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
2026-08-20 14:22:52 -03:00
thatguygriff 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
2026-08-20 16:56:38 +00:00
thatguygriff 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
2026-08-20 16:37:58 +00:00
Release Bot 164c8ebf97 Bump version to 1.5.3 and open changelog section 2026-08-20 16:28:20 +00:00
thatguygriffandClaude Opus 5 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
2026-08-20 13:27:22 -03:00
thatguygriff 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: #174
v1.5.2
2026-08-20 15:53:02 +00:00
thatguygriffandClaude Opus 5 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
2026-08-20 12:43:29 -03:00
thatguygriff 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
2026-08-11 22:06:41 +00:00
Release Bot b950e35e5a Bump version to 1.5.2 and open changelog section 2026-08-11 22:06:18 +00:00
thatguygriff 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: #171
v1.5.1
2026-08-11 20:44:29 +00:00
thatguygriffandClaude Opus 5 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
2026-08-11 17:36:01 -03:00
thatguygriff 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
2026-07-30 19:48:12 +00:00
Release Bot 7ea8d653ee Bump version to 1.5.1 and open changelog section 2026-07-30 19:46:05 +00:00
thatguygriff 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: #168
v1.5.0
2026-07-30 19:41:03 +00:00
thatguygriffandClaude Opus 5 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]>
2026-07-30 16:35:26 -03:00
thatguygriff 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
2026-07-30 19:29:21 +00:00
thatguygriffandClaude Opus 5 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]>
2026-07-30 15:08:05 -03:00
thatguygriff 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
2026-07-30 16:56:33 +00:00