chore: upgrade to Lattice 0.72 and overhaul the kit - #7
Merged
Merged
Conversation
Lattice 0.59 to 0.72 on both sides, Laravel 13.30, Pest 5.1, TypeScript 7, Vite 8. Adds @lattice-php/form as a direct dependency: 0.62 moved the form controls out of the ui package, and the passkey components import them. Drops what the kit no longer uses: Wayfinder generated no route helper any file imported, Chisel and Sail were never referenced at all, and components.json outlived the shadcn token layer that was removed in 0.34. concurrently goes with them — Laravel 13 ships `artisan dev`. The bunny font fetch becomes a self-hosted @fontsource-variable package, so the blade template no longer needs @fonts or a per-page vite entry that has no resources/js/pages to resolve against. resources/js/lattice/generated.d.ts is now committed and checked in CI; it is what declares the wire props of the two custom components.
0.62 moved the layout chrome into Lattice\Ui\Components and the form controls into @lattice-php/form; 0.70 folded StackDirection into Orientation and gave Text its own TextAlign enum; 0.60 validates a form once and hands handle() the validated FormData instead of a Request. The user menu's hand-rolled avatar SVG becomes the Avatar component, which Lattice has shipped since 0.51.
AppServiceProvider registers the team, member, invitation and passkey context keys. A registered key cascades into every child component, so a page that types `Team $team` in render() already seeds it — the explicit `['team' => $team->slug]` at each Form::use() and Table::lazy() call goes away, and so does the ResolvesTeamFromContext trait. The dependent keys resolve inside their parent rather than by a bare findOrFail() on the id, so a forged member, invitation or passkey id in a sealed reference now 404s instead of reaching another team's records. The passkey test asserts that 404 in place of the old 403. Authorization moves onto the definition attributes as `can`/`on`, which replaces eight authorize() bodies. A denied definition is hidden at render time, so the tests that assert a member cannot update or delete a team switch to the submitDeniedForm() helper. Also fixes the nullability the raised PHPStan level surfaced: Team::owner() returns ?User rather than ?Model, an invitation's team is not nullable (the foreign key is NOT NULL and cascades), and a pivot present on a members() result is not either.
One lint job plus one all-in-one test job becomes four workflows behind a shared composite setup action, so a slow browser run no longer holds up the status check that carries the fast suites, and only the jobs that need node or a built bundle pay for them. The js job also fails on a stale lattice/generated.d.ts. Adds the .githooks pair composer install already points core.hooksPath at: pre-commit auto-fixes staged files, pre-push runs PHPStan, the suite and the build, scoped to what the push touches. PHPStan moves from level 5 over app/ to level 8 over app/ and tests/, with no baseline. Rector joins the gate with the Laravel 13 and PHP 8.4 sets; its first pass produced the #[RouteKey] and #[Table] attributes on the models. Dependabot now watches composer and npm as well as the actions.
.ai/guidelines carries how to run and verify the kit; .ai/rules carries the path-scoped conventions an agent has to know before editing — how a definition reads context and declares authorization, why the dependent keys resolve inside their parent, that there are no page components to write, and which data-test identity a browser test targets. Laravel Boost compiles both into the git-ignored CLAUDE.md / AGENTS.md. boost.json drops the wayfinder skill and picks up testing-best-practices.
A feature test renders a real route, so `@vite` threw without a built manifest — which is why the whole suite quietly depended on `npm run build` having run. Tests\TestCase now calls withoutVite(); the new Tests\BrowserTestCase turns that back off for the one suite that really does drive the built bundle. The php-tests CI job needs no node toolchain as a result.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the starter kit from Lattice 0.34 (main) to 0.72.2 and gives the surrounding tooling — and the teams surface — the same pass. No new product features: the kit still has teams, Fortify auth, 2FA and passkeys, and nothing more.
Lattice 0.72
Lattice\Ui\Components(0.62),StackDirectionfolded intoOrientationandText::align()took its ownTextAlign(0.70), andhandle()now receives validatedFormDatainstead of aRequest(0.60).AppServiceProvider, not through hand-threaded['team' => …]context or route parameters.member,invitationandpasskeyresolve inside their parent, so a forged id in a sealed reference 404s instead of reaching another team's records.can: 'removeMember', on: 'team'), replacing eightauthorize()bodies. Page middleware drops the re-declared'web'that 0.43 started merging in.data-testwith the full node identity (0.62 + 0.72).Bugs this surfaced
visibleFrom()/hiddenFrom()did nothing.@lattice-php/coreowns the responsive-visibility wrapper (hidden md:contents), and nothing pointed Tailwind at that package's dist, somd:contentswas never generated and every such node stayed hidden at all widths. The same missing glob is why the members and invitations tables rendered without a header row.collapsible(), but Lattice ships no trigger of its own — belowmdthe drawer never opened, so a phone had no team switcher, no navigation and no logout. A topbar now carries the toggle, the breadcrumbs and the user menu.DashboardPage::breadcrumbs()was dead code — no layout ever rendered aBreadcrumbsnode.Calloutsjoins it so a flashed effect has somewhere to land.Teams performance
TeamsTablesource, 6 teamsUser::teams()now uses theMembershippivot class, so the cast role is read off the loaded row instead of re-queried.AuthorizesRowActionscaches a row action's policy check on the definition instance — which lives exactly one render, unlike a cache on the model, where a laterattach()goes stale (the suite caught that attempt).Overhaul
components.json(shadcn leftover),concurrently(Laravel 13 shipsartisan dev), the bunny font fetch (self-hosted@fontsource-variableinstead), and the 12 KB commented tsconfig.AppPagecarries the layout, middleware and notification listeners for the four signed-in pages;PageHeaderreplaces ten hand-built heading-plus-lead stacks.lattice/generated.d.ts..githookspre-commit (format staged files) and pre-push (PHPStan, suite, build — scoped to the push), wired bycomposer install.app/andtests/, no baseline. Rector joins the gate with the Laravel 13 / PHP 8.4 sets..ai/guidelines+.ai/rulescompiled into the git-ignoredCLAUDE.md/AGENTS.mdby Boost.Verification
composer ci:checkgreen (68 feature/unit tests, PHPStan 0, Rector clean, oxlint/oxfmt/tsc clean),composer test:browsergreen (5 tests, two of them new: the mobile drawer and the breadcrumb trail),npm run buildgreen.