feat: agent CRM directory, seat-invite cancel/resend, M20 removal, eslint hardening#261
Merged
important-new merged 15 commits intoJul 21, 2026
Merged
Conversation
…gs-widget, settings-catalog-booking, verify-token, concierge-expired, agent-invite-expired) Pre-existing cleanup from the orphan-route audit; rides the same release as the agent-CRM / invite-cancel / IdentitySwitcher-removal workstreams. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
… routes
The two new /api/team/invites/{token}[/resend] routes (Task 1) added MCP-exposed
operations; the committed snapshot must reflect them. Missed earlier because
Task 1 ran a focused suite; surfaced by the full test:unit in Task 3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
…ad-only agent email
- PillTone (hub-blocks.ts) + CreateInspectionFromWizardInput (wizard.schema.ts): genuinely used via inline import type refs that knip cannot trace — kept exported, marked @public (knip honors the tag). The plan's drop-export/static-import approach was wrong: dropping PillTone's export broke inspection-hub.tsx, and static imports pushed two already-capped service files over the file-size ratchet. - TotpRegenerateSchema alias collapsed into TotpDisableSchema (identical schema). - Server formatDate/formatTime made statically visible to knip via a void[] ref in the parity test (used dynamically as (srvFmt as any)[fn]). Baseline is now []. No behavior change; no file-size/OpenAPI-snapshot impact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
…/JWT guards (warn-first rollout) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
…-strategy Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
…OCTOU) An invite accepted between cancelInvite's existence SELECT and its DELETE would get its now-historical row hard-deleted. Fold the status check into the DELETE where-clause so the race no-ops instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
…s, promises) Fast-follows surfaced by the final review, folded before release: - team: getMembers now selects users.name and TeamMembersResponseSchema exposes it, so /team active rows render the real name instead of "Unnamed". - editor/ItemEditor: the visibleTabs derivation sat after the `if (!item)` early return as a useMemo — a conditional hook (react-hooks/rules-of-hooks). Demote it to a plain const (a 3-id map isn't worth memoizing). - contacts: the loader filtered contacts server-side by `?type=`, starving the Agents tab on a `?type=client` deep-link. Fetch the full list; both tabs already filter locally (Contacts by state seeded from ?type=, Agents by type). - i18n: drop 3 orphaned keys left by the Agents-table rewrite (contacts_agents_col_status / _col_linked / _revoke; zero code refs). - lint(no-misused-promises): set checksVoidReturn.attributes:false — async JSX event handlers are idiomatic React; this removed 44 false positives (45 -> 1). - durable-objects/inspection-doc: `void` the ctor blockConcurrencyWhile — the one genuine server-side floating promise (the runtime gates on it internally; a ctor cannot await). The other 149 no-floating-promises are RR fetcher/ navigate idioms with no server-executed mutations; left as warn. - test(magic-login): bump the whole-app-graph allowlist test's timeout 30s -> 60s so it can't flake under concurrent CI load. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
Pre-launch policy (drizzle.config.ts): the schema is the single source of truth and forward migrations collapse into one baseline. Regenerated migrations/0000_baseline.sql from server/lib/db/schema after clearing the forward migrations + meta; migrations/data/ (severity-normalization data migration) is preserved. The fresh baseline drops user_identity_links (removed from the schema when the dormant M20 IdentitySwitcher was deleted) with no standalone drop migration — the squash omits it. Verified: 87/87 schema tables present (zero missing, zero orphans), db:check green (hand=87 == generated=87), wipe + fresh local apply clean (243 commands), worker boots and /login·/inspections·/book serve with no schema errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YiCgWAmqyzpfs3pp1kjg3
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.
Ships four independent people/access-surface fixes plus lint hardening and a migration squash:
cancelInviteDELETE re-assertsstatus='pending'(TOCTOU-safe).?type=clientdeep-link that starved the tab.hono/jwtboundary guards; fixed one conditional-hook bug and one real server-side floating promise.0000_baseline(pre-launch policy);user_identity_linksdropped.🤖 Generated with Claude Code