Skip to content

Conversation

@vanpelt
Copy link
Collaborator

@vanpelt vanpelt commented Nov 7, 2025

Summary

Fixes two critical UX issues in the Catnip iOS app:

Issue 1: Stale codespace state after deletion

  • After deleting a codespace in GitHub, the app shows stale "Access My Codespace" button
  • This leads to confusing Setup view with manual devcontainer instructions when clicked
  • Fix: Implemented verification caching with rate limiting to refresh state on reconnect

Issue 2: Confusing Setup flow

  • Generic Setup view doesn't guide users based on their GitHub state
  • Fix: Smart routing based on user's repository state:
    • Zero repos → friendly "Create Repository" onboarding screen
    • Repos without Catnip → "Install Catnip" flow
    • Repos with Catnip → "Launch Codespace" flow

Architecture

Worker Side:

  • 60-second verification cache for /v1/user/status to reduce GitHub API calls
  • 10-second rate limiting on ?refresh=true requests (dual-layer: client + server)
  • SSE setup events enriched with next_action field based on GitHub repository state

iOS Side:

  • Call fetchUserStatus(forceRefresh: true) on reconnect to get fresh state
  • Route users based on worker's next_action determination
  • New friendly "Create Repository" guidance screen for zero-repo users

Test Plan

  • Build succeeds (verified with xcodebuild)
  • Manual testing: Delete codespace in GitHub, verify reconnect shows correct flow
  • Manual testing: Test zero-repo user sees "Create Repository" screen
  • Manual testing: Verify rate limiting prevents rapid refresh calls
  • Deploy to production and monitor

Implementation Details

Worker Changes (Tasks 1-4)

  • Added VerificationCache interface and helpers for Durable Object storage
  • Implemented cache storage routes (GET/PATCH /verification-cache/{username})
  • Enhanced /v1/user/status with caching and rate limiting
  • Added GitHub repository state checking to SSE setup events

iOS Changes (Tasks 5-9)

  • Updated SSEEvent.setup to include nextAction parameter
  • Added client-side rate limiting to CatnipInstaller.fetchUserStatus
  • Created createRepository phase and view for zero-repo users
  • Updated handleSSEEvent to route based on next_action
  • Updated WorkspacesView reconnect flow to refresh state before navigation

References

  • Design doc: docs/plans/2025-11-07-codespace-state-refresh-design.md
  • Implementation plan: docs/plans/2025-11-07-codespace-state-refresh.md

🤖 Generated with Claude Code

Claude and others added 20 commits November 7, 2025 00:48
…ements

- Worker-side verification caching with 60s TTL and 10s rate limiting
- iOS reconnect flow with forceRefresh parameter
- SSE setup event enrichment with next_action routing
- Create repository guidance screen for zero-repo users

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

Co-Authored-By: Claude <noreply@anthropic.com>
Prevent worktree contents from being tracked in repository.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Comprehensive plan with 11 tasks broken into bite-sized steps:

Worker tasks (1-4):
- Add verification cache infrastructure to Durable Object
- Implement cache storage routes
- Add caching and rate limiting to /v1/user/status
- Enhance SSE setup event with next_action routing

iOS tasks (5-9):
- Update SSEEvent enum with nextAction parameter
- Add rate limiting to CatnipInstaller
- Create repository guidance screen for zero-repo users
- Route setup events based on worker next_action
- Update WorkspacesView reconnect flow

Testing and deployment (10-11):
- Manual testing verification
- Deploy and monitor

Each task follows TDD with explicit test/verify steps.

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md

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

Co-Authored-By: Claude <noreply@anthropic.com>
Add VerificationCache interface and helper functions to support
rate-limited codespace verification with GitHub API.

- Cache tracks lastVerified (60s TTL) and lastRefreshRequest (10s rate limit)
- Helper functions for get/update operations
- Foundation for /v1/user/status caching logic

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
Add GET and PATCH routes for verification-cache storage:
- GET /verification-cache/{username} - retrieve cache
- PATCH /verification-cache/{username} - update cache fields

Cache stored in Durable Object state with key pattern
'verification-cache:{username}' for per-user isolation.

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
- Add try-catch for JSON parsing in PATCH handler (return 400 on error)
- Add 405 handler for unsupported HTTP methods
- Changes GET/PATCH from separate if blocks to if/else if/else chain

Addresses code review feedback from Task 2 review.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Implement verification cache with dual-layer protection:
- 60-second cache TTL for normal requests (reduces GitHub API calls)
- 10-second rate limiting on ?refresh=true (prevents abuse)
- Server-side enforcement protects quota even if client has bugs

Rate limiting behavior:
- If refresh requested < 10s after last refresh, log warning and use cache
- If cache older than 60s, automatically verify even without refresh param
- Graceful degradation serves cached data when rate limited

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
- Add nested try-catch for verification logic (graceful degradation on error)
- Add null safety check when accessing cached codespace data
- Fall back to cached data or false when verification fails

Addresses code review feedback from Task 3.
Worker now determines appropriate next_action based on user's GitHub state:
- 'create_repo' if user has zero repositories
- 'install' if user has repositories (default flow)
- 'launch' reserved for future use (when we detect Catnip repos)

Setup event now includes:
- message: User-facing message
- next_action: Routing hint for iOS (install/launch/create_repo)
- total_repositories: Count of writable repos

This centralizes decision logic in worker, simplifying iOS routing.

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
Remove repositories_with_catnip field from zero-repo setup event
for consistency. iOS will determine Catnip feature status separately.

Addresses code review feedback from Task 4.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Update SSEEvent enum to include nextAction routing hint from worker:
- SSEEvent.setup now includes nextAction string parameter
- Parse next_action field from SSE JSON data
- Default to 'install' if field missing (backward compatibility)

Enables iOS to route based on worker's determination of user state.

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
Implement dual-layer rate limiting protection:
- Track lastRefreshTime to prevent calls within 10 seconds
- Add forceRefresh parameter to explicitly request verification
- Fail fast on client to avoid unnecessary network calls
- Server also enforces same limit as backup

Rate limiting behavior:
- If forceRefresh called < 10s after last, log and return early
- Track timestamp BEFORE call to prevent race conditions
- Clear logging for debugging reconnect flows

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
Add friendly onboarding flow for users with zero repositories:
- New createRepository phase in CodespacePhase enum
- Welcoming screen with clear call-to-action
- Direct link to GitHub new repo page
- Self-service 'I Created a Repository' button to refresh and advance
- Graceful error handling if still no repos after refresh

Replaces technical Setup view for zero-repo users with
approachable first-time experience.

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
Update handleSSEEvent to route based on worker's next_action:
- 'create_repo' → createRepository phase (friendly onboarding)
- 'install' → repositorySelection with installation mode
- 'launch' → repositorySelection with launch mode
- unknown → fallback to old setup view (backward compatibility)

Worker now centralizes decision logic based on GitHub state,
iOS just follows routing instructions.

Clear logging for debugging routing decisions.

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
Update reconnect flow to fetch fresh user status before navigating:
- Call fetchUserStatus(forceRefresh: true) on reconnect button press
- Triggers worker verification with GitHub API (rate-limited)
- Navigate to CodespaceView only after state refreshes
- Graceful degradation if refresh fails

Ensures CodespaceView sees fresh has_any_codespaces flag,
preventing stale 'Access My Codespace' button after deletion.

Ref: docs/plans/2025-11-07-codespace-state-refresh-design.md
Remove implementation plan documents from version control:
- Remove docs/plans/2025-11-07-codespace-state-refresh-design.md
- Remove docs/plans/2025-11-07-codespace-state-refresh.md
- Add docs/plans/ to .gitignore

These planning documents are kept locally for reference but not
tracked in git as they're specific to development workflow.
Fix TypeScript compilation errors:
- Use optional chaining for verifiedCodespaces.length access
- Properly handle null/undefined with parentheses and ?? operator
- Change from 'cache?.verifiedCodespaces.length > 0 ?? false'
  to '(cache?.verifiedCodespaces?.length ?? 0) > 0'

Resolves TS18048 and TS2870 type errors.
@vanpelt vanpelt merged commit 6d2ad22 into main Nov 8, 2025
2 checks passed
@vanpelt vanpelt deleted the feature/codespace-state-refresh branch November 8, 2025 06:01
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