After today's WS-6 feat-branch audit revealed ten stale branches that
had been merged via squash/cherry-pick paths but never deleted, codify
the cleanup expectation directly in CLAUDE.md. Each feature branch is
expected to be deleted locally and on origin immediately after merge —
not "eventually" — to prevent SHA-illusion confusion in future audits.
Co-Authored-By: Claude <noreply@anthropic.com>
mysql-client is now installed on the dev host (brew install
mysql-client + PATH=/opt/homebrew/opt/mysql-client/bin), which
unblocks the fast path that session 2.7 prepared but couldn't
activate.
Changes:
- api/database/schema/mysql-schema.sql committed (current state, 122 KB,
1749 lines, all 155 migration records).
- api/database/schema/.gitignore removed: the dump is no longer opt-in,
it's the default code path (active in dev + CI).
- Makefile schema-dump target simplified: drops the docker exec mysqldump
workaround in favour of plain `php artisan schema:dump`. Now also runs
migrate to head first so the dump always reflects the latest migration
set without manual prep.
- CLAUDE.md "Schema dumps" rewritten: "opt-in fast path" → "CI fast
path", reflects that the dump is committed by default and contributors
regenerate via `make schema-dump` after adding migrations.
Backfill test wall-time, 4 classes combined:
- Session 2.7 baseline: 127.90s
- This session: 27.55s (78% reduction)
Per class (PHPUnit Duration):
- FormFieldBindingMigrationTest: 4.59s (2 tests)
- ConditionalLogicBackfillTest: 5.45s (4 tests)
- FormFieldOptionsBackfillTest: 12.25s (9 tests)
- FormFieldValidationRuleBackfillTest: 10.75s (6 tests)
Full suite knock-on: 209.95s → 89.16s (-57%) — every RefreshDatabase
test pays the up-front migrate cost, which `schema:dump` collapses
from ~6s × N to a single ~1s SQL load.
Refs: WS-6 session 2.7 deviation #3 cleanup, Q1 closure
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Session 2.6's per-test migrate:fresh in 4 backfill test classes
replays ~120 migrations. Laravel's schema:dump produces a single
SQL file that migrate:fresh loads atomically when present —
significantly faster on environments where the host has the
\`mysql\` CLI available for Laravel's load step.
Changes:
- New Makefile target \`schema-dump\` runs mysqldump INSIDE the
bm_mysql Docker container (no host mysqldump dependency to
GENERATE the dump). Outputs api/database/schema/mysql-schema.sql.
- api/database/schema/.gitignore added: mysql-schema.sql is NOT
committed by default. Laravel's auto-load on migrate:fresh
shells out to the host's \`mysql\` CLI; on hosts without it,
the presence of the dump file actively breaks migrate (exit 127).
Treat the dump as opt-in per dev environment.
- CLAUDE.md "Schema dumps (opt-in fast path)" subsection added with
the workflow: brew install mysql-client → migrate to head →
make schema-dump → optionally commit.
phpstan-baseline.neon: removed a stale "unused use \$actor" entry
in FormSubmissionService whose underlying closure pint cleaned up
in commit 060d6f3 (Task 1).
Wall-time on this dev machine (no host mysql CLI): NO speedup,
backfill tests still ~6s per setUp. The dump file is ready for
environments that have mysql CLI. Documented as a deviation.
JSON canonicalization (commit 060d6f3) is the load-bearing
correctness fix from this branch; the schema-dump perf path is a
nice-to-have that activates per-environment.
Refs: WS-6 session 2.6 deviation #5 cleanup
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CLAUDE.md's existing "Database" subsection is expanded to spell out
that MySQL 8.0 is the exclusive database engine for dev, testing, and
production. SQLite is forbidden — its quirks (rebuild-on-FK-add
cascade behaviour, JSON key-order non-determinism on round-trip,
VARCHAR length enforcement gaps, concurrency model) mask bugs that
would surface in production MySQL.
Context: WS-6 session 2.5 had to omit a FK constraint due to an
SQLite-only quirk; session 2.6 (Task 1+2 of this branch) moved test
infrastructure to MySQL and restored the FK. This policy ensures the
issue cannot recur. Includes the cross-engine query-rewrite rules
the migration surfaced (information_schema.STATISTICS over
sqlite_master, assertEquals over assertSame on JSON-derived data).
Refs: RFC-WS-6.md v1.1, WS-6 session 2.5 deviation #1, session 2.6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both items currently sit at default priority. Foundation-tooling
commit 3 (ts-reset install) surfaced them but didn't constrain
when they need to close.
Adds explicit S3b-trigger rationale: launching the form-builder
organizer UI in apps/app on top of:
- 22 unverified pre-existing TypeScript errors in apps/portal,
AND
- zero Vitest setup in apps/app
is asymmetric quality, exactly the discipline gap that bites in
post-launch debugging. Both items now flagged "high before S3b
lands" with concrete close-criteria.
TECH-TS-PORTAL-TSC additionally clarifies tiptap node_modules
handling. Phase A check confirmed `skipLibCheck: true` is already
set in both SPAs' single tsconfig.json (no `tsconfig.app.json` /
`tsconfig.node.json` variants exist). Despite that, the 4 tiptap
errors persist because tiptap ships uncompiled `.ts` source files
in `node_modules/.../@tiptap/core/src/`, not `.d.ts` — and
`skipLibCheck` only suppresses checking of `.d.ts` files. Real
fix paths are upstream `@tiptap/*` upgrade (newer majors may ship
`.d.ts` only) or a focused `exclude` glob; flipping skipLibCheck
is a non-fix because it is already on.
TECH-APP-VITEST adds a 6-step setup outline scoped to "harness
exists + one test passes", explicitly excluding comprehensive
test-writing for existing apps/app code. `useImpersonationStore.ts`
called out as a natural early target — it has no runtime test
today and the pending TECH-TS-IMPERSONATION shape-validation work
benefits from coverage.
CLAUDE.md quality-gates list adds a Vitest entry that surfaces
the apps/portal / apps/app asymmetry, with a pointer to
TECH-APP-VITEST.
No code changes. Documentation only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Installs laravel/telescope ^5.0 (v5.12.5) as a dev-dependency.
Three-layer production safety adapted to Laravel 11 layout (no
Kernel.php; routing/schedule in bootstrap/app.php +
routes/console.php):
1. composer.json `extra.laravel.dont-discover` lists
laravel/telescope. After editing, `php artisan package:discover`
regenerates bootstrap/cache/packages.php — without this step
the auto-discovery cache still registers the vendor provider.
2. AppServiceProvider::register() gates registration to local +
testing environments. Registers BOTH the vendor
Laravel\Telescope\TelescopeServiceProvider (routes, migrations,
publishing) AND the project's App\Providers\TelescopeService
Provider (gate + filter) — they're sibling classes that extend
ServiceProvider independently, not parent/child, so both must
register for the dashboard to work. bootstrap/providers.php
deliberately does NOT list either Telescope provider.
3. .env TELESCOPE_ENABLED flag (false in .env.example). Runtime
toggle that disables Telescope even when the providers are
registered.
Production safety verified via simulated APP_ENV=production check:
confirms no Telescope-* providers are loaded.
Authorization: viewTelescope gate restricts dashboard to users
with the super_admin Spatie Permission role. Even in local
environments, only super_admin can view. Default was an email
allow-list stub — replaced with `$user->hasRole('super_admin')`.
Pruning: Schedule::command('telescope:prune --hours=48') added in
routes/console.php (Laravel 11's schedule location), environment-
gated to local + testing only.
Documentation: /dev-docs/TELESCOPE.md added; CLAUDE.md gets a
Development-tooling section. The doc explicitly calls out the
dual-provider registration (vendor + app) which differs from the
single-provider pattern in older Laravel versions.
Migrations applied: telescope_entries, telescope_entries_tags,
telescope_monitoring tables. Route registration verified in local
(42 telescope.* routes).
Tests: 1208/1208 passing — Telescope loads in the testing
environment as well, so the suite exercised it without issues.
Deployment note (flag for separate docs): a production operator
who runs `php artisan migrate` manually will still apply the
Telescope migrations — but because the providers never register
in production, the tables stay empty.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Installs @total-typescript/ts-reset 0.6.1 as a dev-dependency in
apps/portal/ and apps/app/. Patches TypeScript's loosest default
types: Array.filter(Boolean) returns non-nullable, JSON.parse
returns unknown, fetch().json() returns unknown, Map.get() strict,
etc.
Configuration: src/reset.d.ts in each SPA imports the reset. Both
tsconfig.json files already include ./src/**/* so the .d.ts is
picked up automatically — no tsconfig edits needed.
Issues surfaced during install:
- apps/app — 0 pre-install tsc errors in own code; install
surfaced 2 errors in src/stores/useImpersonationStore.ts
(both from JSON.parse on sessionStorage content returning
unknown instead of any). Fixed inline at lines 19 + 123 via
`as ImpersonationState` casts that make the existing
trust-in-sessionStorage explicit. Backlog entry
TECH-TS-IMPERSONATION tracks proper runtime shape validation.
- apps/portal — 22 pre-existing tsc errors in own code (mostly
tiptap editor components — tracked as TECH-TS-PORTAL-TSC,
unrelated to ts-reset). Zero new errors in portal's own code.
4 additional errors surfaced in tiptap's uncompiled node_modules
.ts sources (third-party); left as-is.
Neither SPA achieves `tsc --noEmit` clean today — pre-existing
state unrelated to this work package. Build + vitest are the
actual working gates and both remain green:
- apps/portal: vitest 113/113 passing; production build succeeds
- apps/app: (no vitest setup — tracked as TECH-APP-VITEST);
production build succeeds
Documentation: /dev-docs/FRONTEND-TOOLING.md added; CLAUDE.md
quality-gates updated.
Backlog: TECH-TS-IMPERSONATION (runtime validation of stored
impersonation state), TECH-TS-PORTAL-TSC (pre-existing portal tsc
errors), TECH-APP-VITEST (Vitest coverage for apps/app).
No production behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frontend:
- Consolidate duplicate API layers into single src/lib/axios.ts per app
- Remove src/lib/api-client.ts and src/utils/api.ts (admin)
- Add src/lib/query-client.ts with TanStack Query config per app
- Update all imports and auto-import config
Backend:
- Fix organisations.billing_status default to 'trial'
- Fix user_invitations.invited_by_user_id to nullOnDelete
- Add MeResource with separated app_roles and pivot-based org roles
- Add cross-org check to EventPolicy view() and update()
- Restrict EventPolicy create/update to org_admin/event_manager (not org_member)
- Attach creator as org_admin on organisation store
- Add query scopes to Event and UserInvitation models
- Improve factories with Dutch test data
- Expand test suite from 29 to 41 tests (90 assertions)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>