Skip to content

feat: agent CRM directory, seat-invite cancel/resend, M20 removal, eslint hardening#261

Merged
important-new merged 15 commits into
InspectorHub:mainfrom
important-new:agent-crm-invite-cleanup
Jul 21, 2026
Merged

feat: agent CRM directory, seat-invite cancel/resend, M20 removal, eslint hardening#261
important-new merged 15 commits into
InspectorHub:mainfrom
important-new:agent-crm-invite-cleanup

Conversation

@important-new

Copy link
Copy Markdown
Contributor

Ships four independent people/access-surface fixes plus lint hardening and a migration squash:

  • Team seats: cancel + resend pending seat invites (owner/manager), with expiry badges and a confirm modal; cancelInvite DELETE re-asserts status='pending' (TOCTOU-safe).
  • Contacts / Agents: the Agents tab is now a clean CRM directory — soft-archive delete (hard-delete only when zero referrals), a Referrals count, agent-email immutability, and archived contacts stay visible to their referring agent (pinned by a regression test). Fixed a ?type=client deep-link that starved the tab.
  • IdentitySwitcher (M20) removed: the dormant identity list/switch/link surface is gone; GDPR account export/delete routes kept.
  • Dead-code + i18n: knip baseline zeroed; 3 orphaned message keys removed.
  • ESLint hardening (warn-first): react-hooks (rules-of-hooks error, 0 violations), type-aware promise rules, react/jsx-a11y, and app<->server + hono/jwt boundary guards; fixed one conditional-hook bug and one real server-side floating promise.
  • DB: migrations squashed into a single 0000_baseline (pre-launch policy); user_identity_links dropped.

🤖 Generated with Claude Code

important-new and others added 15 commits July 21, 2026 12:06
…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
- 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
@important-new
important-new merged commit 1209425 into InspectorHub:main Jul 21, 2026
6 of 7 checks passed
@important-new
important-new deleted the agent-crm-invite-cleanup branch July 21, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant