Repository navigation
Releases: teylabs/kit
Release list
v0.7.8
Added
- docs/upgrading-0.5-to-0.7.md: one pass from v0.5.117 to v0.7.7, now that 0.5.x is frozen. It covers the order of the
--lineageruns, the 0.6.0 ownership moves, and a checklist of the Upgrading steps that need a person.
Upgrading
- Run
kit:update plan|applyforcore(re-pinnedsha256:dc02fc76…f634→sha256:71423bf5…6132):Kit/Layout/StickyBar.vuetakes the fix below. Ported from 0.5.x, which is frozen.
Fixed
- A request no longer queries the database schema to learn whether the workspaces table has
admittedanddeleting_at. The kit asked once per request, since PHP-FPM dropsstaticmemos between requests, and asked again each time a column was missing. A column found is now cached (kit-workspace-columns, in the default cache store) until the nextmigrateforgets it. One not found yet is still asked again, so a table migrated later is seen at once. - A StickyBar at the viewport's foot counts as pinned, however it got there. Until now a bar counted as pinned only when it was scrolled to the foot or held there with
pinned. A host that lays the Workbench out itself (:fill="false"in its own full-height column) put the actions bar at the foot without either, so bottom toasts weren't lifted above it. A bar whose bottom is within 12px of the viewport's (room for its container's gap) now counts as pinned, re-checked on scroll, window resize and layout resize. auth.passkeyscounts the kit's own passkey offer. The shell'spages/Workspace/Layout.vuerenders<PasskeyOffer />on every signed-in page through@kit/ui, but the row counted only a host import of it, so an app using the kit shell readmissing. The row now also counts the installed Layout; importing the offer again in the host would show it twice.
What's Changed
- Port the three 0.5.x-only fixes to main by @jaspertey in #531
- Upgrade guide: 0.5.x to 0.7 in one pass by @jaspertey in #532
- Release v0.7.8 by @jaspertey in #533
Full Changelog: v0.7.7...v0.7.8
v0.7.7
Upgrading
- Run
kit:update plan|applyforcore(re-pinnedsha256:e238b630…7408→sha256:dc02fc76…f634) andshell(sha256:93780271…035d→sha256:c7036fbf…8b21). The breadcrumb trail moves from the shell's row into<Page>, above the heading (see below). Three things for an app:- Pages without
<Page>lose their trail, because the shell no longer draws one. Render<Breadcrumbs />(from@kit/ui) above the page's own heading. - An edited
Partials/Breadcrumbs.vueconflicts on update. The shell's copy is now a thin wrapper around core'sBreadcrumbs, kept for pages that import it; the trail itself isKit/Layout/Breadcrumbs.vue, which you edit or replace through@kit/ui. - Headings move up. The 40px row under the header is gone, and a 20px trail line plus an 8px gap sits above the heading. So every page's H1 is about 30px higher than before, and at the same height on every page.
- Pages without
- Run
kit:update plan|applyforsession-ui(re-pinnedsha256:c5b8919e…e94e→sha256:d96afd41…1b7f) andworkspace-ui(re-pinnedsha256:5d746402…9202→sha256:749a17ac…5112), thenphp artisan migrate: the new0001_01_02_000505_add_finished_at_to_kit_account_deletions_tablemigration, andWorkspace/Settings/Delete/Page.vueandWorkspace/Account/Delete/Page.vue, which show the password field whileconfirms_passwordis true. A host that edited those pages adds the field itself. Clients that callDELETE /@{workspace}/settings/deletemust now sendpassword, unlesskit.lifecycle.confirm_passwordisfalse. - Run
kit:update plan|applyforaccess(re-pinnedsha256:a179066f…6d98→sha256:98af2e26…2de6) andsettings(re-pinnedsha256:fb83eb1d…5f1e→sha256:37ad7378…6eba): their000300and000400migrations now adopt a compatible host table in place. A host that has already run them is unaffected (migrate doesn't re-run them). A host with its ownentitlements,complimentary_grantsor spatie settings table should runkit:adopt --json(orkit:add's plan) first: an incompatible table now blocks with its columns named instead of failing inmigrate. - Run
kit:update plan|applyforcore(re-pinnedsha256:6e87fc26…fe56→sha256:e67b1978…efa5):kit.csstakes the change below. A host that defined its own--container-2kcan drop it; its own value still wins if kept. - Run
kit:update plan|applyforcore(re-pinnedsha256:b7a9b9ba…5e9d→sha256:6e87fc26…fe56):Kit/Form/Slider.vuetakes the fix below. - No feature is re-pinned: the mail views and account mail ship in the package. To show the brand's lockup in mail, add a PNG of it for a light background (2x the 220px it shows at, or more) and set
KIT_BRAND_MAIL_LOCKUP(kit.brand.mail_lockup) to its path inpublic/or an absolute file path. Without it, the mail header is the product's name, centred. - A host that published its own
resources/views/vendor/mailkeeps its views. To leave the manage-notifications link off security mail there too, give itsmessageandfooterviews themanageprop (see docs/mail.md › Account mail). - A host that set its own
ResetPassword::toMailUsing()orVerifyEmail::toMailUsing()keeps it, and doesn't get the kit's account mail. - Run
kit:update plan|applyforworkspaces(re-pinnedsha256:40fdd76c…81c4→sha256:56912cc3…eafd) andsession-ui(re-pinnedsha256:abbeb463…c64f→sha256:a4581c16…377d), thenphp artisan migrate: the new0001_01_02_000210_add_deleting_at_to_kit_workspaces_table(nullabledeleting_atanddeleting_byonworkspaces, recorded as kit-added on an adopted table) and0001_01_02_000504_create_kit_account_deletions_tablemigrations below. Existing workspaces stay open. Nothing changes for a host whose purge handler doesn't opt into deferral. - Run
kit:update plan|applyforsession-ui(re-pinnedsha256:a4581c16…377d→sha256:c5b8919e…f94e):0001_01_02_000501_add_user_foreign_key_to_kit_security_events_table.phpand0001_01_02_000503_add_user_foreign_key_to_kit_user_state_table.phptake the type fix below. It's a type-only change, so there's nothing to migrate: a host that already ran them keeps its schema, andkit:update plan's note that a changed migration won't reach the database needs no action here. - Run
kit:update plan|applyforworkspace-ui(re-pinnedsha256:2f226d38…39e7→sha256:5d746402…9202):Workspace/Placeholder/Page.vue,Workspace/Settings/Placeholder/Page.vueandWorkspace/Settings/People/Page.vuetake the type fix below. No behaviour changes. - The capability fix below is in the package and re-pins nothing. A host that adopted
settingsonly so People would open, or so owners could manage others' MCP connections, no longer needs it for that. Keep it if you use its General section or its MCP settings tools, and for the AI assistants page whilekit.mcp.connection_abilityis the defaultsettings.read(kit:statusflags this). - The kit now refuses to boot when an ability is gated on a capability no adopted feature declares, naming the ability, the capability and the feature. A host whose own provider calls
Registry::abilities()keeps booting: the check covers what the kit's features contribute. kit:statusnow flags declared permissions missing from the database as a blocking step.php artisan migrate --forceputs them back.- Run
kit:update plan|applyforcore(re-pinnedsha256:e67b1978…efa5→sha256:145cf85d…412e),mcp(re-pinnedsha256:e35e2879…7ae0→sha256:760d2e0a…d924),workspace-ui(re-pinnedsha256:5d746402…9202→sha256:0e1a0d0e…3320) andnotes(re-pinnedsha256:1a0455e7…551b→sha256:cc6e2adb…bb95):DataTable.vue,ActivityEntryRow.vue, the Connections, People and Security pages andNoteCard.vuetake the table and date fixes below. A host that copiestypes/kit.d.tsre-copies it:TableColumnhasdate. Nothing to migrate.
Tests
- The resources check (
composer test:resources) no longer copiesvendor,node_modules,browserortypecheckinto each disposable package copy. It reads onlysrcandresources. All 15 tamper cases still run, and the check goes from about 50s to about 6s locally. CI gives each test step its owntimeout-minutes(4 for the composer checks, 6 for the browser run), so a hang fails its own step quickly instead of holding the job until its 10 to 15 minute limit. - Run
kit:update plan|applyforsession-ui(re-pinnedsha256:c5b8919e…f94e→sha256:fae5ea29…bcb6): this addsAuth/Register/Page.vue; hosts with their own register page can drop it.Auth/Login/Page.vuetakes the "Create an account" link below. A host that copiestypes/kit.d.tsre-copies it:LoginViewModelhasregister, andRegisterViewModelis new. Registration stays closed by default, so nothing changes until an app opens it.
Added
-
Deleting a workspace asks for your password (docs/lifecycle-events.md › Delete a workspace). Settings › Delete workspace now requires the current
passwordwith the typed name. A missing or wrong one is a 422 and nothing changes. Account deletion already asked for it. Both are on by default.kit.lifecycle.confirm_password(KIT_DELETE_CONFIRM_PASSWORD) turns them off, but only when it isfalseexactly: any other value keeps the password required. -
A host can keep the user row when an account is deleted (docs/lifecycle-events.md › Keeping the user row). Bind
Tey\Kit\Features\SessionUi\Contracts\DeletesAccountRow::delete(Model $user)to anonymise the row instead of deleting it, for tables that keep history on RESTRICT foreign keys to users. Unbound, the user is hard-deleted as before. After the kit's rows go, an anonymised row keeps its Concierge account deactivated, so it never signs in. Itskit_account_deletionsrecord is markedfinished_at, andFinishAccountDeletionreturnsfalseon a retry instead of failing. -
Adopt-in-place for the access and settings tables. A host's own
entitlementsandcomplimentary_grantstables, and spatie/laravel-settings' table, are adopted when compatible (the columns the kit reads and writes in a compatible type, nullable where the kit writes null, an auto-incrementingid, the kit's unique index, and no other required host column on a table the kit inserts into): nothing is created or changed on them, and each is recorded inkit_adoptions. Settings then creates onlysettings_intents. Plans list each underadoptions, andkit:adopt --jsonwith no feature lists each such table undertablesasadopted,adoptableorconflict. See Adopting existing access and settings tables.composer test:table-adoptcovers it on SQLite and PostgreSQL. -
max-w-2k(160rem), the wide page measure the apps share, as a--container-2ktoken inkit.cssunder@theme default, so a host's own value wins. -
/mcp storm guards (
kit.mcp.limits,kit.mcp.list_cache_seconds). An assistant that reconnects in a burst no longer fills every PHP worker. Each access token may have 4 requests in flight and 120 a minute; over either, the request gets a 429 withRetry-After(a JSON-RPC error body) before it does any work. An app-wide in-flight cap (total) is off by default: set it below your PHP worker count. Slots live in the cache store and free when the request ends, or afterhold_secondsif the worker died.tools/listanswers are reused per connection for 60 seconds; a call still checks its tool's gate, so a list stale by a permission change only lists. A host that publishedconfig/kit.phpgets these defaults without adding the keys.composer test:mcp-limitscovers both. -
Deferred workspace and account deletion ([docs/lifecycle-events.md › Deferred deletion](docs/lifecycle-events.md#deferred-d...
v0.5.117
Upgrading
- Run
kit:update plan|applyforcore(re-pinnedsha256:727e9746…192e→sha256:b4ae55de…cb8b):Kit/Form/submitForm.ts,Kit/Form/formFrame.ts,Kit/Form/FormActions.vue,Kit/Workbench/Workbench.vueandKit/Workbench/workbench.tstake the Save gate and the status fix below, and the kit'suilang file gainsform.submit_blocked.*. A host that disabled Save by hand during an upload, or watched its form to clear "Not saved", can drop that.
Added
- Save is disabled while it can't succeed (docs/submit-form.md › When Save is disabled).
FormActionsand a Workbench with:formdisable Save, and ignore Enter and ⌘S, while pending work is in flight (an upload), a 409 stale-write conflict is open, or the host'scanSubmitis false.canSubmitis a new prop on both (can-submit) and auseSubmitFormoption, a boolean,Refor getter,trueby default; while it's falsesubmit()resolvesfalsewithout calling the handler. The button carriesdata-blockedand anaria-describedbywith the reason, fromkit::uiform.submit_blocked.*("Waiting for uploads to finish").submitBlock()andsubmitBlockMessages()are exported.tests/js/submit-form.test.mjsandworkbench-form.test.mjscover each reason; the browser job checks a pending upload,can-submitfalse and an open 409 on the Workbench.
Fixed
- A failed save no longer stays "Not saved" after further edits (docs/workbench.md › Form frame). A Workbench with
:formwhose form was already dirty kept "Not saved" through every later edit. The next edit now puts the bar back to "Unsaved changes" (editedSaveState), for auseSubmitFormand auseFormalike. The browser job checks both. - Run
kit:update plan|applyforcore(re-pinnedsha256:727e9746…192e→sha256:4be76339…aa64),mcp(re-pinnedsha256:e35e2879…7ae0→sha256:760d2e0a…d924),workspace-ui(re-pinnedsha256:bb66501a…3253→sha256:429f11b2…67b5) andnotes(re-pinnedsha256:1a0455e7…551b→sha256:cc6e2adb…bb95):DataTable.vue,ActivityEntryRow.vue, the Connections, People and Security pages andNoteCard.vuetake the table and date fixes below. A host that copiestypes/kit.d.tsre-copies it:TableColumnhasdate. Nothing to migrate.
Fixed
- DataTable sits on the card surface. The table and its two empty states had no background, so rows showed the page ground. They now use
bg-card text-card-foreground, the sticky first column matches, and column headers no longer wrap ("Connected by"). - Dates show through DateTime, not as raw ISO values. A
TableColumn(andTableQuery::column()) takesdate, a DateTime format, and DataTable renders that column throughDateTime: the viewer's zone, the full value intitle. AI assistants' Connected (a raw2026-…T…+00:00) and Expires, People's joined, sent and expiry dates, Security's sessions and log, Notes and activity entries now all use it. - Removing one MCP grant is no longer styled destructive. On AI assistants only Revoke stays destructive; "Remove " is a plain row action.
- Avatar upload and removal work on PostgreSQL with string or uuid morphs. Avatars compared
media.model_id(varchar there) with an integer, a 500 ("operator does not exist: character varying = integer"). The lookups and the avatar holder's media relation now bind the id as a string.
What's Changed
- DataTable card surface, date columns, nowrap headers; plain grant removal; string avatar model_id (0.5.x) by @jaspertey in #516
- Disable form Save while it can't succeed; clear Not saved on the next edit (0.5.x) by @jaspertey in #502
- Release v0.5.117 by @jaspertey in #529
Full Changelog: v0.5.116...v0.5.117
v0.5.116
Fixed
- A request no longer queries the database schema to learn whether the workspaces table has
admittedanddeleting_at. The kit asked once per request, since PHP-FPM dropsstaticmemos between requests, and asked again each time a column was missing. A column found is now cached (kit-workspace-columns, in the default cache store) until the nextmigrateforgets it. One not found yet is still asked again, so a table migrated later is seen at once.
What's Changed
- Cache the workspace column checks across requests by @jaspertey in #525
- Release v0.5.116 by @jaspertey in #527
Full Changelog: v0.5.115...v0.5.116
v0.5.115
Added
- /mcp storm guards (
kit.mcp.limits,kit.mcp.list_cache_seconds). An assistant that reconnects in a burst no longer fills every PHP worker. Each access token may have 4 requests in flight and 120 a minute; over either, the request gets a 429 withRetry-After(a JSON-RPC error body) before it does any work. An app-wide in-flight cap (total) is off by default: set it below your PHP worker count. Slots live in the cache store and free when the request ends, or afterhold_secondsif the worker died.tools/listanswers are reused per connection for 60 seconds; a call still checks its tool's gate, so a list stale by a permission change only lists. A host that publishedconfig/kit.phpgets these defaults without adding the keys.composer test:mcp-limitscovers both.
What's Changed
- Guard /mcp against reconnect storms (0.5.x) by @jaspertey in #523
- Release v0.5.115 by @jaspertey in #524
Full Changelog: v0.5.114...v0.5.115
v0.7.6
Upgrading
- Run
kit:update plan|applyforcore(re-pinnedsha256:5b4af9a6…b7df→sha256:b7a9b9ba…5e9d): this addsKit/Form/pendingWork.ts, andKit/Form/formFrame.ts,Kit/Form/FormActions.vue,Kit/Form/BulkDrop.vue,Kit/Workbench/Workbench.vueandKit/index.tstake the leave-guard changes below. A host with a hand-rolled Back/Forward or pending-upload guard beside a kit form frame can drop it, or keep it withwarnOnLeave: false.
Added
- Pending work counts as unsaved (docs/submit-form.md › Pending work).
usePendingWork()is the page's registry of work the form doesn't see yet:track(promise)holds it until it settles,begin()returns an idempotentend(), andpendingandcountare readable. While any is registered, the leave guard ofFormActions,FormCardand a Workbench with:formtreats the page as unsaved, even with a clean form. BulkDrop'sdirectuploads register themselves.createPendingWork()makes a separate registry. Both are exported from the kit barrel.tests/js/pending-work.test.mjscovers the registry.
Fixed
- The leave guard holds Back and Forward (docs/submit-form.md › Leaving the page). It caught only Inertia GET visits and closing the tab, so Back from a dirty form left it without asking. Where the browser has the Navigation API, a same-document traverse is now cancelled and asked about, and made again on Leave. Elsewhere the guard keeps one extra history entry for the page while it's unsaved, so Back lands on the page itself and asks; the entry is taken off when the page is clean, when a save starts and before a confirmed visit, so no duplicate entry is left behind. Stay keeps the page and its edits; Leave always goes.
- A save's own follow-up visit is no longer held. The guard let a visit through only in the same task as the form's submit event, so a
router.visit()inonSuccess, where Inertia'suseForm()is still dirty, asked "Leave this page?" after a successful save. The guard is now saving from the submit (orprocessingturning on) untilprocessingis off and every visit started meanwhile has finished. The browser job checks all of this on the real@inertiajs/vue3(leave.html): Back dirty and clean, with and withoutwindow.navigation, an upload in flight, and a save whoseonSuccessvisits another page.
What's Changed
- Leave guard: Back/Forward, pending work, and the save's follow-up by @jaspertey in #490
- Release v0.7.6 by @jaspertey in #492
Full Changelog: v0.7.5...v0.7.6
v0.7.5
Upgrading
- Run
kit:update plan|applyforcore(re-pinnedsha256:9a290690…555b→sha256:bda1ecaa…c43e) andsession-ui(sha256:c59375ef…dd74→sha256:287b46fe…d2ed):Kit/Form/PasswordInput.vuefollows its form's reset, andAuth/Login,Auth/ForgotPassword,Auth/ResetPassword,Auth/ConfirmPasswordandAuth/VerifyEmailtake the two fixes below. - tey/standards ^0.11.54 is required. Earlier releases reject the adoption inventory's new
features.<id>.composerkey (exit 2), so kit raises its floor with it. - Run
kit:update plan|applyforcore(re-pinnedsha256:bda1ecaa…c43e→sha256:87f1548e…8cde):Kit/Form/submitForm.tsaddshttpSubmit, andKit/index.tsexports it. A handler that awaits Inertia'suseHttpdirectly should move tohttpSubmit(below). - Run
kit:update plan|applyforcore(re-pinnedsha256:87f1548e…8cde→sha256:5b4af9a6…b7df):Kit/Form/Slider.vueis new,Kit/Form/RadioCardGroup.vuegains theoptionslot, image tiles andcolumns, andKit/contract.tsandKit/index.tsexport them.RadioCardOptionnow needs alabelor animage(withalt); an option with a label is unchanged. - Run
kit:update plan|applyforsession-ui(re-pinnedsha256:287b46fe…d2ed→sha256:abbeb463…c64f):Auth/Login,Auth/ForgotPassword,Auth/ResetPassword,Auth/ConfirmPasswordandAuth/VerifyEmailtake the type fix below. No behaviour changes.
Fixed
- A rate-limited auth submit no longer opens Inertia's error modal. The guest auth pages handed their 429 handler to
<Form>as@http-exception, but<Form>before Inertia 3.8 has noonHttpExceptionprop: the listener was never called, so Laravel's 429 page (fromthrottle:) still opened in Inertia's modal. The pages now pass it as the visit option (:options="{ onHttpException }"), which every Inertia 3<Form>honours, and the in-card "Too many requests" alert shows. The browser checks now run these pages on the real@inertiajs/vue3(3.7.1) and shadcn-vue Input, answering each submit with Laravel's 429 HTML or an Inertia response with errors. - A failed sign-in, reset or confirmation really clears the password.
reset-on-errorresets the input in the DOM only, andPasswordInputkept its own value, so the next re-render (showing the password, an error appearing) wrote the old password back. The pages now hold their password fields (v-model) and clear them on an error or a 429, andPasswordInputfollows its form'sresetevent (a native reset, or Inertia's whole-form reset), so a host form's reset clears it too. - The adoption inventory lists each feature's composer dependencies.
resources/adoption/surfaces.jsonfeatures.<id>.composernow carries every feature'sfeature.jsonrequires.packageskeys (lessphpandlaravel/framework), and the schema allows the key. tey/standards readspatie/laravel-multitenancyin an app's composer.json as a hand-rolledworkspaces.tenancysignal, because kit carries it only inrequire-dev; tey/standards 0.11.54 exempts a package an installed feature declares here. The drift test rebuilds the list from the feature.json files. - A
useSubmitFormhandler over Inertia'suseHttpno longer reads a 422 as a success.useHttpdoesn't reject on a validation failure: it callsonErrorand resolvesundefined, so a handler that awaited it returned normally and the form showed Saved for a refused save. The docs said auseHttperror is read from what it throws, which is only true of the other statuses; they now say to usehttpSubmit(docs/submit-form.md › With Inertia's useHttp). - The guest auth pages type-check on every Inertia 3. They bound
:options="{ onHttpException: tooMany }"inline, and<Form>'soptionstype (FormComponentOptions, aPickofVisitOptionswithoutonHttpException) failed vue-tsc's excess-property check (TS2353) in a host. Each page now types the handler as the visit'sonHttpExceptionand widens it to<Form>'soptionstype (:options="formOptions"), both read off@inertiajs/vue3's ownFormandrouter.visit, so no stub imports@inertiajs/core; at runtime<Form>still spreads it into the visit. The components type-check (bin/typecheck-components.sh, CI's vue-tsc job) now covers these five pages and runs a second time against@inertiajs/vue33.7.1 (installed as@inertiajs/vue3-37,typecheck/tsconfig.inertia-3.7.json) as well as the pinned current version.
Added
httpSubmit((data, hooks) => request), auseSubmitFormhandler over auseHttprequest: pass it the hooks it gives you (http.post(url, hooks)). A 422 rejects with aSubmitErrorof the field errors; a 409stale_writerejects as the conflict (form.conflict); any other status, a network failure, and a resolve ofundefinedwith noonErrorreject with the generic message. Only a 2xx resolves, with the response data. Exported from the kit barrel.node --test tests/js/submit-form.test.mjscovers each outcome over a stand-in foruseHttp3.9.1, including a bareuseHttphandler that reads a 422 as success.<RadioCardGroup>tiles (docs/radio-card.md). Theoptionslot ({ option, checked, disabled }) replaces a card's body while the native radio, its label and the focus ring stay the kit's. An option withimage: { src, alt }is an image tile: a lazy, async image in a squareobject-coverbox, so ~100 tiles load without layout shift, named by its label or its image'salt. A checked image or slot tile shows the primary ring and anaria-hiddencheck mark, so the state isn't colour alone.columns(2,3,4,6orauto, at least 7rem a tile) lays the cards in a grid. A disabled option stays a native disabled radio the arrow keys skip.<Slider>(docs/slider.md), a native range input:v-modelnumber,min,max,step,disabled,invalid,valueTextforaria-valuetext,inputemitted live andchangeon release, andshow-valuefor a visible<output>linked byfor. Caller attributes land on the input; in a Field it is described by the Field's description and error; in a locked LockedForm it is read-only.- The adoption inventory counts
Sliderunderform.inputs, and a raw<input type="range">in a Vue or Blade template as hand-rolled (it also marks the apphas-forms).
What's Changed
- Keep a 429 out of Inertia's modal and clear auth passwords for real by @jaspertey in #478
- List each feature's composer dependencies in the adoption inventory by @jaspertey in #480
- Fail useSubmitForm on a useHttp 422 with httpSubmit by @jaspertey in #483
- Add RadioCardGroup option slot, image tiles and columns, and a Slider by @jaspertey in #439
- Type the auth pages' onHttpException for every Inertia 3 by @jaspertey in #485
- Release v0.7.5 by @jaspertey in #488
Full Changelog: v0.7.4...v0.7.5
v0.7.4
Upgrading
- Run
kit:update plan|applyforsession-ui(re-pinnedsha256:592e21c4…7353→sha256:c59375ef…dd74):Auth/Login,Auth/ForgotPassword,Auth/ResetPassword,Auth/ConfirmPasswordandAuth/VerifyEmailtake the auth-state fixes below.ResetPasswordViewModelgainsstatus; a host that hand-copiestypes/kit.d.tscopies it again. A host overridinglang/vendor/kit/{locale}/ui.phpcan addform.throttledandform.throttled_retry. kit.people.hide_grantsneedstey/concierge0.9.3 or later, forMembersIndex::scopeMembersUsing(). With no hidden grants nothing changes on any Concierge the workspaces feature allows (^0.8.4 || ^0.9); with some on an older one, the app refuses to boot ("kit.people.hide_grants needs tey/concierge 0.9.3 or later"), never a page. The kit's own checks now resolve Concierge^0.8.4 || ^0.9(was ^0.8.4), the range the workspaces feature already allows. No stub changes, no re-pin.
Added
kit.people.hide_grants: a list of Concierge grants (roles) whose holders the people lists leave out, such as a support role (docs/dashboard.md › Hiding people by grant). A member holding one of them in the workspace is absent from Settings › People (rows and the Members count alike), the People widget and people search, and their own Change role and Transfer ownership pages, role change and removal by id are a 404. The default is[]; a value that isn't a list of names is refused at boot.composer test:people-hide-grants(new, in CI) covers each surface.
Changed
- The auth forms validate in the card, not in the browser.
Auth/Login,Auth/ForgotPassword,Auth/ResetPasswordandAuth/ConfirmPasswordcarrynovalidate, so the server's field errors render in the card (an address like "nobody" gets the "valid email address" error) instead of the browser's own bubble. The fields keeptype="email",requiredandautocomplete.
Fixed
- The guest auth pages keep a rate-limited submit in the card. A 429 from a limiter (Fortify's login limiter, the verification resend, a reset or reset-link request) opened Inertia's error modal over the page.
Auth/Login,Auth/ForgotPassword,Auth/ResetPassword,Auth/ConfirmPasswordandAuth/VerifyEmailnow refuse the modal for a 429 only (onHttpExceptionreturns false), clear the stale field errors and show an in-card alert (role="alert"): "Too many requests. Try again in N seconds." when the response carriesRetry-After, else "Too many requests. Try again in a moment." The copy is in the kit'suilang file (form.throttled,form.throttled_retry). Any other HTTP error still gets Inertia's modal; nothing the server allows or limits changes. - A failed sign-in, reset or confirmation clears the password and marks the field.
Auth/Login,Auth/ResetPasswordandAuth/ConfirmPasswordreset their password fields on error, setaria-invalidon the fields with an error, and move focus to the first one (the email after bad credentials). - A bad or expired reset token is visible. Fortify reports it on
email, a hidden field onAuth/ResetPassword, so nothing showed. It now shows as an in-card alert with a "Request a new link" link to/forgot-password. - The reset-password page's status comes from its view model.
ResetPasswordViewModeltakesstatus(the flashed session status), so theFormStatuson that page shows the server's status without the host supplying it. - The sign-in page focuses the email field on load, as
Auth/ConfirmPasswordalready focuses the password.
What's Changed
- Keep session-ui auth failures and 429s in the card by @jaspertey in #453
- Hide members by Concierge grant in the people lists by @jaspertey in #462
- Release v0.7.4 by @jaspertey in #476
Full Changelog: v0.7.3...v0.7.4
v0.7.3
Upgrading
- Run
kit:update plan|applyforcore(re-pinnedsha256:909d632f…822e→sha256:9a290690…555b):feature.jsonaddsclass-variance-authorityandtw-animate-csstorequires.npm. No stub changes. - The dev floor for tey/standards is now
^0.11.52, the first release that reads an inventory'sfeaturesblock.
Added
- CI guards the adoption inventory's completeness (contributor-facing).
composer test:adoption-completeness(tests/AdoptionCompletenessCheck.php, in the same CI job as the other adoption checks) fails when a page, layout or shell component a feature installs underresources/js/pagesis named by nosurfaces.jsonrow'suses.files,uses.componentsoruses.importsand has no entry in the newresources/adoption/internal.json, and when a row withhandRolledsignals has no adoption fixture expecting ithand-rolled(or anunprovenentry there). Staleinternal.jsonentries fail too, and every entry needs a reason. Rowsadoptedin no fixture are listed as a note. To pass,uses.filesnow also names the shell partials (UserMenu,WorkspaceSwitcher,Breadcrumbs,GlobalSearch,UserAvatar,Wordmark,FocusShellundershell.workspace;GuestShellandPublic/Markdownundershell.public), the remaining auth screens (ResetPassword,ConfirmPassword,VerifyEmail), the dashboard partials,Sections/Index,Search, the placeholder, error and no-workspace pages (workspace.pages),Settings/Delete(workspace.settings) and the People pages and invitation page (access.members);internal.jsonlists the sample notes pages and the welcome steps. New fixtures pin a hand-rolled outcome for the 19 rows that had none:public-shell-hand-rolled,split-hand-rolled,search-hand-rolled,action-bar-hand-rolled,menu-hand-rolled,modal-hand-rolled,rich-text-hand-rolled,list-hand-rolled,badge-hand-rolled,avatar-hand-rolled,alert-hand-rolled,activity-hand-rolled,timezone-hand-rolled,auth-hand-rolled,passkeys-hand-rolled,teams-hand-rolled(workspace.pages,access.members,workspaces.tenancy) andmcp-hand-rolled. No stub changes, no re-pin.
Changed
- A workspace without the dashboard opens its first navigation page. With
workspace.dashboardoff, the workspace root (/@{workspace}, and/in the short form) was a 404. It now redirects (302) to the first primary navigation page the viewer may open, or to/me/workspaceswhen there is none. At the kit's home, a person with several workspaces still goes to the root of the one they last used, so with the dashboard off they land on its first page instead of the workspace list (docs/dashboard.md › Without a dashboard). Give your pageorder< -10 to make it first. Only the capability being off redirects: a role that lacksdashboard.read, a non-member and a JSON client are refused as before. An accepted invitation's landing uses the same lookup, unchanged. No stub changes, no re-pin.composer test:workspace-root(new, in CI) covers it;test:capability-guardnow expects the root's redirect.
Fixed
-
standards:adoption no longer reads the kit's own dependencies and host UI as gaps.
resources/adoption/surfaces.jsonnow carries a top-levelfeaturesobject: for each feature withrequires.npmorrequires.host_ui, its npm package names and its host UI primitives, exactly as itsfeature.jsonlists them. tey/standards 0.11.52 treats those packages as the kit's (never a package.json gap or a third-party import) and thosecomponents/uifiles as kit-owned (never an app component, gap or hand-rolled hit) for each installed feature. The schema allows the field, and the inventory test fails when it drifts from thefeature.jsonfiles. -
core declares every npm package its components need.
Kit/Feedback/alertVariants.ts(like the shadcnbuttonit pairs with) importsclass-variance-authority, and the kit's menus, toasts and select animate withtw-animate-cssutilities (animate-in,fade-in-0,zoom-in-95); neither was inrequires.npm, so a host without them got no plan attention for it. Both are now required, at^0.7.1and^1.4.0. -
The adoption inventory reads hand-rolled account and settings pages wherever they live.
account.pagesandworkspace.settingsapplied only when a route URI saidsettings, so an app serving its own account page at/account, or workspace settings at/w/{workspace}/deletion, readn/a. Two facts computed from file names close that:has-account-pages(pages underAccount/,Me/,Profile/,Security/,Preferences/,Settings/{Account,Profile,Security,Password,Preferences}/, and*AccountSettings*,*ProfileSettings*,*UserSettings*,*UserPreferences*pages) andhas-workspace-settings-pages(Teams/Show.vue,Team*/Settings/,Workspace*/Edit,Workspace*/Deletion/,Organisation/Organizationsettings,Settings/Workspace*,Settings/Team*, and*TeamSettings*,*WorkspaceSettings*,*OrganizationSettings*pages). Both read inside tey/mod module page folders too, and never flag the kit's ownWorkspace/Account/**andWorkspace/Settings/**pages orcomponents/ui/**. A domain component such asSlideSettings.vuematches none of them.account.pagesalso counts the kit's Password and Delete pages as adoption. -
New surfaces:
settings.frameandmcp.connections.settings.frameis the account and workspace settings frame (Workspace/Account/Layout.vue,Workspace/Settings/Layout.vue); an app's ownAccount/Layout.vue,Settings/Layout.vue,Settings/Indexorlayouts/settings/**reads hand-rolled.mcp.connectionsis the AI assistants page (Workspace/Settings/Connections/Page.vue);*Connection*,*ConnectedApp*,*Assistant*and*Token*pages underSettings/,Account/,Profile/orMe/,ConnectedApp*/,ApiTokens/and Jetstream'sAPI/pages, and an app controller, view model, action or Livewire class referencing Passport'sToken/TokenRepositoryor Sanctum'sPersonalAccessTokenread hand-rolled. -
platform.timezoneflags an app's own timezone picker. A*Timezone*.vueoutside the kit's components and Welcome timezone pages reads hand-rolled. The kit ships no passkey management page (only thePasskeyOffer), soauth.passkeysis unchanged.
The fixtures account-hand-rolled, account-kit-installed and slide-settings-domain pin these outcomes. No stub changes, no re-pin.
What's Changed
- Declare feature npm and host UI deps in the adoption inventory by @jaspertey in #465
- Read hand-rolled account and settings pages in the adoption inventory by @jaspertey in #468
- Guard the adoption inventory's completeness in CI by @jaspertey in #470
- Open the first navigation page at a workspace root without a dashboard by @jaspertey in #457
- Release v0.7.3 by @jaspertey in #473
Full Changelog: v0.7.2...v0.7.3
v0.7.2
Changed
- A person with several workspaces lands on the one they last used. The kit's home (
kit.home) sends a signed-in person with several workspaces to the workspace they last opened (the session's current one), while they are still an active member and may open its dashboard; with none, or a stale one (removed, suspended, not admitted, deleted), it is the workspace list as before. For a signed-in person the logo (shell.product.url) andshell.links.homenow link tokit.hometoo, not/me/workspaces; the switcher and a guest's shell are unchanged. One workspace still shows its dashboard there, and a guest still signs in first. Sign-in lands onkit.homewith workspace-ui too: the kit'sfortify.homedefault is nowkit.homewith or without workspace-ui (it was/workspaces, the list). A host with its ownconfig/fortify.phpkeeps itshome; set it to yourkit.home(/by default) to land on the last workspace. No stub changes, no re-pin.
What's Changed
- Home: a person with several workspaces lands on the one they last used by @jaspertey in #455
- Release v0.7.2 by @jaspertey in #460
Full Changelog: v0.7.1...v0.7.2