Skip to content

Assessment and migration proposal of the UI to Remix#1116

Open
mihow wants to merge 5 commits intoRolnickLab:mainfrom
mihow:claude/nextjs-migration-planning-nx9Cd
Open

Assessment and migration proposal of the UI to Remix#1116
mihow wants to merge 5 commits intoRolnickLab:mainfrom
mihow:claude/nextjs-migration-planning-nx9Cd

Conversation

@mihow
Copy link
Collaborator

@mihow mihow commented Feb 4, 2026

Summary by CodeRabbit

  • Documentation
    • Added a comprehensive migration planning suite: current state, framework assessments (Next.js vs Remix), recommendations (Remix favored), phased migration and rollback guidance, risk/questions, testing & evaluation plans, AI-driven migration playbook, and E2E testing strategy.
  • Chores
    • Added detailed migration steps, custom-vs-stock inventories, timelines, checklists, QA criteria, and verification guidance to support a phased proof-of-concept then full migration.

Comprehensive planning documentation for migrating the UI from
Vite + React Router to Next.js App Router:

- 00-index.md: Document index and quick reference
- 01-current-state.md: Current UI/API architecture analysis
- 02-nextjs-benefits.md: What Next.js offers and replaces
- 03-required-changes.md: All changes required for migration
- 04-custom-bespoke.md: Domain-specific code that stays custom
- 05-migration-steps.md: Phased implementation plan
- 06-questions-risks.md: Questions, risks, alternatives, benefits
- 07-testing-evaluation.md: Testing and validation strategy

Co-Authored-By: Claude <noreply@anthropic.com>
New documents:
- 08-motivations.md: Team context (2 devs + interns + AI), goals
  for reducing boilerplate, prescribed patterns, DRF-like experience
- remix/01-remix-benefits.md: Loaders, actions, forms, URL state
- remix/02-required-changes.md: Migration requirements (~43 fewer files)
- remix/03-custom-bespoke.md: What stays custom (domain models, design system)
- remix/04-migration-steps.md: 6-7 week phased plan
- remix/05-questions-risks.md: Comparison, risks, recommendation
- remix/06-testing-evaluation.md: Testing strategy for Remix

Updated 00-index.md to recommend Remix over Next.js based on
team motivations and boilerplate reduction goals.

Co-Authored-By: Claude <noreply@anthropic.com>
Documents how Claude Code can execute the Remix migration in 1-2 hours:
- Current test infrastructure audit (29 tests pass, lint passes)
- Phase-by-phase breakdown with verification checkpoints
- Automated verification script
- Risk assessment and mitigation strategies
- Pre-migration checklist
- Success criteria

Key findings:
- No E2E tests exist (risk for visual verification)
- Build currently fails (pre-existing TS issue, can ignore)
- Tests and lint pass (can use as checkpoints)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 4, 2026 22:56
@netlify
Copy link

netlify bot commented Feb 4, 2026

Deploy Preview for antenna-ssec canceled.

Name Link
🔨 Latest commit c8d1d20
🔍 Latest deploy log https://app.netlify.com/projects/antenna-ssec/deploys/6983fc0190bcb300089c259a

@netlify
Copy link

netlify bot commented Feb 4, 2026

Deploy Preview for antenna-preview canceled.

Name Link
🔨 Latest commit c8d1d20
🔍 Latest deploy log https://app.netlify.com/projects/antenna-preview/deploys/6983fc012946640008d0adc5

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds a comprehensive planning suite of Markdown documents that analyze the current UI/API, evaluate Next.js and Remix migration options, and provide phased migration steps, bespoke inventories, testing/evaluation plans, risk assessments, and AI-assisted migration guidance.

Changes

Cohort / File(s) Summary
Index & Motivation
.agents/planning/00-index.md, .agents/planning/08-motivations.md
High-level migration overview, motivations, recommendation (Remix favored in index), and proposed phased approach.
Current State & Risks
.agents/planning/01-current-state.md, .agents/planning/06-questions-risks.md, .agents/planning/remix/05-questions-risks.md
Current UI/API architecture, pain points, decision questions, risk catalog with mitigations, and alternatives matrix.
Next.js Assessment & Migration
.agents/planning/02-nextjs-benefits.md, .agents/planning/03-required-changes.md, .agents/planning/04-custom-bespoke.md, .agents/planning/05-migration-steps.md, .agents/planning/07-testing-evaluation.md
Next.js App Router capabilities, required project restructuring, bespoke vs stock surface, detailed migration steps, and Next.js-focused testing/evaluation guidance.
Remix Assessment & Migration
.agents/planning/remix/01-remix-benefits.md, .agents/planning/remix/02-required-changes.md, .agents/planning/remix/03-custom-bespoke.md, .agents/planning/remix/04-migration-steps.md, .agents/planning/remix/06-testing-evaluation.md, .agents/planning/remix/07-ai-driven-migration.md
Remix patterns (loaders/actions/forms), required project changes, bespoke/component inventory, phased migration plan, testing strategy, and AI-driven migration workflow.
E2E & Testing Strategy
.agents/planning/09-e2e-testing-strategy.md, .agents/planning/07-testing-evaluation.md, .agents/planning/remix/06-testing-evaluation.md
Playwright-based E2E strategy, test-generation approaches, visual/regression/performance testing guidance, CI integration, and migration verification checklists.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through plans and mapped each lane,
Routes and loaders sketched beneath my paw.
Tests, risks, and checklists like fresh carrots lain —
I nibble notes and hum a migration law.
Onward we bound, with tiny hopeful hops!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author, which violates the repository's required description template that specifies Summary, List of Changes, Related Issues, Detailed Description, Testing, Screenshots, Deployment Notes, and Checklist sections. Provide a comprehensive PR description following the template, including a summary of the assessment findings, list of planning documents added, migration rationale, and any deployment or testing considerations.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Assessment and migration proposal of the UI to Remix' accurately reflects the primary change: comprehensive planning documentation for migrating from Vite + React Router to Remix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
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

This pull request contains comprehensive planning documentation for migrating the Antenna UI from Vite + React Router to either Next.js or Remix, with a recommendation for Remix. The assessment includes detailed analysis of the current state, migration requirements, risks, testing strategies, and implementation plans.

Changes:

  • Adds 14 new planning documents totaling ~4,500 lines of technical documentation
  • Provides parallel assessments of both Next.js and Remix migration options
  • Includes detailed motivations, technical requirements, migration steps, and testing strategies
  • Recommends Remix as the preferred option due to better alignment with team goals

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
.agents/planning/00-index.md Overview and index of all planning documents with comparison matrix
.agents/planning/01-current-state.md Comprehensive analysis of current UI architecture and API patterns
.agents/planning/02-nextjs-benefits.md Evaluation of Next.js features and potential benefits
.agents/planning/03-required-changes.md Detailed catalog of changes needed for Next.js migration
.agents/planning/04-custom-bespoke.md Analysis of custom code that remains unchanged with Next.js
.agents/planning/05-migration-steps.md Phased implementation plan for Next.js (6-8 weeks)
.agents/planning/06-questions-risks.md Risk assessment and alternative evaluation for Next.js
.agents/planning/07-testing-evaluation.md Comprehensive testing strategy for Next.js migration
.agents/planning/08-motivations.md Team context and rationale for considering migration
.agents/planning/remix/01-remix-benefits.md Analysis of Remix features emphasizing loaders, actions, and forms
.agents/planning/remix/02-required-changes.md Detailed changes required for Remix migration
.agents/planning/remix/03-custom-bespoke.md Custom code analysis for Remix approach
.agents/planning/remix/04-migration-steps.md Phased Remix migration plan (6-7 weeks)
.agents/planning/remix/05-questions-risks.md Remix-specific risks and comparison with alternatives
.agents/planning/remix/06-testing-evaluation.md Testing strategy tailored for Remix patterns
.agents/planning/remix/07-ai-driven-migration.md AI-assisted migration approach with automation focus

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

- Feature inventory
- Screenshot baseline

See `../07-testing-evaluation.md` for complete baseline checklist.
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The reference to "../07-testing-evaluation.md" appears to be incorrect. Based on the file structure, this should refer to "../../07-testing-evaluation.md" (two levels up) to reach the Next.js testing document, or it should reference a different document. Verify this path is correct.

Suggested change
See `../07-testing-evaluation.md` for complete baseline checklist.
See `../../07-testing-evaluation.md` for complete baseline checklist.

Copilot uses AI. Check for mistakes.
Comment on lines +473 to +475
export function useProjectContext() {
return useOutletContext<{ project: Project }>()
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The function useProjectContext is referenced but not defined in the code snippet. It should be imported from '@remix-run/react' as useOutletContext, or the implementation should be included to show how it's defined.

Copilot uses AI. Check for mistakes.
export default defineConfig({
plugins: [
remix({
ignoredRouteFiles: ['**/*.css'],
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

In the Remix configuration, the ignoredRouteFiles option uses the pattern '/*.css', but this might inadvertently ignore CSS files that are legitimate route files with '.css' in their path. Consider being more specific with the pattern, such as '/*.css' only if you're certain no route files will have .css in their names, or use a more targeted approach.

Copilot uses AI. Check for mistakes.

## Overview

This document outlines how Claude Code can execute the Remix migration in **0.5-2 hours** with automated verification at each step, minimizing the need for human oversight.
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The claim of "0.5-2 hours" for a complete migration seems unrealistically optimistic. This is a complex UI migration involving 25 routes, authentication changes, data fetching pattern changes, and component updates. Even with AI assistance, the timeline in the table at the end shows 105 minutes which is already at the upper bound, and this doesn't account for debugging, testing, or unexpected issues. Consider revising this to a more realistic range like "8-16 hours" or "1-2 days" for AI-assisted migration.

Suggested change
This document outlines how Claude Code can execute the Remix migration in **0.5-2 hours** with automated verification at each step, minimizing the need for human oversight.
This document outlines how Claude Code can execute the Remix migration in **8–16 hours (1–2 days of AI-assisted work)** with automated verification at each step, minimizing the need for human oversight.

Copilot uses AI. Check for mistakes.
Comment on lines +237 to +242
timeout 10 npm run dev &
DEV_PID=$!
sleep 5
if ps -p $DEV_PID > /dev/null; then
echo "Dev server started successfully"
kill $DEV_PID
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The bash script uses the timeout command which is not available on macOS by default (it's a GNU coreutils command). For cross-platform compatibility, consider using a more portable approach or noting that this script requires GNU coreutils on macOS.

Suggested change
timeout 10 npm run dev &
DEV_PID=$!
sleep 5
if ps -p $DEV_PID > /dev/null; then
echo "Dev server started successfully"
kill $DEV_PID
npm run dev > /dev/null 2>&1 &
DEV_PID=$!
sleep 5
if ps -p "$DEV_PID" > /dev/null 2>&1; then
echo "Dev server started successfully"
kill "$DEV_PID" >/dev/null 2>&1 || true

Copilot uses AI. Check for mistakes.
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('taxon=123'),
expect.anything()
)
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

Typo: "recieve" should be "receive".

Copilot uses AI. Check for mistakes.
Comment on lines +860 to +866
| Phase 2: Core Routes | 5 days | 2 weeks |
| Phase 3: Feature Routes | 10 days | 4 weeks |
| Phase 4: Components | 5 days | 5 weeks |
| Phase 5: Testing | 5 days | 6 weeks |
| Phase 6: Deployment | 2-3 days | ~6.5 weeks |

**Total: ~6-7 weeks** (slightly faster than Next.js due to simpler patterns)
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The timeline shows Phase 3 taking 10 days but cumulative shows 4 weeks (20 working days). The cumulative should be 2.5 weeks (2 days + 5 days + 10 days = 17 days ≈ 3.4 weeks, not 4). Please verify and correct the cumulative timeline calculations throughout the table.

Suggested change
| Phase 2: Core Routes | 5 days | 2 weeks |
| Phase 3: Feature Routes | 10 days | 4 weeks |
| Phase 4: Components | 5 days | 5 weeks |
| Phase 5: Testing | 5 days | 6 weeks |
| Phase 6: Deployment | 2-3 days | ~6.5 weeks |
**Total: ~6-7 weeks** (slightly faster than Next.js due to simpler patterns)
| Phase 2: Core Routes | 5 days | 10 days |
| Phase 3: Feature Routes | 10 days | 20 days |
| Phase 4: Components | 5 days | 25 days |
| Phase 5: Testing | 5 days | 30 days |
| Phase 6: Deployment | 2-3 days | ~33 days |
**Total: ~6-7 weeks** (≈30–33 working days; slightly faster than Next.js due to simpler patterns)

Copilot uses AI. Check for mistakes.
### 2.1 Loader Testing

```typescript
// __tests__/routes/projects.$projectId.occurrences.test.ts
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The import statement shows 'import { createRequest } from '/test-utils'' but the utility is defined in 'app/test-utils.ts'. In Remix, the '' alias typically maps to the app directory, so this should work, but it would be clearer to verify this alias is configured in the Remix config or explicitly note the alias convention being used.

Suggested change
// __tests__/routes/projects.$projectId.occurrences.test.ts
// __tests__/routes/projects.$projectId.occurrences.test.ts
// Note: In this project, `~` is aliased to the `app` directory (see Remix config).

Copilot uses AI. Check for mistakes.
3. **Visualizations** - 100%
4. **Business logic** - 100%

**Net reduction**: ~30% fewer files, ~40% less glue code
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The comparison matrix shows "~30% fewer files, ~40% less glue code" but the summary table shows net reduction of -28 files (from 68 to 40). This is actually 41% fewer files, not 30%. Ensure the percentages are accurate and consistent throughout the document.

Suggested change
**Net reduction**: ~30% fewer files, ~40% less glue code
**Net reduction**: ~40% fewer files, ~40% less glue code

Copilot uses AI. Check for mistakes.
Comprehensive guide for efficient E2E test generation:

- Tool comparison (Playwright recommended)
- Test generation strategies:
  - Playwright codegen (recording)
  - Analytics-driven prioritization (GA, NewRelic)
  - AI-assisted generation
  - Session recording analysis
  - Log-based test generation
- Page Object Model structure
- Visual regression testing
- CI/CD integration with GitHub Actions
- Recommended phased approach (~5 hours for comprehensive coverage)

Also updated index to include AI-driven migration doc.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Contributor

@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: 14

🤖 Fix all issues with AI agents
In @.agents/planning/02-nextjs-benefits.md:
- Line 286: The table row containing the text "| Initial Load | Client render |
Server render | 40-60% faster FCP |" makes an unsupported quantified claim;
either add a proper citation and brief methodology/baseline supporting the
"40-60% faster FCP" figure (e.g., link to the benchmark/case study and note
conditions: device, network, baseline) or remove the numeric range and rephrase
to a non-quantified benefit (e.g., "improved FCP with server rendering") to
match the nuance in 08-motivations.md; update the table cell text accordingly
and, if adding a citation, append a reference note in the document showing
source and measurement details.

In @.agents/planning/03-required-changes.md:
- Around line 13-56: The fenced code blocks under "Current Structure:" and
"Next.js Structure:" are missing language tags; update each triple-backtick
fence to include a language (e.g., ```text or ```bash) so markdownlint stops
flagging them—specifically edit the two blocks showing the ui/ tree structures
in .agents/planning/03-required-changes.md (the blocks labeled Current Structure
and Next.js Structure) and prepend an appropriate language identifier to the
opening ``` for each fenced code block.

In @.agents/planning/04-custom-bespoke.md:
- Around line 340-344: Update the heading text "## 11. React Query Hooks
(Modified but Custom)" to hyphenate the compound adjective so it reads cleanly
(e.g., "## 11. React Query Hooks (Modified-but-custom)" or another consistent
hyphenation style); edit the heading string in the
.agents/planning/04-custom-bespoke.md file where that exact heading appears to
apply the change.

In @.agents/planning/05-migration-steps.md:
- Around line 560-570: The fenced route-tree block is missing a language tag;
update the triple-backtick fence that precedes the directory listing (the
app/projects/[projectId]/occurrences/ route tree block) to include a language
identifier such as "text" (e.g., change ``` to ```text) so the code block is
properly labeled by the renderer.

In @.agents/planning/07-testing-evaluation.md:
- Around line 15-25: The fenced block containing the performance metrics table
lacks a language tag; update the fenced code block in
.agents/planning/07-testing-evaluation.md that surrounds the metrics table to
include a language (e.g., use ```text or ```markdown) so the table renders with
proper formatting and syntax highlighting.
- Around line 35-41: The fenced checklist beginning with "## Authentication" is
missing a language tag; update that code fence (the block containing "##
Authentication", "- [ ] Login with email/password", etc.) to include a language
identifier such as ```text (i.e., replace the opening ``` with ```text and keep
the closing ```), so the fenced feature checklist is properly marked as plain
text for rendering and linting.
- Around line 374-387: The fenced checklist for "## Route: /auth/login" is
missing a language tag; update the opening triple-backtick for that route
checklist (the block containing "## Route: /auth/login" and the list of checks)
to include a language identifier (e.g., ```text or ```markdown) so syntax
highlighting and linting recognize it; apply the same fix to any other route
checklist blocks found in the file.

In @.agents/planning/remix/01-remix-benefits.md:
- Around line 296-301: The fenced code block showing the route tree in
.agents/planning/remix/01-remix-benefits.md is missing a language tag; update
the block that starts with ``` and the lines containing "routes/" and the three
route entries (projects.tsx, projects.$projectId.tsx,
projects.$projectId.occurrences.tsx) to use a language tag such as ```text so
the snippet is properly marked and rendered.

In @.agents/planning/remix/02-required-changes.md:
- Around line 296-301: The fenced code block showing the route tree is missing a
language tag; update the markdown around the route tree example (adjacent to the
createIdentification usage and comment) to add a language identifier (e.g.,
```text) on the opening fence and the matching closing fence so the route tree
renders with proper formatting and syntax highlighting.

In @.agents/planning/remix/03-custom-bespoke.md:
- Around line 140-150: The Custom Code Inventory totals are inconsistent: update
the "Custom Code Inventory (Post-Migration)" table and the "Total Custom" and
"Reduced from ~145" note so counts reconcile with the stated 43 deleted files;
specify whether the listed per-category numbers (Domain Models, Design System,
Visualization, Route Files, Service Files, Utilities) represent pre-migration or
post-migration totals, and state explicitly whether test files are included in
those counts; adjust the "Total Custom" value to match the sum of the categories
(or change categories to match the intended total) and recompute the "Reduced
from" value using the 43 deleted files (or correct the deleted count) so the
three numbers (pre-migration total, post-migration total, and deleted count) are
mathematically consistent with the table header "Custom Code Inventory
(Post-Migration)" and the "Total Custom" row.

In @.agents/planning/remix/04-migration-steps.md:
- Around line 828-841: The docker-compose snippet for the ui service references
a django service but doesn't include the django service, shared network, or any
volumes/env wiring; update the compose example to include a matching django
service definition (the service name "django"), add a common network (e.g.
"antenna-network") and attach both "django" and "ui" to it, ensure the ui's
API_URL remains the internal URL (http://django:8000), keep SESSION_SECRET in
environment, and add any needed shared volumes or environment variables for the
Django service so the relationship between services (depends_on: - django,
API_URL, networks, volumes) is explicit.

In @.agents/planning/remix/06-testing-evaluation.md:
- Around line 27-66: The tests call mockFetch in the Occurrences loader tests
but never show how fetch is mocked; update the test setup to mock global fetch
and clear mocks between tests so loader calls can be asserted. Instruct adding a
global fetch mock (using vi.fn() or jest.fn()) in the test setup file or at top
of the test, ensure beforeEach() clears mocks, and in the 'respects filter
params' test stub the fetch response (ok/json) before calling loader({ request,
params:{ projectId: '1' }, context: {} }) so expect(fetch or
mockFetch).toHaveBeenCalledWith(expect.stringContaining('taxon=123'),
expect.anything()) succeeds; reference the loader function, createRequest
helper, and mockFetch/fetch used in the assertion.

In @.agents/planning/remix/07-ai-driven-migration.md:
- Around line 13-20: Add a blank line immediately before the table header row "|
Type | Status | Files |" and another blank line immediately after the table's
last row so the table is surrounded by blank lines (ensuring Markdownlint
passes); edit the markdown block in
.agents/planning/remix/07-ai-driven-migration.md that contains the table header
to insert one empty line above the header and one empty line below the final
row.
- Around line 65-79: Add a blank line before and after the fenced workflow block
under "### Approach: Incremental with Checkpoints" (the code block starting with
"For each phase:") so the workflow table/block is surrounded by empty lines to
satisfy MD058; update the section so there is an empty line immediately above
the triple-backtick that begins the block and an empty line immediately below
the closing triple-backtick.
🧹 Nitpick comments (14)
.agents/planning/06-questions-risks.md (3)

1-1: Clarify document scope: Is this Next.js-specific or framework-agnostic?

The document title suggests it covers general migration questions and risks, but the content mixes Next.js-specific concerns (Q1 mentions "Server Components," Q4 discusses next/image, R3 shows dynamic() imports) with framework-agnostic alternatives (section includes Remix, Astro, etc.).

Consider either:

  • Renaming to indicate Next.js focus (e.g., "Next.js Migration: Questions, Risks, and Alternatives")
  • Moving Next.js-specific technical details to a separate document and keeping this framework-neutral

342-354: Recommendation section assumes Next.js without explicit justification.

The recommendation section (lines 343-354) states "Proceed with Next.js migration" but doesn't explain why Next.js was chosen over Remix, Vite+SSR, or other alternatives listed in this same document. The decision matrix on lines 294-302 shows Remix scoring comparably to Next.js in several categories.

Given the team context (small team, maintenance burden concerns per 08-motivations.md), an explicit framework selection rationale should precede the recommendation, addressing:

  • Why Next.js over Remix (both score ★★★★★ for SSR and performance)
  • Trade-offs accepted (e.g., Next.js has steeper learning curve ★★★☆☆ vs ★★☆☆☆ for Remix)
  • Alignment with motivation of "convention-over-configuration"

Would you like me to draft a framework selection rationale section, or check if this decision is documented elsewhere in the planning files?


124-127: Mark Next.js-specific code examples clearly.

The example uses dynamic() which is Next.js-specific. Since this document discusses multiple framework alternatives, code examples should indicate which framework they apply to.

♻️ Proposed fix to clarify framework
-**Example**:
+**Example (Next.js)**:
 ```typescript
 const Map = dynamic(() => import('@/components/map'), { ssr: false })
</details>

</blockquote></details>
<details>
<summary>.agents/planning/08-motivations.md (1)</summary><blockquote>

`120-130`: **Success criteria lack measurable metrics.**

The success criteria are qualitative ("interns can add features faster," "bug count decreases") without quantifiable targets. For a 6-8 week migration (per `06-questions-risks.md` line 344), measurable KPIs would help evaluate ROI.



Consider adding quantitative targets:
- "Onboarding time for new features: < X hours (baseline from intern time tracking)"
- "Bug count in migrated code: < Y per sprint (track post-migration)"
- "Code review cycle time: < Z hours (compare pre/post)"
- "Custom boilerplate LOC: reduce by N% (measure hooks → loaders conversion)"

These align with the team's small size and maintenance burden concerns.

</blockquote></details>
<details>
<summary>.agents/planning/02-nextjs-benefits.md (2)</summary><blockquote>

`29-39`: **Add language identifier to fenced code block.**

Static analysis correctly flags missing language identifier. While this is a directory structure (not code), specifying a language improves rendering and accessibility.


<details>
<summary>♻️ Proposed fix</summary>

```diff
-**Migration Example:**
-```
+**Migration Example:**
+```plaintext
 # Current structure
 src/pages/occurrences/occurrences.tsx

118-140: API Routes (BFF) pattern lacks trade-off discussion.

The section presents adding a Backend-for-Frontend layer via Next.js API routes as purely beneficial (lines 136-139: "Aggregate," "Add caching," "Implement BFF"). However, for a 2-person team (per 08-motivations.md), this introduces:

  • Additional latency (client → Next.js → Django vs direct client → Django)
  • More code to maintain (the motivation document emphasizes reducing maintenance burden)
  • Increased complexity in authentication (token forwarding, session management)
  • Potential cache invalidation challenges

Consider adding a "Trade-offs" subsection:

  • Latency: Extra hop adds 10-50ms (measure with Django on same network)
  • Maintenance: Additional layer to debug/deploy/monitor
  • When to use: Only for specific use cases (complex aggregations, rate limiting) rather than as default pattern
.agents/planning/remix/04-migration-steps.md (2)

41-48: Clarify installation approach: template vs manual.

Lines 41-48 show both create-remix template and manual npm install approaches without recommending which to use. For a migration (rather than greenfield), the manual approach is typically better to avoid template boilerplate, but this should be explicit.

📝 Suggested clarification
 ### 1.1 Initialize Remix
 
 ```bash
 cd ui
 
-# Create Remix app alongside existing code
-npx create-remix@latest --template remix-run/remix/templates/vite
-
-# Or manual setup
+# Manual setup (recommended for migration to preserve existing structure)
 npm install `@remix-run/node` `@remix-run/react` `@remix-run/serve` isbot
 npm install -D `@remix-run/dev` vite
+
+# Alternative: Use template for reference
+# npx create-remix@latest --template remix-run/remix/templates/vite
</details>

---

`800-824`: **Dockerfile missing .dockerignore recommendation.**

The Dockerfile is well-structured with multi-stage build, but there's no mention of `.dockerignore` to exclude unnecessary files (node_modules, .git, src/, etc.), which can significantly impact build times and image size.



<details>
<summary>📦 Add .dockerignore file</summary>

Create `.dockerignore` alongside the Dockerfile:

```plaintext
node_modules
.git
.env
.env.*
!.env.example
src
.vscode
*.log
.DS_Store
README.md
.github

This prevents copying unnecessary files into the build context, especially important for the COPY . . instruction on line 808.

.agents/planning/remix/06-testing-evaluation.md (2)

364-383: Use modern glob API instead of deprecated sync method.

Line 369 uses glob.sync() which is deprecated in glob v9+. The async API is preferred and works better with modern test runners.

♻️ Update to async glob pattern
-// __tests__/patterns/loader-compliance.test.ts
-import { glob } from 'glob'
-import * as fs from 'fs'
+import { glob } from 'glob'
+import { readFile } from 'fs/promises'
 
 describe('Loader Pattern Compliance', () => {
-  const routeFiles = glob.sync('app/routes/**/*.tsx')
-
-  routeFiles.forEach((file) => {
-    const content = fs.readFileSync(file, 'utf-8')
+  it('should use requireAuth in all loaders', async () => {
+    const routeFiles = await glob('app/routes/**/*.tsx')
+    
+    for (const file of routeFiles) {
+      const content = await readFile(file, 'utf-8')
+      const hasLoader = content.includes('export async function loader')
+      const hasRequireAuth = content.includes('requireAuth(request)')
     
-    if (hasLoader && !file.includes('auth.')) {
-      it(`${file} should use requireAuth`, () => {
+      if (hasLoader && !file.includes('auth.')) {
         expect(hasRequireAuth).toBe(true)
-      })
+      }
     }
   })
 })

402-416: Performance test requires network mocking.

The loader performance test (lines 402-416) measures real network requests to Django, making results non-deterministic and environment-dependent. For reliable performance testing, mock the network layer.

Add network mocking:

describe('Loader Performance', () => {
  beforeEach(() => {
    // Mock fetch to simulate 100ms API response
    global.fetch = vi.fn().mockImplementation(() =>
      new Promise(resolve =>
        setTimeout(() =>
          resolve({
            ok: true,
            json: async () => ({ results: [], count: 0 }),
          }),
          100
        )
      )
    )
  })

  it('occurrences loader completes within 500ms', async () => {
    const start = performance.now()
    await loader({
      request: createRequest('/projects/1/occurrences'),
      params: { projectId: '1' },
      context: {},
    })
    const duration = performance.now() - start
    
    // Should complete in <150ms (100ms mock + processing overhead)
    expect(duration).toBeLessThan(150)
  })
})

For E2E performance testing, use the Playwright setup from section 4.

.agents/planning/remix/03-custom-bespoke.md (2)

69-69: Clarify service file consolidation ratio.

Line 69 states "22 hook files → 10 service files + loaders" without explaining why 10 service files (not 22). This suggests consolidation (e.g., multiple occurrence hooks → single occurrences.server.ts), which should be made explicit.

Add explanation:

-**Net change**: 22 hook files → 10 service files + loaders in routes
+**Net change**: 22 hook files → 10 service files + loaders in routes
+
+Services consolidate related fetches:
+- occurrences.server.ts (getOccurrences, getOccurrence, searchOccurrences)
+- jobs.server.ts (getJobs, getJob, getJobTasks)
+- etc.

47-54: Add language identifiers to directory structure blocks.

Lines 47 and 57 show directory structures without language identifiers. While not critical, specifying plaintext or text improves rendering consistency.

♻️ Add language identifiers
 **Before** (22 hook directories):
-```
+```plaintext
 data-services/hooks/
 
 **After** (Embedded in routes):
-```
+```plaintext
 app/routes/

Also applies to: 57-67

.agents/planning/01-current-state.md (2)

187-209: Pain points lack prioritization.

The "Current Pain Points" section lists 12+ issues across development, performance, SEO, and architecture without indicating severity or business impact. For migration planning, prioritization helps justify effort and guide framework selection.

Add priority indicators:

Pain Point Severity Business Impact Framework Solution
No SSR Medium Poor SEO, slow FCP Next.js/Remix SSR
Manual route management Low Dev friction File-based routing
Large bundle High Slow initial load Automatic code-splitting
No image optimization Medium Poor Core Web Vitals next/image or Remix Image

This helps validate framework choice against actual pain points (e.g., if "Large bundle" is highest priority, verify that Next.js App Router reduces bundle size compared to Vite).


25-49: Add language identifiers to code blocks for consistency.

Multiple code blocks lack language identifiers (lines 25, 89, 140, 149, 181). While not critical for plaintext content, adding identifiers improves documentation consistency and rendering across Markdown viewers.

Example fixes:

  • Line 25: Add plaintext for route hierarchy
  • Line 89: Add plaintext for directory structure
  • Line 140, 149, 181: Add http or plaintext for API endpoint lists

This addresses static analysis warnings and aligns with best practices used in other planning documents.

Also applies to: 89-101, 140-145, 149-155, 181-183


| Feature | Current Stack | Next.js | Improvement |
|---------|--------------|---------|-------------|
| Initial Load | Client render | Server render | 40-60% faster FCP |
Copy link
Contributor

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
# First, explore the directory structure
find .agents/planning -type f -name "*.md" | head -20

Repository: RolnickLab/antenna

Length of output: 810


🏁 Script executed:

#!/bin/bash
# Read the specific file around line 286
wc -l .agents/planning/02-nextjs-benefits.md

Repository: RolnickLab/antenna

Length of output: 104


🏁 Script executed:

#!/bin/bash
# Read the file around line 286 with context
sed -n '280,295p' .agents/planning/02-nextjs-benefits.md

Repository: RolnickLab/antenna

Length of output: 741


🏁 Script executed:

#!/bin/bash
# Check if there are any citations, sources, or footnotes in the entire file
grep -n "cite\|source\|research\|study\|baseline\|FCP\|Lighthouse\|WebPageTest" .agents/planning/02-nextjs-benefits.md | head -30

Repository: RolnickLab/antenna

Length of output: 134


🏁 Script executed:

#!/bin/bash
# Check 01-current-state.md around the mentioned context
wc -l .agents/planning/01-current-state.md

Repository: RolnickLab/antenna

Length of output: 102


🏁 Script executed:

#!/bin/bash
# Read 08-motivations.md around line 113
sed -n '110,120p' .agents/planning/08-motivations.md

Repository: RolnickLab/antenna

Length of output: 370


🏁 Script executed:

#!/bin/bash
# Check the beginning of 02-nextjs-benefits.md for context/methodology
head -50 .agents/planning/02-nextjs-benefits.md

Repository: RolnickLab/antenna

Length of output: 1774


🏁 Script executed:

#!/bin/bash
# Check the end of 02-nextjs-benefits.md for references/footnotes
tail -30 .agents/planning/02-nextjs-benefits.md

Repository: RolnickLab/antenna

Length of output: 1181


🏁 Script executed:

#!/bin/bash
# Search for "40-60" or similar percentage claims in the file
grep -n "40\|60\|percent\|improvement\|faster" .agents/planning/02-nextjs-benefits.md

Repository: RolnickLab/antenna

Length of output: 320


🏁 Script executed:

#!/bin/bash
# Search for any references, citations, or links in the entire file
grep -n "\[.*\]\|http\|source\|reference\|note\|see\|based on" .agents/planning/02-nextjs-benefits.md

Repository: RolnickLab/antenna

Length of output: 373


Add source or remove unsupported claim.

The 40-60% FCP improvement at line 286 lacks any citation, baseline measurement, or methodology. No sources appear in the document. This claim contradicts the earlier statement in 08-motivations.md that "current performance is acceptable." Either cite research/case studies supporting this figure, or reframe as a general Next.js capability without quantifying improvement for this specific application.

🤖 Prompt for AI Agents
In @.agents/planning/02-nextjs-benefits.md at line 286, The table row containing
the text "| Initial Load | Client render | Server render | 40-60% faster FCP |"
makes an unsupported quantified claim; either add a proper citation and brief
methodology/baseline supporting the "40-60% faster FCP" figure (e.g., link to
the benchmark/case study and note conditions: device, network, baseline) or
remove the numeric range and rephrase to a non-quantified benefit (e.g.,
"improved FCP with server rendering") to match the nuance in 08-motivations.md;
update the table cell text accordingly and, if adding a citation, append a
reference note in the document showing source and measurement details.

Comment on lines +13 to +56
**Current Structure:**
```
ui/
├── src/
│ ├── index.tsx # Entry point
│ ├── app.tsx # Router configuration
│ ├── pages/ # Page components
│ ├── components/ # Shared components
│ ├── design-system/ # UI primitives
│ ├── data-services/ # API hooks
│ └── utils/ # Utilities
├── vite.config.ts
├── tailwind.config.js
└── package.json
```

**Next.js Structure:**
```
ui/
├── app/ # App Router pages
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ ├── auth/
│ │ ├── login/page.tsx
│ │ └── reset-password/page.tsx
│ ├── projects/
│ │ ├── page.tsx
│ │ └── [projectId]/
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── occurrences/
│ │ │ ├── page.tsx
│ │ │ ├── loading.tsx
│ │ │ └── [id]/page.tsx
│ │ └── ...
│ └── api/ # Route handlers (optional BFF)
├── components/ # Shared components
├── design-system/ # UI primitives (unchanged)
├── data-services/ # API hooks (modified)
├── utils/ # Utilities (modified)
├── next.config.js
├── tailwind.config.js
└── package.json
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language tags to fenced code blocks.
Markdownlint flags missing languages; this improves readability and tooling.

✅ Example fix (apply similarly to other blocks)
-```
+```text
 ui/
 ├── src/
 │   ├── index.tsx           # Entry point
 │   ├── app.tsx             # Router configuration
 │   ├── pages/              # Page components
 │   ├── components/         # Shared components
 │   ├── design-system/      # UI primitives
 │   ├── data-services/      # API hooks
 │   └── utils/              # Utilities
 ├── vite.config.ts
 ├── tailwind.config.js
 └── package.json
-```
+```
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 29-29: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In @.agents/planning/03-required-changes.md around lines 13 - 56, The fenced
code blocks under "Current Structure:" and "Next.js Structure:" are missing
language tags; update each triple-backtick fence to include a language (e.g.,
```text or ```bash) so markdownlint stops flagging them—specifically edit the
two blocks showing the ui/ tree structures in
.agents/planning/03-required-changes.md (the blocks labeled Current Structure
and Next.js Structure) and prepend an appropriate language identifier to the
opening ``` for each fenced code block.

Comment on lines +340 to +344
## 11. React Query Hooks (Modified but Custom)

All domain-specific data fetching hooks remain custom:

```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hyphenate the compound adjective.

“Modified but Custom” should be “Modified-but-custom” (or similar) to read cleanly.

💡 Suggested fix
-## 11. React Query Hooks (Modified but Custom)
+## 11. React Query Hooks (Modified-but-Custom)
🧰 Tools
🪛 LanguageTool

[grammar] ~342-~342: Use a hyphen to join words.
Context: ...ed but Custom) All domain-specific data fetching hooks remain custom: ``` data-...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
In @.agents/planning/04-custom-bespoke.md around lines 340 - 344, Update the
heading text "## 11. React Query Hooks (Modified but Custom)" to hyphenate the
compound adjective so it reads cleanly (e.g., "## 11. React Query Hooks
(Modified-but-custom)" or another consistent hyphenation style); edit the
heading string in the .agents/planning/04-custom-bespoke.md file where that
exact heading appears to apply the change.

Comment on lines +560 to +570
```
app/projects/[projectId]/occurrences/
├── page.tsx # List view
├── loading.tsx # List loading
├── [id]/
│ └── page.tsx # Full page detail
├── @modal/
│ └── (.)[id]/
│ └── page.tsx # Modal detail (intercepts)
└── layout.tsx # Handles modal slot
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a language tag to the fenced block.

The route tree block needs a language identifier (e.g., ```text).

💡 Suggested fix
-```
+```text
 app/projects/[projectId]/occurrences/
 ├── page.tsx                    # List view
 ├── loading.tsx                 # List loading
 ├── [id]/
 │   └── page.tsx               # Full page detail
 ├── `@modal/`
 │   └── (.)[id]/
 │       └── page.tsx           # Modal detail (intercepts)
 └── layout.tsx                  # Handles modal slot
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.20.0)</summary>

[warning] 560-560: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @.agents/planning/05-migration-steps.md around lines 560 - 570, The fenced
route-tree block is missing a language tag; update the triple-backtick fence
that precedes the directory listing (the app/projects/[projectId]/occurrences/
route tree block) to include a language identifier such as "text" (e.g., change
totext) so the code block is properly labeled by the renderer.


</details>

<!-- fingerprinting:phantom:triton:eagle -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +15 to +25
**Performance Metrics** (use Chrome DevTools Lighthouse):
```
| Metric | Current Value | Target |
|--------|---------------|--------|
| First Contentful Paint (FCP) | ___ms | < FCP |
| Largest Contentful Paint (LCP) | ___ms | < LCP |
| Time to Interactive (TTI) | ___ms | < TTI |
| Total Blocking Time (TBT) | ___ms | < TBT |
| Cumulative Layout Shift (CLS) | ___ | < CLS |
| Bundle Size (JS) | ___KB | < Size |
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a language tag to the fenced block.

The metrics table is inside a fenced block without a language. Use text or markdown.

💡 Suggested fix
-```
+```text
 | Metric | Current Value | Target |
 |--------|---------------|--------|
 | First Contentful Paint (FCP) | ___ms | < FCP |
 | Largest Contentful Paint (LCP) | ___ms | < LCP |
 | Time to Interactive (TTI) | ___ms | < TTI |
 | Total Blocking Time (TBT) | ___ms | < TBT |
 | Cumulative Layout Shift (CLS) | ___ | < CLS |
 | Bundle Size (JS) | ___KB | < Size |
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.20.0)</summary>

[warning] 16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @.agents/planning/07-testing-evaluation.md around lines 15 - 25, The fenced
block containing the performance metrics table lacks a language tag; update the
fenced code block in .agents/planning/07-testing-evaluation.md that surrounds
the metrics table to include a language (e.g., use text or markdown) so
the table renders with proper formatting and syntax highlighting.


</details>

<!-- fingerprinting:phantom:triton:eagle -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +140 to +150
## Custom Code Inventory (Post-Migration)

| Category | Files | Status |
|----------|-------|--------|
| Domain Models | 10 | Unchanged |
| Design System | 50+ | Unchanged |
| Visualization | 8 | Unchanged, lazy load |
| Route Files | 25 | New (replace pages) |
| Service Files | 10 | New (shared fetch logic) |
| Utilities | 10 | Mostly unchanged |
| **Total Custom** | ~113 | Reduced from ~145 |
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Custom file count inconsistency.

The table on lines 142-150 shows:

  • Domain Models: 10
  • Design System: 50+
  • Visualization: 8
  • Route Files: 25
  • Service Files: 10
  • Utilities: 10

This sums to ≥113, but the description says "~113" which implies approximation. However, line 150 shows "Total Custom: ~113" and line 151 says "Reduced from ~145". The reduction math (145 - 43 deleted per line 162 = 102, not 113) doesn't align.

Clarify:

  • Is 113 before or after migration?
  • Does it include test files?
  • Reconcile with the 43 files deleted (section starting line 152)
🤖 Prompt for AI Agents
In @.agents/planning/remix/03-custom-bespoke.md around lines 140 - 150, The
Custom Code Inventory totals are inconsistent: update the "Custom Code Inventory
(Post-Migration)" table and the "Total Custom" and "Reduced from ~145" note so
counts reconcile with the stated 43 deleted files; specify whether the listed
per-category numbers (Domain Models, Design System, Visualization, Route Files,
Service Files, Utilities) represent pre-migration or post-migration totals, and
state explicitly whether test files are included in those counts; adjust the
"Total Custom" value to match the sum of the categories (or change categories to
match the intended total) and recompute the "Reduced from" value using the 43
deleted files (or correct the deleted count) so the three numbers (pre-migration
total, post-migration total, and deleted count) are mathematically consistent
with the table header "Custom Code Inventory (Post-Migration)" and the "Total
Custom" row.

Comment on lines +828 to +841
```yaml
services:
ui:
build:
context: ./ui
dockerfile: Dockerfile
ports:
- "4000:3000"
environment:
- API_URL=http://django:8000
- SESSION_SECRET=${SESSION_SECRET}
depends_on:
- django
```
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Incomplete docker-compose.yml integration.

The example docker-compose.yml shows only the ui service but references django service (line 840: depends_on: - django) without showing the full integration. For a complete migration guide, the relationship between services (networking, shared volumes, environment variables) should be clear.

🐳 Complete docker-compose example
services:
  django:
    # ... existing Django service config
    networks:
      - antenna-network

  ui:
    build:
      context: ./ui
      dockerfile: Dockerfile
    ports:
      - "4000:3000"
    environment:
      - API_URL=http://django:8000  # Internal Docker network URL
      - SESSION_SECRET=${SESSION_SECRET}
    depends_on:
      - django
    networks:
      - antenna-network

networks:
  antenna-network:
    driver: bridge

Note the internal network URL http://django:8000 for service-to-service communication.

🤖 Prompt for AI Agents
In @.agents/planning/remix/04-migration-steps.md around lines 828 - 841, The
docker-compose snippet for the ui service references a django service but
doesn't include the django service, shared network, or any volumes/env wiring;
update the compose example to include a matching django service definition (the
service name "django"), add a common network (e.g. "antenna-network") and attach
both "django" and "ui" to it, ensure the ui's API_URL remains the internal URL
(http://django:8000), keep SESSION_SECRET in environment, and add any needed
shared volumes or environment variables for the Django service so the
relationship between services (depends_on: - django, API_URL, networks, volumes)
is explicit.

Comment on lines +27 to +66
```typescript
// __tests__/routes/projects.$projectId.occurrences.test.ts
import { loader } from '~/routes/projects.$projectId.occurrences'
import { createRequest } from '~/test-utils'

describe('Occurrences Loader', () => {
it('returns occurrences for valid project', async () => {
const request = createRequest('/projects/1/occurrences', {
token: 'valid-token',
})

const response = await loader({
request,
params: { projectId: '1' },
context: {},
})

const data = await response.json()
expect(data.results).toBeDefined()
expect(data.count).toBeGreaterThanOrEqual(0)
})

it('respects filter params', async () => {
const request = createRequest(
'/projects/1/occurrences?taxon=123&score_min=0.8',
{ token: 'valid-token' }
)

const response = await loader({
request,
params: { projectId: '1' },
context: {},
})

// Verify API was called with correct filters
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('taxon=123'),
expect.anything()
)
})
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Test examples lack complete mock setup.

The loader test examples reference mockFetch (line 62) but the test utilities section (145-191) doesn't show how to set up fetch mocking. For a complete testing guide, include the mock configuration.

🧪 Add fetch mock setup

Add to the test file or setup:

// __tests__/setup.ts or in test file
import { vi } from 'vitest' // or jest

// Mock global fetch
global.fetch = vi.fn()

// In test:
beforeEach(() => {
  vi.clearAllMocks()
})

it('respects filter params', async () => {
  // Setup mock
  (fetch as vi.Mock).mockResolvedValueOnce({
    ok: true,
    json: async () => ({ results: [], count: 0 }),
  })

  const request = createRequest(
    '/projects/1/occurrences?taxon=123&score_min=0.8',
    { token: 'valid-token' }
  )

  await loader({ request, params: { projectId: '1' }, context: {} })

  // Verify
  expect(fetch).toHaveBeenCalledWith(
    expect.stringContaining('taxon=123'),
    expect.anything()
  )
})
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 49-49: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In @.agents/planning/remix/06-testing-evaluation.md around lines 27 - 66, The
tests call mockFetch in the Occurrences loader tests but never show how fetch is
mocked; update the test setup to mock global fetch and clear mocks between tests
so loader calls can be asserted. Instruct adding a global fetch mock (using
vi.fn() or jest.fn()) in the test setup file or at top of the test, ensure
beforeEach() clears mocks, and in the 'respects filter params' test stub the
fetch response (ok/json) before calling loader({ request, params:{ projectId:
'1' }, context: {} }) so expect(fetch or
mockFetch).toHaveBeenCalledWith(expect.stringContaining('taxon=123'),
expect.anything()) succeeds; reference the loader function, createRequest
helper, and mockFetch/fetch used in the assertion.

Comment on lines +13 to +20
| Type | Status | Files |
|------|--------|-------|
| Unit Tests | ✅ Pass | 9 files, 29 tests |
| Lint | ✅ Pass | ESLint configured |
| TypeScript Build | ⚠️ Fails | Pre-existing issue (react-hook-form types) |
| E2E Tests | ❌ None | No Playwright/Cypress |
| Integration Tests | ❌ None | No component integration tests |

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Surround the table with blank lines.

Markdownlint requires blank lines before and after tables.

💡 Suggested fix
-### What Exists
-
-| Type | Status | Files |
+### What Exists
+
+| Type | Status | Files |
 |------|--------|-------|
 | Unit Tests | ✅ Pass | 9 files, 29 tests |
 | Lint | ✅ Pass | ESLint configured |
 | TypeScript Build | ⚠️ Fails | Pre-existing issue (react-hook-form types) |
 | E2E Tests | ❌ None | No Playwright/Cypress |
 | Integration Tests | ❌ None | No component integration tests |
+
🤖 Prompt for AI Agents
In @.agents/planning/remix/07-ai-driven-migration.md around lines 13 - 20, Add a
blank line immediately before the table header row "| Type | Status | Files |"
and another blank line immediately after the table's last row so the table is
surrounded by blank lines (ensuring Markdownlint passes); edit the markdown
block in .agents/planning/remix/07-ai-driven-migration.md that contains the
table header to insert one empty line above the header and one empty line below
the final row.

Comment on lines +65 to +79
## AI Migration Strategy

### Approach: Incremental with Checkpoints

Instead of big-bang migration, use small atomic changes with verification after each:

```
For each phase:
1. Make changes
2. Run verification commands
3. Start dev server, check for errors
4. Commit if passing
5. Continue or rollback
```

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Surround the workflow table with blank lines.

Add blank lines before and after the table block to satisfy MD058.

💡 Suggested fix
-For each phase:
+For each phase:
+
   1. Make changes
   2. Run verification commands
   3. Start dev server, check for errors
   4. Commit if passing
   5. Continue or rollback
+
📝 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
## AI Migration Strategy
### Approach: Incremental with Checkpoints
Instead of big-bang migration, use small atomic changes with verification after each:
```
For each phase:
1. Make changes
2. Run verification commands
3. Start dev server, check for errors
4. Commit if passing
5. Continue or rollback
```
## AI Migration Strategy
### Approach: Incremental with Checkpoints
Instead of big-bang migration, use small atomic changes with verification after each:
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 68-68: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
In @.agents/planning/remix/07-ai-driven-migration.md around lines 65 - 79, Add a
blank line before and after the fenced workflow block under "### Approach:
Incremental with Checkpoints" (the code block starting with "For each phase:")
so the workflow table/block is surrounded by empty lines to satisfy MD058;
update the section so there is an empty line immediately above the
triple-backtick that begins the block and an empty line immediately below the
closing triple-backtick.

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In @.agents/planning/00-index.md:
- Around line 85-106: Add a blank line before each markdown table under the "###
Next.js Migration" and "### Remix Migration" sections so the tables are
separated from the preceding headings/text (fixing MD058); locate the tables
shown in the diff (the pipe rows beginning with "| Aspect | Value |") and insert
one empty line immediately above each table so the markdown linter accepts them.
🧹 Nitpick comments (4)
.agents/planning/09-e2e-testing-strategy.md (3)

73-73: Improve markdown formatting for better rendering and accessibility.

Several code blocks are missing language specifiers, and one table needs blank line spacing. Adding language specifiers improves syntax highlighting and accessibility. As per coding guidelines, markdownlint flags these formatting issues.

📝 Proposed formatting improvements

Add language specifiers to code blocks:

 #### From Google Analytics
 
 **Most visited pages:**
-```
+```text
 GA > Behavior > Site Content > All Pages

```diff
 **Common user flows:**
-```
+```text
 GA > Behavior > Behavior Flow

```diff
 | 8 | Deployments list | 20% |
+
 **Action:** Export top flows, create test for each.
 **Transaction traces:**
-```
+```text
 NewRelic > APM > Transactions > Most time consuming

```diff
 **Error rates by page:**
-```
+```text
 NewRelic > Browser > Page views > Sort by JS errors

```diff
 **User sessions:**
-```
+```text
 NewRelic > Browser > Session traces

```diff
 **Prompt example:**
-```
+```text
 Given this Remix route file for occurrences:
 **Example workflow:**
-```
+```text
 FullStory session shows:
 ### Directory Layout
-```
+```text
 e2e/

Also applies to: 80-80, 87-87, 103-103, 110-110, 117-117, 137-137, 211-211, 261-261


547-549: Make the service readiness check more robust.

The 60-second timeout may be insufficient in resource-constrained CI environments. Consider increasing the timeout or making it configurable, and add a health check endpoint verification.

🔧 Proposed improvements
       - name: Wait for services
         run: |
-          timeout 60 bash -c 'until curl -s http://localhost:8000/api/v2/ > /dev/null; do sleep 2; done'
+          timeout 180 bash -c 'until curl -f -s http://localhost:8000/api/v2/health/ > /dev/null; do echo "Waiting for backend..."; sleep 3; done'

Or with retry logging:

      - name: Wait for services
        run: |
          max_attempts=60
          attempt=0
          until curl -f -s http://localhost:8000/api/v2/ > /dev/null || [ $attempt -eq $max_attempts ]; do
            echo "Attempt $((attempt+1))/$max_attempts: Backend not ready..."
            sleep 3
            attempt=$((attempt+1))
          done
          if [ $attempt -eq $max_attempts ]; then
            echo "Backend failed to start"
            exit 1
          fi

440-446: Add safety warning for the destructive database flush command.

The manage.py flush command deletes all database data and should never be run against production. Add explicit warnings and recommend environment safeguards.

⚠️ Recommended safety improvements

Add a warning and environment check:

 ### Option C: Seed Database with Fixtures
 
+> ⚠️ **WARNING**: The `flush` command is destructive and deletes all data. Only use in isolated test environments. Never run against production databases.
+
 ```bash
 # Before tests
 docker compose run --rm django python manage.py loaddata e2e_fixtures.json
 
 # After tests
-docker compose run --rm django python manage.py flush --no-input
+# Only safe in test environment with DATABASE_NAME containing 'test'
+docker compose run --rm django python manage.py flush --no-input --database=default

Consider adding a Django management command wrapper that checks the environment:

```python
# management/commands/flush_test_db.py
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings

class Command(BaseCommand):
    def handle(self, *args, **options):
        if 'test' not in settings.DATABASES['default']['NAME'].lower():
            raise CommandError('flush_test_db can only run on test databases')
        # Proceed with flush
.agents/planning/00-index.md (1)

129-131: Consider documenting agent usage in .agents/AGENTS.md.

The team context mentions "AI agents" as part of the development team. Since these planning documents are in the .agents/ directory and appear designed to guide migration work (possibly by AI agents), consider documenting the agent workflows and how they use these planning documents in .agents/AGENTS.md.

Based on learnings: "Document agent implementations in .agents/AGENTS.md"

Comment on lines +85 to +106
---

## Key Metrics

### Next.js Migration
| Aspect | Value |
|--------|-------|
| Estimated Duration | 6-8 weeks |
| Routes to Migrate | ~25 |
| Files to Change | ~150 |
| Custom Code Preserved | ~85% |
| Boilerplate Reduction | Low |

### Remix Migration
| Aspect | Value |
|--------|-------|
| Estimated Duration | 6-7 weeks |
| Routes to Migrate | ~25 |
| Files to Delete | ~43 (hooks → loaders) |
| Custom Code Preserved | ~85% |
| Boilerplate Reduction | **High** |

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdown formatting: Add blank lines before tables.

The tables at lines 90 and 99 need blank lines before them to comply with markdown best practices (MD058).

📝 Proposed formatting fix
 ### Next.js Migration
+
 | Aspect | Value |
 |--------|-------|
 ### Remix Migration
+
 | Aspect | Value |
 |--------|-------|
📝 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
---
## Key Metrics
### Next.js Migration
| Aspect | Value |
|--------|-------|
| Estimated Duration | 6-8 weeks |
| Routes to Migrate | ~25 |
| Files to Change | ~150 |
| Custom Code Preserved | ~85% |
| Boilerplate Reduction | Low |
### Remix Migration
| Aspect | Value |
|--------|-------|
| Estimated Duration | 6-7 weeks |
| Routes to Migrate | ~25 |
| Files to Delete | ~43 (hooks → loaders) |
| Custom Code Preserved | ~85% |
| Boilerplate Reduction | **High** |
---
## Key Metrics
### Next.js Migration
| Aspect | Value |
|--------|-------|
| Estimated Duration | 6-8 weeks |
| Routes to Migrate | ~25 |
| Files to Change | ~150 |
| Custom Code Preserved | ~85% |
| Boilerplate Reduction | Low |
### Remix Migration
| Aspect | Value |
|--------|-------|
| Estimated Duration | 6-7 weeks |
| Routes to Migrate | ~25 |
| Files to Delete | ~43 (hooks → loaders) |
| Custom Code Preserved | ~85% |
| Boilerplate Reduction | **High** |
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 90-90: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)


[warning] 99-99: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
In @.agents/planning/00-index.md around lines 85 - 106, Add a blank line before
each markdown table under the "### Next.js Migration" and "### Remix Migration"
sections so the tables are separated from the preceding headings/text (fixing
MD058); locate the tables shown in the diff (the pipe rows beginning with "|
Aspect | Value |") and insert one empty line immediately above each table so the
markdown linter accepts them.

Updated remix/07-ai-driven-migration.md with:
- New section "Recommended: Set Up E2E Tests First"
- Quick 3-5 hour E2E setup guide before migration
- Integration of E2E tests into migration verification
- Updated pre-migration checklist with E2E items
- Updated success criteria to include E2E tests passing
- Timeline comparison showing E2E-first approach benefits

Key recommendation: Invest 3-5 hours in E2E tests before starting
migration to enable fully automated verification during migration.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In @.agents/planning/remix/07-ai-driven-migration.md:
- Around line 224-256: The verify.sh script is brittle and non-portable: replace
the direct use of the GNU-only timeout and the naive sleep/ps/kill logic in the
dev-server check (symbols: timeout, DEV_PID, ps -p, kill, DEV_STATUS) with a
portable wait-and-probe pattern — start the dev server in background capturing
its PID (DEV_PID), poll for a known readiness indicator (e.g., an HTTP health
endpoint or an open port) in a loop with a configurable timeout instead of a
fixed sleep, use a portable timeout mechanism (or fall back to node/npm-based
timeout when GNU timeout is missing), and perform robust cleanup by terminating
the whole process group (use group kill semantics like killing -PID or sending
TERM then SIGKILL if still running) to ensure children are stopped before
setting DEV_STATUS appropriately.
🧹 Nitpick comments (4)
.agents/planning/remix/07-ai-driven-migration.md (4)

22-33: Specify language for code block.

The fenced code block should have a language identifier for proper rendering. Consider adding text or bash:

💡 Suggested fix
-```
+```text
 src/data-services/hooks/auth/tests/useLogout.test.ts

322-327: Specify language for code block.

Add bash as the language identifier for the code block:

💡 Suggested fix
-```
+```bash
 After each phase that passes verification:

400-409: Add blank lines around the table.

The table should be surrounded by blank lines for proper markdown rendering:

💡 Suggested fix
 Record these 7 flows:
+
 | Flow | Time | Priority |
 |------|------|----------|
 | Login → Projects | 15 min | Critical |
 | Filter occurrences | 20 min | Critical |
 | View occurrence detail | 15 min | Critical |
 | Agree with ML prediction | 15 min | Critical |
 | Create job | 20 min | High |
 | View job progress | 15 min | High |
 | Logout | 5 min | Medium |
+

1-553: Consider documenting this agent process in the central registry.

This document outlines a comprehensive AI-driven migration agent. Based on learnings, agent implementations should be documented in .agents/AGENTS.md for discoverability and consistency across the project. Consider adding an entry that references this migration agent and its capabilities.

Based on learnings: "Document agent implementations in .agents/AGENTS.md"

Comment on lines +224 to +256
```bash
#!/bin/bash
# verify.sh

echo "=== Running Lint ==="
npm run lint
LINT_STATUS=$?

echo "=== Running Tests ==="
npm run test
TEST_STATUS=$?

echo "=== Starting Dev Server (5 second check) ==="
timeout 10 npm run dev &
DEV_PID=$!
sleep 5
if ps -p $DEV_PID > /dev/null; then
echo "Dev server started successfully"
kill $DEV_PID
DEV_STATUS=0
else
echo "Dev server failed to start"
DEV_STATUS=1
fi

echo "=== Results ==="
echo "Lint: $([ $LINT_STATUS -eq 0 ] && echo 'PASS' || echo 'FAIL')"
echo "Tests: $([ $TEST_STATUS -eq 0 ] && echo 'PASS' || echo 'FAIL')"
echo "Dev Server: $([ $DEV_STATUS -eq 0 ] && echo 'PASS' || echo 'FAIL')"

exit $(( LINT_STATUS + TEST_STATUS + DEV_STATUS ))
```

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Verification script has portability and reliability concerns.

The automated verification script has several issues that could affect cross-platform execution:

  1. macOS incompatibility: The timeout command (line 237) is not available by default on macOS. It requires GNU coreutils.
  2. Process cleanup: Using kill $DEV_PID may not properly terminate the dev server and its child processes, potentially leaving zombie processes or bound ports.
  3. Timing assumptions: The 5-second sleep may be insufficient for dev server startup, leading to false negatives.
  4. Process detection: The ps -p check may not work reliably with background processes that spawn children.
🔧 Suggested improvements
 #!/bin/bash
 # verify.sh
+
+# Use GNU timeout if available, fallback to perl-based timeout
+if command -v timeout &> /dev/null; then
+    TIMEOUT_CMD="timeout"
+elif command -v gtimeout &> /dev/null; then
+    TIMEOUT_CMD="gtimeout"
+else
+    echo "Warning: timeout command not available, skipping dev server check"
+    TIMEOUT_CMD=""
+fi

 echo "=== Running Lint ==="
 npm run lint
 LINT_STATUS=$?

 echo "=== Running Tests ==="
 npm run test
 TEST_STATUS=$?

-echo "=== Starting Dev Server (5 second check) ==="
-timeout 10 npm run dev &
-DEV_PID=$!
-sleep 5
-if ps -p $DEV_PID > /dev/null; then
-    echo "Dev server started successfully"
-    kill $DEV_PID
-    DEV_STATUS=0
-else
-    echo "Dev server failed to start"
-    DEV_STATUS=1
-fi
+if [ -n "$TIMEOUT_CMD" ]; then
+    echo "=== Starting Dev Server (10 second check) ==="
+    $TIMEOUT_CMD 15 npm run dev > /tmp/dev-server.log 2>&1 &
+    DEV_PID=$!
+    sleep 10
+    if ps -p $DEV_PID > /dev/null 2>&1; then
+        echo "Dev server started successfully"
+        # Kill process group to clean up children
+        kill -TERM -$DEV_PID 2>/dev/null || true
+        sleep 2
+        kill -KILL -$DEV_PID 2>/dev/null || true
+        DEV_STATUS=0
+    else
+        echo "Dev server failed to start. Check /tmp/dev-server.log"
+        DEV_STATUS=1
+    fi
+else
+    DEV_STATUS=0
+fi

 echo "=== Results ==="
 echo "Lint: $([ $LINT_STATUS -eq 0 ] && echo 'PASS' || echo 'FAIL')"
 echo "Tests: $([ $TEST_STATUS -eq 0 ] && echo 'PASS' || echo 'FAIL')"
-echo "Dev Server: $([ $DEV_STATUS -eq 0 ] && echo 'PASS' || echo 'FAIL')"
+[ -n "$TIMEOUT_CMD" ] && echo "Dev Server: $([ $DEV_STATUS -eq 0 ] && echo 'PASS' || echo 'FAIL')"

 exit $(( LINT_STATUS + TEST_STATUS + DEV_STATUS ))
🤖 Prompt for AI Agents
In @.agents/planning/remix/07-ai-driven-migration.md around lines 224 - 256, The
verify.sh script is brittle and non-portable: replace the direct use of the
GNU-only timeout and the naive sleep/ps/kill logic in the dev-server check
(symbols: timeout, DEV_PID, ps -p, kill, DEV_STATUS) with a portable
wait-and-probe pattern — start the dev server in background capturing its PID
(DEV_PID), poll for a known readiness indicator (e.g., an HTTP health endpoint
or an open port) in a loop with a configurable timeout instead of a fixed sleep,
use a portable timeout mechanism (or fall back to node/npm-based timeout when
GNU timeout is missing), and perform robust cleanup by terminating the whole
process group (use group kill semantics like killing -PID or sending TERM then
SIGKILL if still running) to ensure children are stopped before setting
DEV_STATUS appropriately.

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