Skip to content

chore: clear type-cast backlog and promote to hard gate#2337

Merged
andrew-bierman merged 13 commits into
developmentfrom
feat/checks-backlog-casts
Apr 26, 2026
Merged

chore: clear type-cast backlog and promote to hard gate#2337
andrew-bierman merged 13 commits into
developmentfrom
feat/checks-backlog-casts

Conversation

@andrew-bierman
Copy link
Copy Markdown
Collaborator

Replaces #2317 (auto-closed when base branch merged).

Summary

  • Promotes check-type-casts from advisory to a hard CI gate (--strict flag)
  • Adds check-type-casts --strict to lefthook pre-push hook
  • Annotates all unsafe as unknown as T casts introduced by the Zod/Treaty migration across expo, packages, and apps
  • Restores one previously-removed cast in current-pack/[id].tsx with // safe-cast: annotation (Treaty response createdAt?: string vs Zod schema string)
  • Fixes Biome useTemplate rule in no-duplicate-guards.ts

Test plan

  • bun run check:casts:strict passes (no unsafe casts)
  • Pre-push hook runs check-type-casts --strict
  • All as unknown as casts have // safe-cast: annotation on the preceding line
  • Biome clean on all modified files

🤖 Generated with Claude Code

…e-positive fixes

- Exclude test files (/__tests__/, /test/) from cast scanning
- Extend safe-cast annotation lookback through consecutive comment lines
  (was 1 line; now scans the entire comment block preceding the cast)
- Add NATURAL_LANGUAGE_CAST pattern to skip content-string false positives
- Add ArrayBuffer, ReadableStream, SQL to SAFE_CAST_PATTERNS
…ditions

- computePackWeights: remove redundant WeightUnit cast
- PackDetailScreen/PackCard: annotation for Pack union narrowing
- usePackTemplateSummary: remove redundant casts
- TrailConditionReportCard: remove redundant casts
- treaty hooks: annotate with safe-cast where response shape matches schema
…ps, components)

- treaty response hooks: safe-cast annotations for shape-match casts
- ToolInvocationRenderer: inline safe-cast on each discriminated case branch
- Icon components: safe-cast for ComponentProps/SfSymbolName icon type coercions
- store.ts obs(): safe-cast for Legend-State Proxy access pattern
- guides generate page: type ContentCategory at Object.entries; DifficultyLevel uses assertEnum
- guides sync-to-r2: inline safe-cast for SyncEnv → ValidatedEnv subset
- landing icons: safe-cast for Lucide icon type narrowing
…, config, web-ui)

- env-validation: safe-cast annotations for Cloudflare Worker runtime bindings
- api middleware/services: safe-cast for treaty/generic boundary casts
- analytics: safe-cast for DuckDB row result types
- guards narrow.ts: assertEnum helper for enum type narrowing
- cli, config, web-ui: safe-cast for legitimate type boundary casts
Type-cast backlog cleared (130 → 0). Remove continue-on-error from CI
and add bun check:casts:strict to the pre-push clean-checks gate.
…acros

Adds scripts/lint/no-unauth-routes.ts which scans packages/api/src/routes
for Elysia route handlers (.get/.post/.put/.patch/.delete/.all) that lack
isAuthenticated/isAdmin/isValidApiKey and have no // public-route: annotation.

- Excludes the admin/ subtree (uses Cloudflare Access + Basic/JWT auth via
  onBeforeHandle, not Elysia macros)
- Annotates the 10 intentionally-public auth routes (login, register,
  verify-email, resend-verification, forgot-password, reset-password,
  refresh, logout, apple, google) with descriptive // public-route: reasons
- Wired into check-all.ts, lint:custom, lefthook.yml pre-push gate, and CI
- no-duplicate-guards.ts: use template literal for path separator (useTemplate)
- no-unauth-routes.ts: restructure while loop to for(;;) to avoid noAssignInExpressions
Treaty response type has createdAt as string | undefined but the
PackItem schema type requires string; the cast bridges this gap.
The /alltrails/preview endpoint is a link-preview proxy that fetches
OG metadata from AllTrails. No user data is accessed or modified,
so it intentionally omits auth macros.
Copilot AI review requested due to automatic review settings April 26, 2026 22:13
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 26, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1287480d-8a9b-49ba-91cb-bb2f6451edcd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/checks-backlog-casts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 26, 2026

Coverage Report for API Unit Tests Coverage (./packages/api)

Status Category Percentage Covered / Total
🔵 Lines 75.74% 609 / 804
🔵 Statements 75.74% (🎯 65%) 609 / 804
🔵 Functions 95.91% 47 / 49
🔵 Branches 88.23% 270 / 306
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/api/src/services/refreshTokenService.ts 0% 100% 100% 0% 2-176
packages/api/src/utils/auth.ts 98.93% 90.9% 100% 98.93% 52
packages/api/src/utils/compute-pack.ts 91.37% 76.19% 100% 91.37% 10, 14, 16, 30, 32
packages/api/src/utils/csv-utils.ts 95.43% 87.5% 100% 95.43% 66-67, 69-70, 88-89, 132-133, 149-150, 171-172
Generated in workflow #847 for commit 0f07bef by the Vitest Coverage Report Action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 26, 2026

Coverage Report for Expo Unit Tests Coverage (./apps/expo)

Status Category Percentage Covered / Total
🔵 Lines 81.4% 521 / 640
🔵 Statements 81.4% (🎯 75%) 521 / 640
🔵 Functions 92.85% 52 / 56
🔵 Branches 92.55% 199 / 215
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
apps/expo/features/packs/utils/computePackWeights.ts 100% 100% 100% 100%
Generated in workflow #847 for commit 0f07bef by the Vitest Coverage Report Action

@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Apr 26, 2026
@andrew-bierman andrew-bierman merged commit 9875eb9 into development Apr 26, 2026
8 of 11 checks passed
@andrew-bierman andrew-bierman deleted the feat/checks-backlog-casts branch April 26, 2026 22:15
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Promotes strict type-cast checking to a hard gate and clears the backlog by annotating intentional casts, while adding a new lint rule to detect unauthenticated Elysia routes.

Changes:

  • Harden check-type-casts (--strict) in CI and pre-push, and annotate intentional casts with // safe-cast:.
  • Add a new custom lint rule (no-unauth-routes) and integrate it into check:all, lint:custom, CI, and lefthook.
  • Fix a Biome useTemplate lint issue and remove a few unnecessary casts now that types align.

Reviewed changes

Copilot reviewed 76 out of 76 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
scripts/lint/no-unauth-routes.ts New lint script to flag Elysia routes missing auth macros or // public-route: annotation
scripts/lint/no-duplicate-guards.ts Biome useTemplate fix in excluded-path check
scripts/check-all.ts Runs the new unauth-route check as part of full check suite
packages/web-ui/src/components/form.tsx Adds // safe-cast: annotation for React context sentinel casts
packages/guards/src/narrow.ts Annotates cast used for Object.entries iteration after object narrowing
packages/config/src/config.ts Annotates cast used for Object.values iteration after isObject guard
packages/cli/src/shared.ts Annotates casts when printing tabular DB/DuckDB-like result objects
packages/checks/src/check-type-casts.ts Tightens strictness behavior (annotations, exclusions, safe patterns)
packages/api/src/utils/env-validation.ts Annotates Worker binding casts and global env priming casts
packages/api/src/utils/csv-utils.ts Annotates generic boundary cast for JSON parse
packages/api/src/utils/compute-pack.ts Removes WeightUnit cast (relies on updated typing)
packages/api/src/utils/auth.ts Annotates JWT payload cast as intentional boundary
packages/api/src/services/refreshTokenService.ts Annotates Drizzle SQL casts where combinators return `SQL
packages/api/src/services/r2-bucket.ts Annotates stream/ArrayBuffer/generic JSON boundary casts
packages/api/src/services/etl/processValidItemsBatch.ts Annotates cast after upstream validation guarantees
packages/api/src/routes/weather.ts Annotates typed casts on WeatherAPI JSON responses
packages/api/src/routes/packs/index.ts Annotates NewPackItem cast used due to inference mismatch
packages/api/src/routes/auth/index.ts Adds // public-route: annotations to auth endpoints
packages/api/src/routes/alltrails.ts Adds // public-route: annotation for preview proxy endpoint
packages/api/src/middleware/auth.ts Annotates AuthUser casts after JWT validation
packages/api/src/index.ts Annotates Worker entrypoint casts (env + app.fetch + queue batch narrowing)
packages/api/src/containers/AppContainer.ts Annotates module-level Workers env cast to typed Env
packages/api-client/src/index.ts Annotates error-field access cast and generic boundary cast on response
packages/analytics/src/core/spec-parser.ts Annotates DuckDB row-to-object casts
packages/analytics/src/core/local-cache.ts Annotates generic boundary cast for typed row objects
packages/analytics/src/core/entity-resolver.ts Annotates DuckDB row-to-object cast
packages/analytics/src/core/enrichment.ts Annotates DuckDB row-to-object casts
package.json Adds unauth-route lint script to lint:custom
lefthook.yml Adds unauth-route lint + strict cast check to pre-push gate
apps/landing/lib/icons.tsx Annotates dynamic Lucide icon lookup casts
apps/guides/scripts/sync-to-r2.ts Annotates env-cast used to satisfy R2BucketService runtime needs
apps/guides/components/search.tsx Annotates DOM event.target as Node cast
apps/guides/app/dev/generate/page.tsx Replaces enum casts with assertEnum and tightens Object.entries typing
apps/expo/lib/store.ts Annotates cast bridging Legend-State Proxy typing mismatch
apps/expo/features/wildlife/hooks/useWildlifeIdentification.ts Annotates cast after response guard validates array
apps/expo/features/weather/screens/LocationPreviewScreen.tsx Annotates cast for formatted weather data
apps/expo/features/weather/hooks/useWeatherAlert.ts Annotates cast for subset type used by alert generator
apps/expo/features/weather/hooks/useLocationSearch.ts Annotates cast for formatted weather location
apps/expo/features/weather/hooks/useLocationRefresh.ts Annotates casts for partial weather location updates
apps/expo/features/trips/screens/TripDetailScreen.tsx Annotates cast used across store hydration/guard boundary
apps/expo/features/trips/components/TripForm.tsx Annotates cast for useForm defaultValues inference
apps/expo/features/trail-conditions/hooks/useTrailConditionReports.ts Annotates JSON parse and Treaty response casts
apps/expo/features/trail-conditions/components/TrailConditionReportCard.tsx Removes enum casts where indexing accepts typed keys
apps/expo/features/packs/utils/computePackWeights.ts Removes WeightUnit cast (relies on updated typing)
apps/expo/features/packs/store/packs.ts Annotates Zod-validated response casts to store types
apps/expo/features/packs/store/packItems.ts Annotates Zod-validated response casts and adds parentheses for clarity
apps/expo/features/packs/screens/PackDetailScreen.tsx Replaces narrative cast comment with // safe-cast: annotation
apps/expo/features/packs/screens/ItemsScanScreen.tsx Annotates cast for untyped router params into SelectedImage
apps/expo/features/packs/hooks/useSeasonSuggestions.ts Annotates Treaty response cast
apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts Annotates Zod parse + cast divergence
apps/expo/features/packs/hooks/usePackGapAnalysis.ts Annotates Treaty response cast
apps/expo/features/packs/hooks/useImageDetection.ts Annotates Treaty response cast
apps/expo/features/packs/hooks/useDuplicatePack.ts Annotates Treaty response cast
apps/expo/features/packs/components/SimilarItemsForPackItem.tsx Annotates cast for untyped items field
apps/expo/features/packs/components/PackCard.tsx Replaces narrative cast comment with // safe-cast: annotation
apps/expo/features/pack-templates/store/packTemplates.ts Annotates Zod-validated response casts to store types
apps/expo/features/pack-templates/store/packTemplateItems.ts Annotates Zod-validated casts and adds parentheses for clarity
apps/expo/features/pack-templates/screens/ItemsScanScreen.tsx Annotates cast for untyped router params into SelectedImage
apps/expo/features/pack-templates/hooks/usePackTemplateSummary.ts Removes WeightUnit casts after typing alignment
apps/expo/features/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts Annotates Treaty error/value probing and response cast
apps/expo/features/catalog/screens/CatalogItemsScreen.tsx Annotates Treaty response casts and refactors pagination mapping
apps/expo/features/catalog/hooks/useSimilarItems.ts Annotates Treaty response cast
apps/expo/features/catalog/components/SimilarItems.tsx Removes unnecessary cast where data is already typed
apps/expo/features/catalog/components/CatalogBrowserModal.tsx Annotates Treaty response casts for popular/search/paginated items
apps/expo/features/auth/hooks/useAuthActions.ts Annotates Href string cast fallback + user cast to local type
apps/expo/features/ai/hooks/useReportedContent.ts Annotates Treaty response cast
apps/expo/features/ai/hooks/useReportContent.ts Annotates Treaty response cast
apps/expo/features/ai/components/ToolInvocationRenderer.tsx Annotates discriminated-union casts per tool type
apps/expo/features/ai/components/ChatBubble.tsx Annotates cast after tool- prefix check
apps/expo/features/ai-packs/hooks/useGeneratedPacks.ts Annotates Treaty response cast
apps/expo/components/Icon/get-icon-names.ts Annotates fallback/icon-name union casts
apps/expo/components/Icon/Icon.tsx Annotates icon-name union casts for runtime fallback chain
apps/expo/components/Icon/Icon.ios.tsx Annotates icon-name union casts for runtime fallback chain
apps/expo/app/(app)/current-pack/[id].tsx Restores cast with // safe-cast: for Treaty vs schema createdAt mismatch
apps/admin/lib/api.ts Annotates generic boundary cast for res.json()
.github/workflows/checks.yml Adds unauth-route check step and makes strict cast check a hard gate
Comments suppressed due to low confidence (8)

scripts/lint/no-unauth-routes.ts:1

  • Excluding an admin directory from the unauth-route audit creates a blind spot that can undermine the purpose of this hard gate (admin routes are often the most sensitive). If packages/api/src/routes/admin exists and should be checked, remove 'admin' from EXCLUDED_DIRS; if it must be excluded, add a clear comment explaining why and consider replacing this with a more explicit allowlist/denylist scoped to the actual route tree structure.
    scripts/lint/no-unauth-routes.ts:1
  • Scanning the entire route-call span for isAuthenticated/isAdmin/isValidApiKey can yield false negatives if those tokens appear inside the handler body (e.g., in an object literal, comment, or string) rather than in the route options. Since this script is now a CI gate, it should avoid missing public routes; consider parsing just the options argument (commonly the last argument) and only searching within that object literal (ideally via a TypeScript AST parse rather than regex on raw text).
    scripts/lint/no-unauth-routes.ts:1
  • Scanning the entire route-call span for isAuthenticated/isAdmin/isValidApiKey can yield false negatives if those tokens appear inside the handler body (e.g., in an object literal, comment, or string) rather than in the route options. Since this script is now a CI gate, it should avoid missing public routes; consider parsing just the options argument (commonly the last argument) and only searching within that object literal (ideally via a TypeScript AST parse rather than regex on raw text).
    scripts/lint/no-unauth-routes.ts:1
  • The parenthesis-walk is a text-level approximation and does not account for parentheses inside strings/template literals/regex literals, which can cause incorrect callEnd detection and therefore missed/incorrect violations. Given this is a security-oriented lint, consider switching to a TypeScript parser (e.g., TypeScript compiler API / ts-morph) to reliably locate CallExpressions and their argument lists; this will also make it much easier to isolate the options object and reduce false positives/negatives.
    scripts/lint/no-unauth-routes.ts:1
  • Computing callLine by slicing from the start of the file and splitting on every match makes this O(n²) in the number of routes in a file (each match re-scans earlier content). Since this runs in CI/pre-push, consider precomputing newline offsets once (or tracking line number incrementally while scanning) to keep route-heavy files fast.
    scripts/lint/no-unauth-routes.ts:1
  • Deriving relPath via string replacement assumes POSIX separators and can produce incorrect output on Windows (or if paths are normalized differently). Prefer path.relative(ROOT, filePath) (and optionally normalize separators for display) to keep this script portable and avoid confusing file paths in CI output.
    packages/checks/src/check-type-casts.ts:1
  • These exclusion patterns hard-code POSIX path separators. If this script is ever run on Windows (local dev or CI), \\__tests__\\ and \\test\\ directories won’t match and could unexpectedly start failing strict cast checks. Consider normalizing file paths to POSIX separators before regex tests, or use a separator-agnostic regex (e.g., matching both / and \\).
    packages/checks/src/check-type-casts.ts:1
  • These exclusion patterns hard-code POSIX path separators. If this script is ever run on Windows (local dev or CI), \\__tests__\\ and \\test\\ directories won’t match and could unexpectedly start failing strict cast checks. Consider normalizing file paths to POSIX separators before regex tests, or use a separator-agnostic regex (e.g., matching both / and \\).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +54 to +55
- name: Check for unauthenticated routes
run: bun scripts/lint/no-unauth-routes.ts
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bun lint:custom now includes bun run scripts/lint/no-unauth-routes.ts (per package.json), so this workflow runs the unauth-route check twice. To keep CI faster and reduce noisy duplication, remove the standalone step or remove no-unauth-routes from lint:custom and keep it as a dedicated CI step (one or the other).

Suggested change
- name: Check for unauthenticated routes
run: bun scripts/lint/no-unauth-routes.ts

Copilot uses AI. Check for mistakes.
andrew-bierman added a commit that referenced this pull request May 14, 2026
chore: clear type-cast backlog and promote to hard gate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api ci/cd dependencies Pull requests that update a dependency file mobile web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants