Skip to content

fix(agreements): Signing-tab send accepts non-UUID agreementId/inspectionId#131

Closed
important-new wants to merge 10 commits into
InspectorHub:mainfrom
important-new:fix/agreement-send-non-uuid
Closed

fix(agreements): Signing-tab send accepts non-UUID agreementId/inspectionId#131
important-new wants to merge 10 commits into
InspectorHub:mainfrom
important-new:fix/agreement-send-non-uuid

Conversation

@important-new

Copy link
Copy Markdown
Contributor

Problem

Track I-a's SendAgreementSchema (Signing-tab / admin send) validates agreementId and inspectionId with .uuid(). But agreements.id and inspections.id are TEXT columns and may hold non-UUID values — e.g. Spectora-imported or seeded rows. So a tenant who imported templates/inspections can send via the inspection hub (already fixed in inspection.schema.ts, #111) but gets a 400 from the Signing-tab send. The handler resolves both ids by tenant-scoped lookup, so .uuid() only rejects legitimate rows — it adds no safety.

Fix

Relax both to z.string().min(1), matching the hub-send fix and rationale. Adds a regression test that sends with non-UUID agreementId/inspectionId and asserts 200.

Surfaced while reconciling the feat/inspection-hub (#111) branch against the just-merged Track I-a (#130) during post-merge cleanup — #111 had independently fixed the identical class of bug on the hub path.

Testing

agreement-send-endpoints 12/12 · type-check:api 0 · pre-commit gates (lint/DS/erasure-manifest/bundle) green.

🤖 Generated with Claude Code

important-new and others added 10 commits June 6, 2026 21:51
The DB stores dates as ISO datetimes (e.g. '2026-05-29T09:00:00'). This
value was loaded verbatim into the form, causing two problems:
1. <input type='date'> could not parse it (showed empty placeholder)
2. sanitizeSettingsPatch bypassed the date→ISO conversion, so the API
   received a datetime without a timezone suffix, failing z.string().datetime()

Fix: strip the time component on load so the date picker gets 'YYYY-MM-DD',
which sanitizeSettingsPatch then correctly converts to a Z-suffixed ISO string.
Applied to closingDate too for consistency.
api.team.members returns {data:{members:[...]}} — the loader was reading
body.data (an object) and calling .filter() on it, causing a TypeError crash.
Align with the BFF sheet route which correctly reads data?.members.
…ings BFF

pageSize: "200" fails Zod refine (valid: 12/25/50/100); the .catch(() => null)
silently swallowed the 400 so the template dropdown was always empty.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lates & dashboard

- TemplateCombobox: new reusable combobox with debounced search, cursor
  pagination (25/page), keyboard nav, load-more; replaces <select> in
  InspectionSettingsSheet (also fixes pageSize 200->100 Zod refine bug)
- /resources/template-search BFF: token-relay loader for TemplateCombobox
- templates.tsx: upgraded from client-side useMemo to URL-based server-side
  search (?q=) with 350ms debounce; sort remains client-side
- server: listTemplates() gains optional q param with LIKE filter;
  listTemplatesRoute schema extended with q; exactOptionalPropertyTypes fix
- /resources/inspection-search BFF: full cross-all-inspections search
  via GET /api/inspections with search+cursor params
- dashboard.tsx: searchQuery now triggers server-side BFF fetch (300ms
  debounce) instead of client-side bucket filter; load-more for results

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tionId (TEXT ids)

The Track I-a SendAgreementSchema gated agreementId + inspectionId with
.uuid(), which 400'd legitimate non-UUID rows (Spectora-imported / seeded
templates and inspections — agreements.id and inspections.id are TEXT, the
handler resolves them by tenant-scoped lookup). Relaxes both to .min(1),
matching the hub send fix in inspection.schema.ts. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@important-new

Copy link
Copy Markdown
Contributor Author

Closing — wrong direction. Verified production agreements.id / inspections.id are ALWAYS canonical UUIDs (crypto.randomUUID() at every create site; Spectora import preserves external ids only for template-internal items, never PKs). The only non-UUID ids are test seeds. So .uuid() validation is correct and protective; relaxing it is an unnecessary compat shim, against the project's pre-launch-no-compat principle. Instead we'll re-tighten #111's relaxation and canonicalize test seeds (separate PR).

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