Skip to content

feat: Add shareable dev patches infrastructure#39

Merged
webdevcody merged 1 commit intowebdevcody:mainfrom
amoscicki:AM01dev/dev-ux-patches
Dec 22, 2025
Merged

feat: Add shareable dev patches infrastructure#39
webdevcody merged 1 commit intowebdevcody:mainfrom
amoscicki:AM01dev/dev-ux-patches

Conversation

@amoscicki
Copy link
Copy Markdown
Contributor

@amoscicki amoscicki commented Dec 21, 2025

Summary

Introduces a patch management system for development-only features that shouldn't be committed to the main codebase. This allows developers to quickly toggle local-only modifications without risk of accidentally pushing them.

Rationale

Lower barrier to entry for contributors:

  • New contributors can start developing immediately without setting up Google OAuth credentials
  • No need to configure R2/S3 storage just to see videos playing locally
  • Reduces "works on my machine" issues by providing consistent dev environment options

Faster iteration during development:

  • Switch between test users instantly without logging in/out
  • Test admin vs regular user flows without database manipulation
  • Test premium vs free user experiences with one click

Safer experimentation:

  • Dev-only code stays completely out of git history
  • Pre-commit hook catches accidental commits with patches active
  • Patches can be shared across team without polluting the codebase

Cross-platform consistency:

  • .gitattributes ensures consistent line endings (LF) across Windows/Mac/Linux
  • Shell scripts work correctly regardless of which OS they were edited on
  • Patches apply cleanly without line ending conflicts

What's Included

1. Dev Patches System (.dev/)

File Purpose
dev-patch.sh Main script for applying/removing patches
setup.sh One-time setup to add convenient git aliases
patches.yaml Tracks which patches are currently on/off
hooks/pre-commit-patch-guard.sh Prevents committing with patches active

2. dev-login-bypass Patch

Floating dev menu for quick user switching without Google OAuth.

  • Draggable bug icon (position persists across sessions)
  • Create test users with randomized emails/names
  • Instantly switch between dev users
  • Toggle admin/premium flags per user

Requires: DEV_BYPASS_AUTH=true in .env

3. mock-storage Patch

Bypasses R2/S3 storage using external sample content.

  • Sample videos from Google Storage (~2MB each)
  • Thumbnails from Unsplash (tech/coding themed)
  • Hash-based selection for consistent content per segment
  • No cloud storage credentials required

Requires: DEV_MOCK_STORAGE=true in .env

4. Line Ending Normalization (.gitattributes)

Enforces LF line endings to prevent cross-platform issues.

Usage

# One-time setup (adds git aliases)
bash .dev/setup.sh

# Quick commands
git dev-on              # Apply all patches
git dev-off             # Remove all patches
git dev-status          # Show patch status

# Individual patch control
bash .dev/dev-patch.sh toggle dev-login-bypass

Test Plan

  • Run bash .dev/setup.sh → git aliases added
  • Run git dev-on → patches apply with success message
  • Run git dev-off → patches remove cleanly
  • Set DEV_BYPASS_AUTH=true → dev menu appears
  • Set DEV_MOCK_STORAGE=true → sample videos load
  • Attempt commit with patches on → blocked by hook

Summary by CodeRabbit

  • Documentation

    • Added comprehensive development documentation for the patch management system and setup procedures.
  • Chores

    • Introduced development patch management infrastructure with control scripts and pre-commit hooks to prevent accidental commits.
    • Added environment configuration variables and git setup automation for developer workflows.
    • Configured git line-ending handling for consistency across platforms.

✏️ Tip: You can customize this high-level summary in your review settings.

Introduces a patch management system for development-only features that
shouldn't be committed to the main codebase.

## Patches included:

### dev-login-bypass
- Floating dev menu for quick user switching without Google OAuth
- Create test users with random emails/names
- Toggle admin/premium status per user
- Requires: DEV_BYPASS_AUTH=true

### mock-storage
- Bypasses R2/S3 when credentials aren't configured
- Sample videos from Google Storage (~2MB each)
- Thumbnails from Unsplash (tech/coding themed)
- Hash-based selection for consistent content per segment
- Requires: DEV_MOCK_STORAGE=true

## Usage:
- Run `bash .dev/setup.sh` once to add git aliases
- Use `git dev-on` / `git dev-off` to toggle all patches
- Pre-commit hook prevents accidental commits with patches active

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 21, 2025

Walkthrough

This PR introduces a development-only patch management system that allows developers to toggle feature patches using a YAML configuration file. It includes infrastructure scripts for applying/managing patches, pre-commit hooks, environment variables, and two feature patches: authentication bypass and mock storage support.

Changes

Cohort / File(s) Summary
Patch Infrastructure & Setup
.dev/README.md, .dev/dev-patch.sh, .dev/setup.sh, .dev/hooks/pre-commit-patch-guard.sh, .dev/patches.yaml
Introduces patch management framework with a Bash script to apply/toggle patches, a setup script that configures Git aliases, a pre-commit hook to prevent committing active patches, and configuration file tracking patch state (dev-login-bypass, mock-storage). Documentation covers setup, usage, and management.
Feature: Dev Authentication Bypass
.dev/patches/dev-login-bypass.patch
Adds development authentication bypass with server-side dev-auth module (devLoginFn, getDevUsersFn, switchDevUserFn, getDevMenuConfigFn), a new /dev-login route, frontend DevFloatingMenu and DevUserCard components, random user/email generators, and integration with Google OAuth flow. Enables draggable dev UI for user switching and creation.
Feature: Mock Storage Support
.dev/patches/mock-storage.patch
Extends storage provider system to support mock storage alongside R2. Introduces MockStorage implementation with presigned URLs for testing, updates getStorage() return type, and modifies thumbnail handling to accept mock storage. Updates video transcoding logic to recognize mock storage type.
Configuration & Tooling
.env.sample, .gitattributes
Adds DEV_BYPASS_AUTH and DEV_MOCK_STORAGE variables to environment template; introduces .gitattributes to enforce LF line endings for text files and mark binary assets.

Sequence Diagram

sequenceDiagram
    participant User
    participant DevFloatingMenu as DevFloatingMenu<br/>(Frontend)
    participant DevAuth as dev-auth<br/>(Server)
    participant Session as Session/DB
    
    User->>DevFloatingMenu: Opens menu
    DevFloatingMenu->>DevAuth: getDevUsersFn()
    DevAuth->>Session: Query dev users
    Session-->>DevAuth: Return user list
    DevAuth-->>DevFloatingMenu: dev users
    DevFloatingMenu-->>User: Display users
    
    User->>DevFloatingMenu: Create new user<br/>(email, name, roles)
    DevFloatingMenu->>DevAuth: devLoginFn(user data)
    DevAuth->>Session: Create/update user<br/>Set as active user
    Session-->>DevAuth: User created
    DevAuth-->>DevFloatingMenu: Login success
    DevFloatingMenu-->>User: Redirect/refresh UI
    
    alt User switches existing user
        User->>DevFloatingMenu: Click user card
        DevFloatingMenu->>DevAuth: switchDevUserFn(userId)
        DevAuth->>Session: Set active user
        Session-->>DevAuth: Success
        DevAuth-->>DevFloatingMenu: Updated config
        DevFloatingMenu-->>User: UI updates
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • dev-login-bypass.patch: Introduces multiple new server functions, routes, and frontend components with complex state management and user switching logic; requires validation of authentication flow integration with existing Google OAuth
  • mock-storage.patch: Modifies storage provider selection logic and type signatures; verify impact on thumbnail handling and presigned URL generation across storage types
  • dev-patch.sh: Complex Bash script with multiple entry points and state management; requires careful review of patch application/removal logic and error handling
  • Verify pre-commit hook behavior doesn't block legitimate commits and that patch state isolation is properly enforced

Poem

🐰 With patches now managed like carrots in rows,
dev features toggle wherever dev goes!
Auth bypass and mock storage, oh what a sight,
Development dancing both day and night!
Patches applied with a hop and a bound, 🎪

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Add shareable dev patches infrastructure' directly and clearly summarizes the main change—introduction of a complete dev patch management system with supporting scripts, hooks, and example patches.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (7)
.dev/README.md (1)

31-42: Add a language specifier to the fenced code block.

The directory structure code block is missing a language identifier. Use text or plaintext for non-code content to satisfy markdown linting rules.

🔎 Proposed fix
-```
+```text
 .dev/
 ├── README.md              # This file
.dev/dev-patch.sh (2)

84-91: Declare and assign separately to avoid masking return values.

Per ShellCheck SC2155, combining local with command substitution masks the command's return value. If get_state fails, the error won't propagate.

🔎 Proposed fix
 status() {
     echo "Dev Patches Status:"
     echo "-------------------"
     for patch in $(get_all_patches); do
-        local state=$(get_state "$patch")
+        local state
+        state=$(get_state "$patch")
         [ "$state" = "on" ] && echo -e "  ${GREEN}$patch: $state${NC}" || echo -e "  $patch: $state"
     done
 }

36-44: Reset function silently suppresses all errors.

The || true pattern in reset_patch_files suppresses all errors, including genuine failures (corrupted patch, missing git, etc.). Consider logging when the reverse fails.

🔎 Proposed improvement
 reset_patch_files() {
     for patch in $(get_all_patches); do
         local patch_file="$PATCHES_DIR/$patch.patch"
         if [ -f "$patch_file" ]; then
             # Use git apply --reverse to cleanly remove patch (handles both modified and new files)
-            git apply --reverse --ignore-whitespace "$patch_file" 2>/dev/null || true
+            if ! git apply --reverse --ignore-whitespace "$patch_file" 2>/dev/null; then
+                # Patch may not be applied or files may have changed - this is expected
+                :
+            fi
         fi
     done
 }
.dev/patches/mock-storage.patch (1)

803-810: External URLs introduce network dependency for dev mode.

The mock storage relies on external URLs (Google Storage, Unsplash). If these services are unavailable or the URLs change, development will break. Consider documenting this dependency or providing a fallback mechanism.

.dev/patches/dev-login-bypass.patch (3)

192-196: Missing dependency in useEffect.

The handleRandomize function is called inside the effect but not included in the dependency array. While this works because handleRandomize only uses a functional state update, it violates the exhaustive-deps rule.

🔎 Wrap handleRandomize with useCallback
+ const handleRandomize = useCallback(() => {
+   setNewUser((prev) => ({
+     ...prev,
+     email: generateRandomEmail(),
+     name: generateRandomName(),
+   }));
+ }, []);

- const handleRandomize = () => {
-   setNewUser((prev) => ({
-     ...prev,
-     email: generateRandomEmail(),
-     name: generateRandomName(),
-   }));
- };

  // Initialize random values when form opens
  useEffect(() => {
    if (showCreateForm && !newUser.email) {
      handleRandomize();
    }
- }, [showCreateForm, newUser.email]);
+ }, [showCreateForm, newUser.email, handleRandomize]);

677-694: Consider joining queries for better performance.

The current implementation fetches user and profile data with separate queries for each dev user (N queries for N users). While acceptable for dev tooling, this could be optimized with a join operation if the number of dev users grows.

For now, the current approach is clear and maintainable, and performance is not critical for development utilities.


771-787: Consider UX of automatic email/name updates.

When users toggle the Admin or Premium checkboxes, updateUserType overwrites any custom email and name values with preset values. This means if a user has typed a custom email, toggling a checkbox will replace it unexpectedly.

For dev tooling, this might be intentional for quick presets. However, you could improve UX by:

  1. Only updating email/name when they match the previous preset (not custom values)
  2. Providing separate "Quick Presets" buttons instead of automatic updates
  3. Adding a tooltip explaining the auto-update behavior

The current implementation works but could surprise users who expect manual input to persist.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed15046 and a6f47dd.

📒 Files selected for processing (9)
  • .dev/README.md (1 hunks)
  • .dev/dev-patch.sh (1 hunks)
  • .dev/hooks/pre-commit-patch-guard.sh (1 hunks)
  • .dev/patches.yaml (1 hunks)
  • .dev/patches/dev-login-bypass.patch (1 hunks)
  • .dev/patches/mock-storage.patch (1 hunks)
  • .dev/setup.sh (1 hunks)
  • .env.sample (1 hunks)
  • .gitattributes (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-12-19T06:04:43.431Z
Learnt from: CR
Repo: webdevcody/agentic-jumpstart PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T06:04:43.431Z
Learning: Applies to **/routes/admin/**/*.tsx : When making an admin page, protect it using `assertIsAdminFn()` in the `beforeLoad` hook with `createFileRoute`

Applied to files:

  • .dev/patches/dev-login-bypass.patch
📚 Learning: 2025-12-19T06:04:43.431Z
Learnt from: CR
Repo: webdevcody/agentic-jumpstart PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T06:04:43.431Z
Learning: Applies to **/routes/**/*.tsx : Pages should use the `Page` component and `PageHeader` component when possible

Applied to files:

  • .dev/patches/dev-login-bypass.patch
📚 Learning: 2025-12-19T06:04:43.431Z
Learnt from: CR
Repo: webdevcody/agentic-jumpstart PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T06:04:43.431Z
Learning: Applies to **/routes/**/*.tsx : Implement error boundaries at the route level using `DefaultCatchBoundary`

Applied to files:

  • .dev/patches/dev-login-bypass.patch
📚 Learning: 2025-12-19T06:04:43.431Z
Learnt from: CR
Repo: webdevcody/agentic-jumpstart PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T06:04:43.431Z
Learning: Routes are auto-generated to `/src/routeTree.gen.ts` and should never be edited manually

Applied to files:

  • .dev/patches/dev-login-bypass.patch
🪛 markdownlint-cli2 (0.18.1)
.dev/README.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Shellcheck (0.11.0)
.dev/dev-patch.sh

[warning] 88-88: Declare and assign separately to avoid masking return values.

(SC2155)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run Playwright Tests
🔇 Additional comments (18)
.gitattributes (1)

1-32: LGTM!

Comprehensive .gitattributes configuration that correctly enforces LF line endings for text files (critical for shell scripts) and declares binary files appropriately. This ensures cross-platform consistency for the patch system.

.dev/README.md (1)

1-30: Documentation is well-structured and comprehensive.

Clear instructions for setup, usage, and creating new patches. The troubleshooting section with common issues is particularly helpful.

.env.sample (1)

33-41: LGTM!

Good approach keeping the dev variables commented out by default, requiring explicit opt-in. Clear documentation references guide developers to the patch system docs.

.dev/patches.yaml (1)

1-6: LGTM!

Clean configuration with safe defaults (both patches off). The format works well with the shell-based YAML parsing in dev-patch.sh.

.dev/hooks/pre-commit-patch-guard.sh (1)

1-13: LGTM!

The hook correctly uses set -e for fail-fast behavior and resolves the script path properly. The conditional check allows the hook to be installed even if the patch system isn't fully set up yet.

.dev/setup.sh (1)

1-30: LGTM!

Well-structured setup script. Using --local for git config is the right choice to scope aliases to this repository. The output feedback listing available commands improves developer discoverability.

.dev/patches/mock-storage.patch (1)

782-787: Improved getThumbnailKey handles edge cases.

Good improvement to handle keys that don't end with .mp4. The fallback appends _thumb.jpg directly.

.dev/patches/dev-login-bypass.patch (11)

1-48: LGTM! Clean integration of dev menu into root route.

The parallel loading with Promise.all is a good optimization, and the defensive default values ensure the dev menu won't render unexpectedly. The conditional rendering based on devMenuConfig.isEnabled is the correct approach.


49-66: LGTM! Proper bypass with redirect_uri preservation.

The early return before the OAuth flow begins is the right approach, and preserving the redirect_uri parameter ensures users end up at their intended destination after dev login.


67-396: Well-structured dev menu with good UX.

The draggable positioning with corner-snapping and localStorage persistence provides a smooth developer experience. The query/mutation integration is clean, and hard reloads after user switches are appropriate for ensuring session state is fully refreshed.


397-477: LGTM! Clean presentational component.

Good use of avatar fallback with initials, proper button semantics, and clear visual distinction for the current user. The conditional styling and disabled states are handled correctly.


478-551: LGTM! Simple and effective random generation.

The localhost.test domain ensures these emails won't conflict with real addresses, and the adjective-noun pattern produces readable test data. Perfect for dev tooling.


576-619: LGTM! Well-designed helper functions.

The hash-based DiceBear style selection ensures consistent avatars for each dev user, and the mock GoogleUser structure properly mirrors the real OAuth response with the "dev-" prefix for easy identification.


629-659: Excellent reuse of existing OAuth flow.

Creating the mock GoogleUser and routing it through getAccountByGoogleIdUseCase and createGoogleUserUseCase ensures dev users follow the same code paths as production OAuth, reducing the chance of divergent behavior. The dev-only flag updates (isAdmin, isPremium) are appropriately applied after creation.


700-721: LGTM! Proper validation and security checks.

The Zod input validation, env var check, and verification that the target is a dev user (via the "dev-" prefix check) provide appropriate safeguards. This is the correct pattern that should also be used in devLoginFn.


723-737: LGTM! Clean configuration retrieval.

The early return when dev bypass is disabled and the safe handling of the current user lookup are both correct. This provides the necessary data for the root route to conditionally render the dev menu.


789-801: LGTM! Proper form submission and redirect.

The hard redirect with window.location.href is necessary to ensure the session cookie is properly set and picked up by the browser. Error handling logs to console, which is appropriate for dev tooling.


803-870: Clean dev login UI with appropriate warnings.

The yellow "DEV" badge and description clearly communicate that this is a development-only feature. The form layout is intuitive and the conditional loading state on the submit button provides good feedback.

Comment thread .dev/dev-patch.sh
Comment on lines +25 to +29
set_state() {
local patch="$1"
local state="$2"
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

sed -i is not portable to macOS.

macOS sed requires an empty string argument for in-place editing (sed -i ''). This will fail on macOS with sed: 1: "...": extra characters at the end of command.

🔎 Proposed fix for cross-platform compatibility
 set_state() {
     local patch="$1"
     local state="$2"
-    sed -i "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    if [[ "$OSTYPE" == "darwin"* ]]; then
+        sed -i '' "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    else
+        sed -i "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    fi
 }

Alternatively, use a portable pattern that works on both:

 set_state() {
     local patch="$1"
     local state="$2"
-    sed -i "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    local tmp_file=$(mktemp)
+    sed "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE" > "$tmp_file" && mv "$tmp_file" "$STATE_FILE"
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
set_state() {
local patch="$1"
local state="$2"
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
}
set_state() {
local patch="$1"
local state="$2"
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
else
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
fi
}
Suggested change
set_state() {
local patch="$1"
local state="$2"
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
}
set_state() {
local patch="$1"
local state="$2"
local tmp_file=$(mktemp)
sed "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE" > "$tmp_file" && mv "$tmp_file" "$STATE_FILE"
}
🤖 Prompt for AI Agents
In .dev/dev-patch.sh around lines 25 to 29, the use of sed -i is not portable to
macOS; update set_state to perform in-place replacement in a cross-platform way
(for example, call sed with a backup suffix on BSD/OSX and then remove the
backup, or detect GNU vs BSD sed and use the appropriate -i invocation, or write
the modified content to a temp file and mv it over the original). Also ensure
the $patch variable is quoted/escaped when used in the sed pattern so patch
names with special chars or spaces are handled safely.

Comment thread .dev/dev-patch.sh
Comment on lines +97 to +100
case "${1:-}" in
on) apply_patch "$2" ;;
off) remove_patch "$2" ;;
toggle) [ "$(get_state "$2")" = "on" ] && remove_patch "$2" || apply_patch "$2" ;;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing argument validation for patch commands.

The on, off, and toggle commands don't validate that $2 is provided. If invoked without a patch name, get_state "" and set_state "" will produce unexpected behavior.

🔎 Proposed fix
 case "${1:-}" in
-    on)      apply_patch "$2" ;;
-    off)     remove_patch "$2" ;;
-    toggle)  [ "$(get_state "$2")" = "on" ] && remove_patch "$2" || apply_patch "$2" ;;
+    on)      [ -z "${2:-}" ] && echo -e "${RED}Usage: dev-patch.sh on <patch-name>${NC}" && exit 1; apply_patch "$2" ;;
+    off)     [ -z "${2:-}" ] && echo -e "${RED}Usage: dev-patch.sh off <patch-name>${NC}" && exit 1; remove_patch "$2" ;;
+    toggle)  [ -z "${2:-}" ] && echo -e "${RED}Usage: dev-patch.sh toggle <patch-name>${NC}" && exit 1; [ "$(get_state "$2")" = "on" ] && remove_patch "$2" || apply_patch "$2" ;;
🤖 Prompt for AI Agents
In .dev/dev-patch.sh around lines 97 to 100, the case branches for on/off/toggle
do not validate that the patch-name argument ($2) is present; add an explicit
check at the start of each branch (or before the case) to ensure "$2" is
non-empty and otherwise print a concise usage/error message to stderr and exit
with non-zero status; adjust the toggle branch so it only calls get_state when
"$2" is provided; keep behavior otherwise identical (call apply_patch "$2" or
remove_patch "$2").

Comment on lines +621 to +623
+export const devLoginFn = createServerFn({ method: "POST" })
+ .inputValidator((data: DevLoginInput) => data)
+ .handler(async ({ data }) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Input validation is bypassed.

The .inputValidator((data: DevLoginInput) => data) does not perform any runtime validation—it merely casts the type. If called with invalid data (e.g., missing fields, wrong types), this could cause runtime errors or database failures.

🔎 Use a proper Zod schema for validation
+const devLoginSchema = z.object({
+  email: z.string().email(),
+  name: z.string().min(1),
+  isAdmin: z.boolean(),
+  isPremium: z.boolean(),
+});
+
 export const devLoginFn = createServerFn({ method: "POST" })
-  .inputValidator((data: DevLoginInput) => data)
+  .inputValidator(devLoginSchema)
   .handler(async ({ data }) => {

This provides runtime type checking and matches the pattern used in switchDevUserFn (line 702).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
+export const devLoginFn = createServerFn({ method: "POST" })
+ .inputValidator((data: DevLoginInput) => data)
+ .handler(async ({ data }) => {
const devLoginSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
isAdmin: z.boolean(),
isPremium: z.boolean(),
});
export const devLoginFn = createServerFn({ method: "POST" })
.inputValidator(devLoginSchema)
.handler(async ({ data }) => {
🤖 Prompt for AI Agents
.dev/patches/dev-login-bypass.patch around lines 621 to 623: the current
.inputValidator((data: DevLoginInput) => data) bypasses runtime validation;
replace it with a Zod-based validator (the same pattern used by switchDevUserFn
at ~line 702) by defining or importing a DevLogin Zod schema and passing its
parse (or safeParse with error handling) to .inputValidator so incoming data is
runtime-validated before the handler runs.

Comment on lines +625 to +627
+ if (process.env.DEV_BYPASS_AUTH !== "true") {
+ throw new Error("Dev login is disabled");
+ }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n "DEV_BYPASS_AUTH" --type ts --type tsx --type js

Repository: webdevcody/agentic-jumpstart

Length of output: 99


🏁 Script executed:

rg "DEV_BYPASS_AUTH" -n

Repository: webdevcody/agentic-jumpstart

Length of output: 54


🏁 Script executed:

cat -n .dev/patches/dev-login-bypass.patch | head -100

Repository: webdevcody/agentic-jumpstart

Length of output: 4927


🏁 Script executed:

sed -n '620,630p' .dev/patches/dev-login-bypass.patch

Repository: webdevcody/agentic-jumpstart

Length of output: 416


🏁 Script executed:

grep -n "DEV_BYPASS_AUTH\|export const dev" .dev/patches/dev-login-bypass.patch | head -30

Repository: webdevcody/agentic-jumpstart

Length of output: 523


🏁 Script executed:

find . -name "*.env*" -o -name "wrangler.toml" -o -name ".github" -type d | head -20

Repository: webdevcody/agentic-jumpstart

Length of output: 95


🏁 Script executed:

cat .env.sample && echo "---" && ls -la .github/workflows/ 2>/dev/null | head -20

Repository: webdevcody/agentic-jumpstart

Length of output: 1656


Confirm DEV_BYPASS_AUTH is not accessible in production deployments.

All dev functions properly gate access with if (process.env.DEV_BYPASS_AUTH !== "true") checks, and the feature is clearly documented in .env.sample as optional for local development only. The environment variable is not in version control. However, verify that:

  1. Your production environment never sets or inherits DEV_BYPASS_AUTH
  2. The .dev/patches/ directory and dev-only code paths are excluded from production builds
  3. No deployment pipeline accidentally exposes this configuration variable
🤖 Prompt for AI Agents
.dev/patches/dev-login-bypass.patch around lines 625 to 627: the added dev auth
bypass must never be available in production — verify and lock it down by (1)
auditing all production and CI/CD environment configurations to ensure
DEV_BYPASS_AUTH is not set or inherited, (2) ensuring .dev/patches/ and any
dev-only code paths are excluded from production builds/artifacts and not
packaged/deployed, (3) updating deployment pipeline and secrets management to
remove or block this variable and add a CI/CD check that fails if
DEV_BYPASS_AUTH is present in prod configs, and (4) hardening the runtime guard
so the bypass only runs in non-production environments (e.g., require NODE_ENV
!== 'production' in the same check) so accidental env leakage cannot enable it
in production.

Comment on lines +29 to +417
@@ -1,194 +1,194 @@
-import { Link } from "@tanstack/react-router";
-import { useContinueSlug } from "~/hooks/use-continue-slug";
-import { createServerFn } from "@tanstack/react-start";
-import { VideoPlayer } from "~/routes/learn/-components/video-player";
-import { useQuery } from "@tanstack/react-query";
-import { Play, ShoppingCart } from "lucide-react";
-import { getStorage } from "~/utils/storage";
-import { getThumbnailKey } from "~/utils/video-transcoding";
-import { database } from "~/db";
-import { segments, modules } from "~/db/schema";
-import { eq } from "drizzle-orm";
-
-const getFirstVideoSegmentFn = createServerFn().handler(async () => {
- // Get segments ordered by module order, then segment order
- const result = await database
- .select({
- segment: segments,
- moduleOrder: modules.order,
- })
- .from(segments)
- .innerJoin(modules, eq(segments.moduleId, modules.id))
- .orderBy(modules.order, segments.order);
-
- // Find the first segment that has a video and is not premium
- // (Landing page should show free preview content)
- const firstVideoSegment = result
- .map((row) => row.segment)
- .find(
- (segment) =>
- segment.videoKey && !segment.isPremium && !segment.isComingSoon
- );
-
- // Get thumbnail URL server-side if available
- let thumbnailUrl: string | null = null;
- if (firstVideoSegment?.videoKey) {
- const { storage, type } = getStorage();
- if (type === "r2") {
- const thumbnailKey =
- firstVideoSegment.thumbnailKey ||
- getThumbnailKey(firstVideoSegment.videoKey);
- const exists = await storage.exists(thumbnailKey);
- if (exists) {
- thumbnailUrl = await storage.getPresignedUrl(thumbnailKey);
- }
- }
- }
-
- return { segment: firstVideoSegment, thumbnailUrl };
-});
-
-export function HeroSection() {
- const continueSlug = useContinueSlug();
-
- const {
- data: firstVideoData,
- isLoading,
- error,
- } = useQuery({
- queryKey: ["first-video-segment"],
- queryFn: () => getFirstVideoSegmentFn(),
- staleTime: 1000 * 60 * 5, // 5 minutes
- gcTime: 1000 * 60 * 10, // 10 minutes
- });
-
- const firstVideoSegment = firstVideoData?.segment;
- const thumbnailUrl = firstVideoData?.thumbnailUrl;
-
- return (
- <section className="relative w-full py-12">
- {/* Modern AI-themed gradient background */}
- <div className="absolute inset-0 hero-background-ai"></div>
- <div className="absolute inset-0 bg-gradient-to-r from-transparent via-theme-500/5 dark:via-theme-950/20 to-transparent"></div>
-
- {/* AI circuit pattern overlay */}
- <div className="absolute inset-0 opacity-5 dark:opacity-10">
- <div className="circuit-pattern absolute inset-0"></div>
- </div>
-
- {/* AI-themed floating elements */}
- <div className="floating-elements">
- <div className="floating-element-1"></div>
- <div className="floating-element-2"></div>
- <div className="floating-element-3"></div>
- <div className="floating-element-small top-10 right-10"></div>
- </div>
-
- {/* Content */}
- <div className="relative z-10 h-full">
- <div className="max-w-7xl mx-auto px-6 lg:px-12 h-full">
- <div className="flex items-center h-full">
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 w-full items-center">
- {/* Left side - Content */}
- <div className="hero-content">
- {/* Badge */}
- <div className="inline-flex items-center px-4 py-2 rounded-full bg-theme-50/50 dark:bg-background/20 backdrop-blur-sm border border-theme-200 dark:border-border/50 text-theme-600 dark:text-theme-400 text-sm font-medium mb-8">
- <span className="w-2 h-2 bg-theme-500 dark:bg-theme-400 rounded-full mr-2"></span>
- Agentic Coding Mastery Course
- </div>
-
- <h1 className="text-6xl leading-tight mb-8">
- Coding is <span className="text-theme-400">Changing</span>,
- Master{" "}
- <span className="text-theme-400">Agentic Coding</span>{" "}
- </h1>
-
- <p className="text-description mb-12 max-w-xl">
- Master AI-first development with Cursor IDE, Claude Code CLI,
- and advanced AI models. Learn how to leverage Opus 4.5,
- Composer1, GPT-5.1 Codex, and cutting-edge agentic programming
- techniques to accelerate your development workflow and build
- applications 10x faster than traditional programming methods.
- </p>
-
- <div className="flex flex-col sm:flex-row gap-4">
- <Link
- to="/purchase"
- className="inline-flex items-center gap-2 rounded-xl bg-cyan-600 dark:bg-[#22d3ee] px-6 py-2 text-xs font-black text-white dark:text-[#0b101a] shadow-lg shadow-cyan-500/20 transition-all duration-200 hover:brightness-110 hover:shadow-[0_0_15px_rgba(34,211,238,0.4)]"
- onKeyDown={(e) => {
- if (e.key === " ") {
- e.preventDefault();
- e.currentTarget.click();
- }
- }}
- >
- <ShoppingCart className="w-4 h-4 stroke-[3.5px]" />
- Buy Now
- </Link>
- <Link
- to={"/learn/$slug"}
- params={{ slug: continueSlug }}
- className="inline-flex items-center gap-2 rounded-xl glass px-5 py-2 text-xs font-bold text-slate-600 dark:text-slate-300 transition-all hover:bg-slate-100 dark:hover:bg-white/10 hover:text-slate-900 dark:hover:text-white"
- onKeyDown={(e) => {
- if (e.key === " ") {
- e.preventDefault();
- e.currentTarget.click();
- }
- }}
- >
- <Play className="w-3.5 h-3.5 text-cyan-600 dark:text-cyan-400 fill-cyan-600 dark:fill-cyan-400" />
- Start Learning
- </Link>
- </div>
- </div>
-
- {/* Right side - Video player */}
- <div className="flex items-center justify-center lg:justify-end">
- {isLoading ? (
- <div className="w-full max-w-lg lg:max-w-xl aspect-video flex items-center justify-center bg-background/20 backdrop-blur-sm rounded-xl">
- <div className="text-sm text-muted-foreground">
- Loading video...
- </div>
- </div>
- ) : error ? (
- <div className="w-full max-w-lg lg:max-w-xl aspect-video flex items-center justify-center bg-background/20 backdrop-blur-sm rounded-xl">
- <div className="text-sm text-muted-foreground">
- Unable to load video
- </div>
- </div>
- ) : firstVideoSegment ? (
- <div className="w-full max-w-lg lg:max-w-xl">
- {/* Video container with glass morphism effect */}
- <div className="video-container">
- <div className="video-wrapper aspect-video relative">
- <VideoPlayer
- segmentId={firstVideoSegment.id}
- videoKey={firstVideoSegment.videoKey!}
- initialThumbnailUrl={thumbnailUrl}
- />
- </div>
-
- {/* Decorative elements - using theme colors */}
- <div className="video-decorative-1"></div>
- <div className="video-decorative-2"></div>
- </div>
- </div>
- ) : (
- <div className="w-full max-w-lg lg:max-w-xl aspect-video flex items-center justify-center bg-background/20 backdrop-blur-sm rounded-xl">
- <div className="text-sm text-muted-foreground">
- No video available
- </div>
- </div>
- )}
- </div>
- </div>
- </div>
- </div>
- </div>
-
- {/* Bottom gradient fade with theme accent */}
- <div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-background via-background/80 to-transparent"></div>
- <div className="section-divider-glow-bottom"></div>
- </section>
- );
-}
+import { Link } from "@tanstack/react-router";
+import { useContinueSlug } from "~/hooks/use-continue-slug";
+import { createServerFn } from "@tanstack/react-start";
+import { VideoPlayer } from "~/routes/learn/-components/video-player";
+import { useQuery } from "@tanstack/react-query";
+import { Play, ShoppingCart } from "lucide-react";
+import { getStorage } from "~/utils/storage";
+import { getThumbnailKey } from "~/utils/video-transcoding";
+import { database } from "~/db";
+import { segments, modules } from "~/db/schema";
+import { eq } from "drizzle-orm";
+
+const getFirstVideoSegmentFn = createServerFn().handler(async () => {
+ // Get segments ordered by module order, then segment order
+ const result = await database
+ .select({
+ segment: segments,
+ moduleOrder: modules.order,
+ })
+ .from(segments)
+ .innerJoin(modules, eq(segments.moduleId, modules.id))
+ .orderBy(modules.order, segments.order);
+
+ // Find the first segment that has a video and is not premium
+ // (Landing page should show free preview content)
+ const firstVideoSegment = result
+ .map((row) => row.segment)
+ .find(
+ (segment) =>
+ segment.videoKey && !segment.isPremium && !segment.isComingSoon
+ );
+
+ // Get thumbnail URL server-side if available
+ let thumbnailUrl: string | null = null;
+ if (firstVideoSegment?.videoKey) {
+ const { storage, type } = getStorage();
+ if (type === "r2" || type === "mock") {
+ const thumbnailKey =
+ firstVideoSegment.thumbnailKey ||
+ getThumbnailKey(firstVideoSegment.videoKey);
+ const exists = await storage.exists(thumbnailKey);
+ if (exists) {
+ thumbnailUrl = await storage.getPresignedUrl(thumbnailKey);
+ }
+ }
+ }
+
+ return { segment: firstVideoSegment, thumbnailUrl };
+});
+
+export function HeroSection() {
+ const continueSlug = useContinueSlug();
+
+ const {
+ data: firstVideoData,
+ isLoading,
+ error,
+ } = useQuery({
+ queryKey: ["first-video-segment"],
+ queryFn: () => getFirstVideoSegmentFn(),
+ staleTime: 1000 * 60 * 5, // 5 minutes
+ gcTime: 1000 * 60 * 10, // 10 minutes
+ });
+
+ const firstVideoSegment = firstVideoData?.segment;
+ const thumbnailUrl = firstVideoData?.thumbnailUrl;
+
+ return (
+ <section className="relative w-full py-12">
+ {/* Modern AI-themed gradient background */}
+ <div className="absolute inset-0 hero-background-ai"></div>
+ <div className="absolute inset-0 bg-gradient-to-r from-transparent via-theme-500/5 dark:via-theme-950/20 to-transparent"></div>
+
+ {/* AI circuit pattern overlay */}
+ <div className="absolute inset-0 opacity-5 dark:opacity-10">
+ <div className="circuit-pattern absolute inset-0"></div>
+ </div>
+
+ {/* AI-themed floating elements */}
+ <div className="floating-elements">
+ <div className="floating-element-1"></div>
+ <div className="floating-element-2"></div>
+ <div className="floating-element-3"></div>
+ <div className="floating-element-small top-10 right-10"></div>
+ </div>
+
+ {/* Content */}
+ <div className="relative z-10 h-full">
+ <div className="max-w-7xl mx-auto px-6 lg:px-12 h-full">
+ <div className="flex items-center h-full">
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 w-full items-center">
+ {/* Left side - Content */}
+ <div className="hero-content">
+ {/* Badge */}
+ <div className="inline-flex items-center px-4 py-2 rounded-full bg-theme-50/50 dark:bg-background/20 backdrop-blur-sm border border-theme-200 dark:border-border/50 text-theme-600 dark:text-theme-400 text-sm font-medium mb-8">
+ <span className="w-2 h-2 bg-theme-500 dark:bg-theme-400 rounded-full mr-2"></span>
+ Agentic Coding Mastery Course
+ </div>
+
+ <h1 className="text-6xl leading-tight mb-8">
+ Coding is <span className="text-theme-400">Changing</span>,
+ Master{" "}
+ <span className="text-theme-400">Agentic Coding</span>{" "}
+ </h1>
+
+ <p className="text-description mb-12 max-w-xl">
+ Master AI-first development with Cursor IDE, Claude Code CLI,
+ and advanced AI models. Learn how to leverage Opus 4.5,
+ Composer1, GPT-5.1 Codex, and cutting-edge agentic programming
+ techniques to accelerate your development workflow and build
+ applications 10x faster than traditional programming methods.
+ </p>
+
+ <div className="flex flex-col sm:flex-row gap-4">
+ <Link
+ to="/purchase"
+ className="inline-flex items-center gap-2 rounded-xl bg-cyan-600 dark:bg-[#22d3ee] px-6 py-2 text-xs font-black text-white dark:text-[#0b101a] shadow-lg shadow-cyan-500/20 transition-all duration-200 hover:brightness-110 hover:shadow-[0_0_15px_rgba(34,211,238,0.4)]"
+ onKeyDown={(e) => {
+ if (e.key === " ") {
+ e.preventDefault();
+ e.currentTarget.click();
+ }
+ }}
+ >
+ <ShoppingCart className="w-4 h-4 stroke-[3.5px]" />
+ Buy Now
+ </Link>
+ <Link
+ to={"/learn/$slug"}
+ params={{ slug: continueSlug }}
+ className="inline-flex items-center gap-2 rounded-xl glass px-5 py-2 text-xs font-bold text-slate-600 dark:text-slate-300 transition-all hover:bg-slate-100 dark:hover:bg-white/10 hover:text-slate-900 dark:hover:text-white"
+ onKeyDown={(e) => {
+ if (e.key === " ") {
+ e.preventDefault();
+ e.currentTarget.click();
+ }
+ }}
+ >
+ <Play className="w-3.5 h-3.5 text-cyan-600 dark:text-cyan-400 fill-cyan-600 dark:fill-cyan-400" />
+ Start Learning
+ </Link>
+ </div>
+ </div>
+
+ {/* Right side - Video player */}
+ <div className="flex items-center justify-center lg:justify-end">
+ {isLoading ? (
+ <div className="w-full max-w-lg lg:max-w-xl aspect-video flex items-center justify-center bg-background/20 backdrop-blur-sm rounded-xl">
+ <div className="text-sm text-muted-foreground">
+ Loading video...
+ </div>
+ </div>
+ ) : error ? (
+ <div className="w-full max-w-lg lg:max-w-xl aspect-video flex items-center justify-center bg-background/20 backdrop-blur-sm rounded-xl">
+ <div className="text-sm text-muted-foreground">
+ Unable to load video
+ </div>
+ </div>
+ ) : firstVideoSegment ? (
+ <div className="w-full max-w-lg lg:max-w-xl">
+ {/* Video container with glass morphism effect */}
+ <div className="video-container">
+ <div className="video-wrapper aspect-video relative">
+ <VideoPlayer
+ segmentId={firstVideoSegment.id}
+ videoKey={firstVideoSegment.videoKey!}
+ initialThumbnailUrl={thumbnailUrl}
+ />
+ </div>
+
+ {/* Decorative elements - using theme colors */}
+ <div className="video-decorative-1"></div>
+ <div className="video-decorative-2"></div>
+ </div>
+ </div>
+ ) : (
+ <div className="w-full max-w-lg lg:max-w-xl aspect-video flex items-center justify-center bg-background/20 backdrop-blur-sm rounded-xl">
+ <div className="text-sm text-muted-foreground">
+ No video available
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ {/* Bottom gradient fade with theme accent */}
+ <div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-background via-background/80 to-transparent"></div>
+ <div className="section-divider-glow-bottom"></div>
+ </section>
+ );
+}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and read the patch file
if [ -f ".dev/patches/mock-storage.patch" ]; then
  echo "=== Patch file found ==="
  wc -l .dev/patches/mock-storage.patch
  echo ""
  echo "=== First 50 lines ==="
  head -50 .dev/patches/mock-storage.patch
  echo ""
  echo "=== Checking for hero.tsx hunk ==="
  grep -n "hero.tsx\|+import { Link }\|if (type ===" .dev/patches/mock-storage.patch | head -20
else
  echo "Patch file not found at .dev/patches/mock-storage.patch"
  # Try to find it
  find . -name "*mock-storage*" -type f 2>/dev/null
fi

Repository: webdevcody/agentic-jumpstart

Length of output: 2688


🏁 Script executed:

#!/bin/bash
# Look at the exact hunk header for hero.tsx and surrounding lines
echo "=== Hero.tsx hunk header and actual change lines ==="
sed -n '25,100p' .dev/patches/mock-storage.patch

echo ""
echo "=== Counting actual content changes in hero.tsx section ==="
# Extract just the hero.tsx section and analyze it
sed -n '25,/^diff --git/p' .dev/patches/mock-storage.patch | grep -c '^-.*import\|^+.*import'

echo ""
echo "=== Look at video-transcoding.ts hunk ==="
sed -n '1,25p' .dev/patches/mock-storage.patch

Repository: webdevcody/agentic-jumpstart

Length of output: 3976


🏁 Script executed:

#!/bin/bash
# Check the actual file to understand the hero.tsx changes
echo "=== Current hero.tsx line count ==="
wc -l src/routes/-components/hero.tsx

echo ""
echo "=== Check for the type === 'r2' change in current file ==="
grep -n "type.*r2.*mock" src/routes/-components/hero.tsx

echo ""
echo "=== Look at git history to see what actually changed ==="
git log --oneline -n 10 2>/dev/null || echo "Not a git repo or git not available"

echo ""
echo "=== Try to find the actual difference using git diff if available ==="
git diff HEAD~1 src/routes/-components/hero.tsx 2>/dev/null | head -100 || echo "Git diff not available"

Repository: webdevcody/agentic-jumpstart

Length of output: 387


🏁 Script executed:

#!/bin/bash
# Examine the hero.tsx section more carefully to see what's actually different
echo "=== Extract hero.tsx section from patch ==="
sed -n '/^diff --git a\/src\/routes\/-components\/hero.tsx/,/^diff --git/p' .dev/patches/mock-storage.patch > /tmp/hero_patch.txt

echo "Lines in hero patch section: $(wc -l < /tmp/hero_patch.txt)"
echo ""

echo "=== Count lines that are DIFFERENT (starting with - or +) ==="
grep -c "^-\|^+" /tmp/hero_patch.txt

echo ""
echo "=== Look for the actual functional change ==="
grep -n "type.*===.*mock\|type.*!=.*mock" /tmp/hero_patch.txt

echo ""
echo "=== Check if this is truly all lines or just appears that way ==="
# Count distinct - lines and + lines
minus_count=$(grep -c "^-" /tmp/hero_patch.txt)
plus_count=$(grep -c "^+" /tmp/hero_patch.txt)
echo "Lines with '-' prefix: $minus_count"
echo "Lines with '+' prefix: $plus_count"

echo ""
echo "=== Sample of actual differences (first 10 +/- lines that differ) ==="
# Get lines that have a - and corresponding + that are different
awk 'NR > 5 && /^[-+]/ {print}' /tmp/hero_patch.txt | head -40

Repository: webdevcody/agentic-jumpstart

Length of output: 2083


🏁 Script executed:

#!/bin/bash
# Check the actual changes - are they truly identical on lines other than the one with "mock"?
echo "=== Extract paired diff lines and check for actual differences ==="
python3 << 'EOF'
import re

# Read the patch section
with open('/tmp/hero_patch.txt', 'r') as f:
    lines = f.readlines()

# Process pairs of - and + lines
i = 0
differences = []
while i < len(lines):
    if lines[i].startswith('-') and i+1 < len(lines) and lines[i+1].startswith('+'):
        old = lines[i][1:].rstrip()
        new = lines[i+1][1:].rstrip()
        if old != new:
            differences.append((i, old, new))
        i += 2
    else:
        i += 1

print(f"Total paired -/+ lines that actually differ: {len(differences)}")
print("\nDifferences found:")
for idx, old, new in differences:
    print(f"Line {idx}:")
    print(f"  Old: {old[:80]}")
    print(f"  New: {new[:80]}")
    if len(old) > 80 or len(new) > 80:
        print(f"  (truncated)")
EOF

echo ""
echo "=== Now check MockStorage implementation ==="
if [ -f "src/utils/storage/mock-storage.ts" ]; then
  echo "MockStorage file exists"
  echo ""
  echo "=== MockStorage.exists() implementation ==="
  sed -n '/\.exists(/,/^[[:space:]]*}/p' src/utils/storage/mock-storage.ts | head -20
  echo ""
  echo "=== MockStorage.getStream() implementation ==="
  sed -n '/\.getStream(/,/^[[:space:]]*}/p' src/utils/storage/mock-storage.ts | head -20
else
  echo "MockStorage file not found - checking patch for implementation"
  sed -n '/class MockStorage/,/^+}/p' .dev/patches/mock-storage.patch | tail -100
fi

Repository: webdevcody/agentic-jumpstart

Length of output: 3832


Use minimal hunks instead of full-file replacement in hero.tsx patch.

The patch shows all 194 lines of hero.tsx as changed when only one line actually differs. This makes the patch fragile—any formatting or whitespace change to the file breaks the patch. Regenerate with context-aware hunks targeting just the functional change: adding || type === "mock" to the storage type check.

The video-transcoding.ts patch correctly uses surgical hunks with proper line ranges (e.g., @@ -169,7 +169,7 @@), serving as a better reference format.

Additionally, MockStorage.exists() always returns true, which could mask file existence checks elsewhere, and MockStorage.getStream() intentionally throws errors. Both are documented but worth considering for test coverage.

Comment on lines +847 to +849
+ async exists(key: string): Promise<boolean> {
+ return true;
+ }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

exists() always returns true - potential silent failures.

MockStorage.exists() unconditionally returns true, which could mask logic errors in code that depends on actual file existence checks. Consider logging a warning or maintaining a set of "known" keys.

🔎 Proposed improvement
   async exists(key: string): Promise<boolean> {
+    // In mock mode, report as existing to allow dev workflows
+    // Note: This may hide issues where code expects real existence checks
+    console.log(`[MockStorage] exists check for: ${key} (always returns true)`);
     return true;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
+ async exists(key: string): Promise<boolean> {
+ return true;
+ }
async exists(key: string): Promise<boolean> {
// In mock mode, report as existing to allow dev workflows
// Note: This may hide issues where code expects real existence checks
console.log(`[MockStorage] exists check for: ${key} (always returns true)`);
return true;
}
🤖 Prompt for AI Agents
In .dev/patches/mock-storage.patch around lines 847 to 849, MockStorage.exists()
currently always returns true which can hide bugs; change it to check a tracked
set of stored keys (e.g., this._keys or this._store) and return whether the key
is present, updating that set when put/delete methods are called; if you want to
preserve older behavior temporarily, emit a warning when exists is called to
indicate the mock may be inaccurate; ensure exists remains async and returns a
boolean based on the internal key set rather than unconditionally true.

Comment on lines +851 to +858
+ async getStream(
+ key: string,
+ rangeHeader: string | null
+ ): Promise<StreamFileResponse> {
+ throw new Error(
+ "[MockStorage] getStream not supported. Use getPresignedUrl instead."
+ );
+ }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find usages of getStream in the codebase to verify they're guarded
echo "=== Searching for getStream usages ==="
rg -n "\.getStream\(" --type ts -C2

Repository: webdevcody/agentic-jumpstart

Length of output: 520


🏁 Script executed:

#!/bin/bash
# Read the video.ts file to understand how storage is used
echo "=== Reading video.ts file ==="
cat -n src/routes/api/segments/$segmentId/video.ts

Repository: webdevcody/agentic-jumpstart

Length of output: 184


🏁 Script executed:

#!/bin/bash
# Find video.ts file
echo "=== Finding video.ts ==="
fd -type f "video.ts"

Repository: webdevcody/agentic-jumpstart

Length of output: 284


🏁 Script executed:

#!/bin/bash
# Find video.ts file
echo "=== Finding video.ts ==="
fd "video.ts"

Repository: webdevcody/agentic-jumpstart

Length of output: 140


🏁 Script executed:

#!/bin/bash
# Read the video.ts file
echo "=== Reading video.ts file ==="
cat -n "src/routes/api/segments/\$segmentId/video.ts"

Repository: webdevcody/agentic-jumpstart

Length of output: 2121


🏁 Script executed:

#!/bin/bash
# Find and read the storage utility file
echo "=== Finding storage utility ==="
find . -path "*/utils/storage*" -type f

Repository: webdevcody/agentic-jumpstart

Length of output: 230


🏁 Script executed:

#!/bin/bash
# Read the storage index file to see the getStorage implementation
echo "=== Reading storage/index.ts ==="
cat -n src/utils/storage/index.ts

Repository: webdevcody/agentic-jumpstart

Length of output: 525


🏁 Script executed:

#!/bin/bash
# Search for MockStorage usage
echo "=== Searching for MockStorage usage ==="
rg "MockStorage" --type ts -C 2

Repository: webdevcody/agentic-jumpstart

Length of output: 111


🏁 Script executed:

#!/bin/bash
# Search for mock-storage references
echo "=== Searching for mock-storage ==="
rg "mock-storage" --type ts -C 2

Repository: webdevcody/agentic-jumpstart

Length of output: 106


🏁 Script executed:

#!/bin/bash
# Look at the patch file itself and its context
echo "=== Reading the mock-storage.patch file ==="
head -100 .dev/patches/mock-storage.patch

Repository: webdevcody/agentic-jumpstart

Length of output: 3781


🏁 Script executed:

#!/bin/bash
# Search for the MockStorage class definition in the patch
echo "=== Searching for MockStorage class in patch ==="
grep -n "class MockStorage" .dev/patches/mock-storage.patch

Repository: webdevcody/agentic-jumpstart

Length of output: 172


🏁 Script executed:

#!/bin/bash
# Read the MockStorage class definition from the patch file
echo "=== Reading MockStorage class from patch ==="
sed -n '803,900p' .dev/patches/mock-storage.patch

Repository: webdevcody/agentic-jumpstart

Length of output: 3465


🏁 Script executed:

#!/bin/bash
# Check if getStorage() is updated in the patch to return MockStorage
echo "=== Checking if getStorage is modified in patch ==="
grep -n "getStorage" .dev/patches/mock-storage.patch | head -20

Repository: webdevcody/agentic-jumpstart

Length of output: 518


🏁 Script executed:

#!/bin/bash
# Read the getStorage function modification in the patch
echo "=== Reading getStorage modification ==="
sed -n '430,460p' .dev/patches/mock-storage.patch

Repository: webdevcody/agentic-jumpstart

Length of output: 1155


🏁 Script executed:

#!/bin/bash
# Check if video.ts is modified in the patch
echo "=== Searching for video.ts modifications in patch ==="
grep -A 30 "diff --git a/src/routes/api/segments" .dev/patches/mock-storage.patch | head -50

Repository: webdevcody/agentic-jumpstart

Length of output: 125


🏁 Script executed:

#!/bin/bash
# Search for getStream calls in the patch file
echo "=== Searching for getStream in patch ==="
grep -n "getStream" .dev/patches/mock-storage.patch

Repository: webdevcody/agentic-jumpstart

Length of output: 217


Guard getStream() call against MockStorage usage.

The video.ts route calls storage.getStream() without checking if MockStorage is active. When DEV_MOCK_STORAGE=true, this will throw a runtime error. Add a check for type !== "mock" similar to the guards in video-transcoding.ts and hero.tsx, or implement getStream() support in MockStorage.

🤖 Prompt for AI Agents
.dev/patches/mock-storage.patch around lines 851 to 858: the MockStorage
implementation throws in getStream(), but routes like video.ts call
storage.getStream() unguarded when DEV_MOCK_STORAGE=true, causing runtime
errors; either add a guard in the caller(s) (e.g., in the video.ts route, check
storage.type !== "mock" before calling getStream(), or follow the same pattern
used in video-transcoding.ts and hero.tsx) or implement getStream() behavior in
MockStorage (return a compatible StreamFileResponse or a helpful
no-op/presigned-url alternative). Update callers or MockStorage so getStream is
never invoked on the mock without a safe fallback.

@webdevcody webdevcody merged commit d71eeef into webdevcody:main Dec 22, 2025
1 of 3 checks passed
webdevcody added a commit that referenced this pull request Dec 22, 2025
fix: Replace real names with auto-generated aliases for privacy (depends on #39)
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.

2 participants