Skip to content

feat: dashboard landing, auto-ingestion, stale article fixes#9

Merged
jeremylongshore merged 3 commits intomainfrom
feat/dashboard-landing-auto-ingestion
Feb 27, 2026
Merged

feat: dashboard landing, auto-ingestion, stale article fixes#9
jeremylongshore merged 3 commits intomainfrom
feat/dashboard-landing-auto-ingestion

Conversation

@jeremylongshore
Copy link
Collaborator

@jeremylongshore jeremylongshore commented Feb 20, 2026

Summary

  • Login redirects to /dashboard instead of / (Articles feed) so users land on the command center
  • Auto-ingestion on dashboard mount — fires POST /trigger/ingestion once per browser session (sessionStorage guard), so the feed stays fresh without manual clicks
  • IngestionButton picks up auto-triggered runs via auto-ingestion-started custom event, showing real-time progress
  • Articles feed filters to last 48 hourswhere("published_at", ">=", cutoff) on both the "all" and per-category queries
  • Backend is_within_time_window() returns False on date parse failure instead of True (defense-in-depth; the exception path is unreachable in practice since normalize_published_date() falls back to datetime.now())

Files Changed

File Change
dashboard/src/pages/Login.tsx navigate("/")navigate("/dashboard")
dashboard/src/hooks/useAutoIngestion.ts New — fire-and-forget ingestion trigger with sessionStorage dedup
dashboard/src/pages/Dashboard.tsx Call useAutoIngestion() hook
dashboard/src/components/IngestionButton.tsx Listen for auto-ingestion-started event
dashboard/src/pages/Articles.tsx Add 48h published_at range filter to Firestore queries
perception_app/mcp_service/routers/rss.py Fix is_within_time_window() to reject unparseable dates

Test plan

  • npx tsc --noEmit — compiles clean
  • npx vite build — production build succeeds
  • pytest tests/mcp/ -v --no-cov — 39/39 passed
  • Manual: login → lands on /dashboard, ingestion auto-starts
  • Manual: navigate to Feed → no old articles, only last 48h
  • Manual: IngestionButton shows progress for auto-triggered run

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Automatic ingestion trigger on dashboard load with a session-level kickoff, user notification including run ID, and live status polling
  • Improvements

    • Articles list limited to items published in the last 48 hours
    • Post-login now redirects to the dashboard
  • Bug Fixes

    • Articles with unparseable timestamps are now excluded
  • Documentation

    • Project docs updated with architecture, setup, and deployment details
  • Tests

    • Timestamp parsing tests updated to reflect exclusion of malformed dates

…tale articles

Post-login now lands on /dashboard instead of the feed. Dashboard auto-triggers
ingestion once per browser session (sessionStorage guard) so the feed stays fresh
without manual clicks. IngestionButton picks up auto-triggered runs to show progress.

Articles query now filters to last 48 hours, and the backend is_within_time_window()
returns False on date parse failure instead of True (defense-in-depth).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link

Review Summary by Qodo

Dashboard landing, auto-ingestion, and stale article filtering

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Redirect login to /dashboard instead of articles feed
• Auto-trigger ingestion on dashboard mount once per session
• Filter articles feed to last 48 hours only
• Listen for auto-ingestion events in IngestionButton component
• Fix backend date parsing to reject unparseable dates safely
Diagram
flowchart LR
  Login["Login Page"] -- "navigate to /dashboard" --> Dashboard["Dashboard Page"]
  Dashboard -- "useAutoIngestion hook" --> AutoIngest["Auto-trigger Ingestion"]
  AutoIngest -- "POST /trigger/ingestion" --> Backend["Backend Service"]
  AutoIngest -- "dispatch custom event" --> IngestionBtn["IngestionButton"]
  IngestionBtn -- "listen & poll" --> Backend
  Articles["Articles Feed"] -- "48h time filter" --> Firestore["Firestore Query"]
  Backend -- "is_within_time_window" --> DateCheck["Date Validation"]
Loading

Grey Divider

File Changes

1. dashboard/src/pages/Login.tsx ✨ Enhancement +1/-1

Redirect login to dashboard page

• Changed post-login redirect from / to /dashboard
• Users now land on dashboard command center instead of articles feed

dashboard/src/pages/Login.tsx


2. dashboard/src/hooks/useAutoIngestion.ts ✨ Enhancement +40/-0

New auto-ingestion hook with session deduplication

• New hook that fires ingestion trigger once per browser session
• Uses sessionStorage to prevent duplicate requests
• Dispatches custom event with run_id for IngestionButton to track
• Fire-and-forget pattern to avoid blocking dashboard load
• Sends 24-hour time window and 50 items per source parameters

dashboard/src/hooks/useAutoIngestion.ts


3. dashboard/src/pages/Dashboard.tsx ✨ Enhancement +3/-0

Integrate auto-ingestion hook into dashboard

• Import and call useAutoIngestion() hook on component mount
• Triggers automatic feed refresh when dashboard loads

dashboard/src/pages/Dashboard.tsx


View more (3)
4. dashboard/src/components/IngestionButton.tsx ✨ Enhancement +14/-0

Listen for auto-ingestion events and track progress

• Add listener for auto-ingestion-started custom event
• Automatically start polling when auto-ingestion is triggered
• Transition from idle phase to starting phase on event receipt
• Clean up event listener on component unmount

dashboard/src/components/IngestionButton.tsx


5. dashboard/src/pages/Articles.tsx ✨ Enhancement +3/-0

Filter articles to last 48 hours only

• Add 48-hour time window filter to both "all" and category queries
• Calculate cutoff timestamp as current time minus 48 hours
• Filter articles using where('published_at', '>=', cutoff) clause
• Ensures only recent articles appear in feed

dashboard/src/pages/Articles.tsx


6. perception_app/mcp_service/routers/rss.py 🐞 Bug fix +3/-2

Fix date parsing to reject invalid dates safely

• Change exception handling in is_within_time_window() to return False instead of True
• Add warning log when date parsing fails with exception details
• Safer default behavior that excludes unparseable articles instead of including them

perception_app/mcp_service/routers/rss.py


Grey Divider

Qodo Logo

@gemini-code-assist
Copy link

Summary of Changes

Hello @jeremylongshore, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience by streamlining navigation, automating content updates, and improving data integrity. It ensures users land on a more functional dashboard, receive up-to-date information without manual refreshes, and see a more relevant article feed, while also hardening the backend against malformed date data.

Highlights

  • Login Redirection: Users are now redirected to the /dashboard page instead of the / (Articles feed) after logging in, providing a central command center experience.
  • Automatic Content Ingestion: The dashboard now automatically triggers content ingestion upon mounting, once per browser session, ensuring the feed remains fresh without manual intervention.
  • Ingestion Button Integration: The IngestionButton component has been updated to recognize and display progress for auto-triggered ingestion runs via a custom event.
  • Articles Feed Filtering: The articles feed now filters content to display only articles published within the last 48 hours, applying this filter to both 'all' and category-specific queries.
  • Backend Date Parsing Robustness: The backend's is_within_time_window() function now explicitly returns False for unparseable dates, preventing stale or invalid articles from being included.
Changelog
  • dashboard/src/components/IngestionButton.tsx
    • Implemented an event listener to pick up and display progress for auto-triggered ingestion runs.
  • dashboard/src/hooks/useAutoIngestion.ts
    • Added a new hook to handle automatic content ingestion on dashboard load.
  • dashboard/src/pages/Articles.tsx
    • Applied a 48-hour time window filter to Firestore queries for articles.
  • dashboard/src/pages/Dashboard.tsx
    • Integrated the useAutoIngestion() hook to enable auto-ingestion functionality.
  • dashboard/src/pages/Login.tsx
    • Changed login redirection from root ('/') to '/dashboard'.
  • perception_app/mcp_service/routers/rss.py
    • Modified is_within_time_window() to return False when published_at cannot be parsed.
Activity
  • The pull request was created by jeremylongshore.
  • The pull request includes a detailed summary of changes and a test plan.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a155887 and 332689b.

📒 Files selected for processing (1)
  • tests/unit/test_rss_parsing.py

📝 Walkthrough

Walkthrough

Adds a once-per-session auto-ingestion hook that POSTs to MCP and emits an auto-ingestion-started event; IngestionButton listens and begins polling from idle. Also adds a 48-hour published_at cutoff for article queries, changes post-login redirect to /dashboard, and excludes unparsable RSS timestamps.

Changes

Cohort / File(s) Summary
Auto-ingestion hook & wiring
dashboard/src/hooks/useAutoIngestion.ts, dashboard/src/pages/Dashboard.tsx, dashboard/src/components/IngestionButton.tsx
New useAutoIngestion hook triggers a POST to the MCP ingestion endpoint once per session (time_window_hours=24, max_items=50), shows a toast on 202 with run_id, sets sessionStorage flag, and dispatches auto-ingestion-started. Dashboard invokes the hook; IngestionButton listens for the event and, if phase is idle, transitions to starting, records start time, and calls pollStatus(runId).
Article query cutoff
dashboard/src/pages/Articles.tsx
Adds a 48-hour published_at >= cutoff filter to Firestore queries for both "all" and category-specific article lists.
Auth redirect
dashboard/src/pages/Login.tsx
Changes post-login redirect target from / to /dashboard.
RSS timestamp handling & tests
perception_app/mcp_service/routers/rss.py, tests/unit/test_rss_parsing.py
is_within_time_window now returns False and logs a warning on timestamp parse failure; corresponding test renamed/assertion updated to expect exclusion of malformed dates.
Docs/manifest
CLAUDE.md
Extensive documentation and project-structure updates: dashboard tech stack, agent roles and entrypoints, MCP endpoints (including /trigger/ingestion), env vars, and revised setup/deploy notes.

Sequence Diagram

sequenceDiagram
    rect rgba(200,200,255,0.5)
    participant Dashboard as Dashboard Component
    participant Hook as useAutoIngestion Hook
    participant Storage as sessionStorage
    participant MCP as MCP Endpoint
    participant Window as window (event)
    participant Button as IngestionButton
    end

    Dashboard->>Hook: mount -> call useAutoIngestion()
    Hook->>Storage: read auto-ingest flag
    alt flag absent
        Hook->>MCP: POST /trigger/ingestion (time_window_hours=24, max_items=50)
        MCP-->>Hook: 202 Accepted + { run_id }
        Hook->>Hook: parse run_id, show toast
        Hook->>Window: dispatch 'auto-ingestion-started' { run_id }
        Hook->>Storage: set auto-ingest flag
    else flag present
        Hook-->>Hook: skip trigger
    end
    Note over Button: listens for 'auto-ingestion-started'
    Window->>Button: event { run_id }
    Button->>Button: if phase == idle -> set starting, record start time
    Button->>Button: call pollStatus(run_id)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped into the dashboard at dawn,
Poked MCP with a whisper and then was gone.
A run_id returned, a tiny bell rang,
Polls began humming, and toasts loudly sang.
Hooray—auto-ingest keeps the data on!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main changes: dashboard landing redirect, auto-ingestion feature, and stale article filtering to the last 48 hours.
Description check ✅ Passed The description covers all required template sections with clear explanations, a detailed files-changed table, and a test plan with specific completion status for each item.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/dashboard-landing-auto-ingestion

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

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request introduces several key improvements to the dashboard experience, including auto-ingestion on mount and a 48-hour filter for the articles feed. The backend fix for date parsing in the time window check is also a good robustness improvement. I have identified a few areas for improvement regarding configuration management and a potential runtime issue with Firestore queries that require composite indexes.

import { useEffect } from 'react'
import { toast } from 'sonner'

const MCP_URL = 'https://perception-mcp-w53xszfqnq-uc.a.run.app'

Choose a reason for hiding this comment

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

medium

The MCP_URL is hardcoded to a specific production endpoint and is duplicated in IngestionButton.tsx. It is better to use an environment variable to allow for different environments (local, staging, production) and to centralize the configuration.

Suggested change
const MCP_URL = 'https://perception-mcp-w53xszfqnq-uc.a.run.app'
const MCP_URL = import.meta.env.VITE_MCP_URL || 'https://perception-mcp-w53xszfqnq-uc.a.run.app'

Comment on lines 248 to 253
q = query(
articlesRef,
where('category', '==', selectedCategory),
where('published_at', '>=', cutoff),
orderBy('published_at', 'desc'),
limit(100)

Choose a reason for hiding this comment

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

medium

This query combines an equality filter (category) with a range filter and ordering on another field (published_at). In Firestore, this specific combination requires a composite index. If this index hasn't been created yet in the Firebase Console, this query will fail at runtime with a failed-precondition error. Please ensure the index for category: ASC, published_at: DESC is deployed.

@greptile-apps
Copy link

greptile-apps bot commented Feb 20, 2026

Greptile Summary

This PR improves UX by redirecting login to /dashboard and auto-triggering ingestion on first session visit, while adding a 48-hour time filter to the articles feed for fresher content.

Key changes:

  • Login now lands users on /dashboard instead of the articles feed
  • New useAutoIngestion hook fires ingestion once per browser session with sessionStorage guard
  • IngestionButton listens for auto-ingestion-started events to track auto-triggered runs
  • Articles feed filters to last 48 hours via Firestore query with where('published_at', '>=', cutoff)
  • Backend is_within_time_window() now returns False on date parse failure (defense-in-depth)

Issues found:

  • Backend behavior change breaks existing unit test test_malformed_date_returns_true which expects True but now gets False - test needs updating
  • Firestore composite query for category filtering with time range should be verified in production

Confidence Score: 4/5

  • Safe to merge after fixing the failing test
  • The implementation is solid with good architectural patterns (fire-and-forget, sessionStorage dedup, event-driven updates). One critical issue: the backend change breaks an existing unit test that expects True for malformed dates but now gets False. This must be fixed before merge. The Firestore index concern is minor and can be verified post-deployment.
  • perception_app/mcp_service/routers/rss.py needs test update, dashboard/src/pages/Articles.tsx should be monitored for Firestore index errors

Important Files Changed

Filename Overview
perception_app/mcp_service/routers/rss.py Changed is_within_time_window to return False on parse failure instead of True, but this breaks existing test expectations
dashboard/src/hooks/useAutoIngestion.ts New hook to auto-trigger ingestion on dashboard mount with sessionStorage deduplication, fire-and-forget pattern
dashboard/src/pages/Articles.tsx Added 48-hour filter to article queries with where('published_at', '>=', cutoff), may need composite Firestore index

Sequence Diagram

sequenceDiagram
    participant User
    participant Login
    participant Dashboard
    participant useAutoIngestion
    participant MCP as MCP Service
    participant IngestionButton
    participant Articles
    participant Firestore

    User->>Login: Enter credentials
    Login->>Login: Authenticate
    Login->>Dashboard: navigate('/dashboard')
    
    Dashboard->>useAutoIngestion: Mount (call hook)
    useAutoIngestion->>useAutoIngestion: Check sessionStorage
    alt First visit this session
        useAutoIngestion->>useAutoIngestion: Set sessionStorage flag
        useAutoIngestion->>MCP: POST /trigger/ingestion
        MCP-->>useAutoIngestion: 202 {run_id}
        useAutoIngestion->>User: toast.info('Refreshing feed...')
        useAutoIngestion->>IngestionButton: dispatch 'auto-ingestion-started' event
        IngestionButton->>IngestionButton: setPhase('starting')
        IngestionButton->>MCP: Poll GET /trigger/ingestion/{run_id}
        MCP-->>IngestionButton: Status updates
        IngestionButton->>User: Show progress
    else Already fired
        useAutoIngestion->>useAutoIngestion: Skip (no-op)
    end
    
    User->>Articles: Navigate to feed
    Articles->>Firestore: Query articles (48h filter)
    Note over Articles,Firestore: where('published_at', '>=', cutoff)<br/>orderBy('published_at', 'desc')
    Firestore-->>Articles: Return filtered articles
    Articles->>User: Display articles
Loading

Last reviewed commit: dadde4d

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 247 to 254
} else {
q = query(
articlesRef,
where('category', '==', selectedCategory),
where('published_at', '>=', cutoff),
orderBy('published_at', 'desc'),
limit(100)
)
Copy link

Choose a reason for hiding this comment

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

Verify that this composite query works without additional Firestore indexes. The query combines category ==, published_at >=, and orderBy published_at desc. While the existing index (category ASC, published_at DESC) should handle this, test in production to confirm no index errors occur.

@greptile-apps
Copy link

greptile-apps bot commented Feb 20, 2026

Additional Comments (1)

perception_app/mcp_service/routers/rss.py
This behavior change breaks the existing test test_malformed_date_returns_true in tests/unit/test_rss_parsing.py:210-216, which expects True. Update the test to expect False and rename it to test_malformed_date_returns_false.

    def test_malformed_date_returns_false(self):
        """Test malformed date returns False (exclusive)."""
        from perception_app.mcp_service.routers.rss import is_within_time_window

        result = is_within_time_window("not-a-date", 24)

        assert result is False

@qodo-code-review
Copy link

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (3) 📎 Requirement gaps (0)

Grey Divider


Action required

1. useAutoIngestion() swallows fetch errors 📘 Rule violation ⛯ Reliability
Description
The new auto-ingestion request silently ignores network/JSON/server failures and unexpected HTTP
statuses, making production issues hard to detect and debug. This can leave users with a stale
dashboard with no actionable feedback or telemetry.
Code

dashboard/src/hooks/useAutoIngestion.ts[R13-38]

+    fetch(`${MCP_URL}/trigger/ingestion`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        trigger: 'auto',
+        time_window_hours: 24,
+        max_items_per_source: 50,
+      }),
+    })
+      .then(async (res) => {
+        if (res.status === 202) {
+          const data = await res.json()
+          toast.info('Refreshing your feed...', {
+            description: `Run ID: ${data.run_id}`,
+          })
+          window.dispatchEvent(
+            new CustomEvent('auto-ingestion-started', {
+              detail: { run_id: data.run_id },
+            })
+          )
+        }
+        // 409 = already running, silently ignore
+      })
+      .catch(() => {
+        // Fire-and-forget — don't block dashboard load
+      })
Evidence
Compliance requires handling failure points with meaningful context; the new code ignores non-202
responses and has an empty .catch() that swallows all errors without logging or user-safe
feedback.

Rule 3: Generic: Robust Error Handling and Edge Case Management
dashboard/src/hooks/useAutoIngestion.ts[22-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useAutoIngestion()` currently ignores non-`202` HTTP responses and swallows all fetch/parse errors in an empty `.catch()`, creating silent failures.

## Issue Context
Auto-ingestion is a critical freshness mechanism for the dashboard. When it fails, we need at least (a) safe user-facing feedback (generic), and (b) internal diagnostics (logging/telemetry) with enough context (status code, request path, correlation/run id if available).

## Fix Focus Areas
- dashboard/src/hooks/useAutoIngestion.ts[13-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Missed auto-ingestion event 🐞 Bug ⛯ Reliability
Description
Auto-ingestion progress can be missed because a one-shot CustomEvent is dispatched from one
component effect while the listener is registered in another component effect, with no guaranteed
ordering. This can leave the IngestionButton idle even though an ingestion run is active (more
likely under React StrictMode dev double-mount).
Code

dashboard/src/components/IngestionButton.tsx[R148-160]

+  // Listen for auto-ingestion started by useAutoIngestion hook
+  useEffect(() => {
+    const handler = (e: Event) => {
+      const runId = (e as CustomEvent<{ run_id: string }>).detail.run_id
+      if (phase === 'idle' && runId) {
+        setPhase('starting')
+        startTimeRef.current = Date.now()
+        pollStatus(runId)
+      }
+    }
+    window.addEventListener('auto-ingestion-started', handler)
+    return () => window.removeEventListener('auto-ingestion-started', handler)
+  }, [phase, pollStatus])
Evidence
Dashboard mounts both useAutoIngestion() and <IngestionButton/>. The run id is communicated via
a one-time window.dispatchEvent(...) from useAutoIngestion’s effect, while IngestionButton
registers its listener in a separate effect; effect ordering across components is not guaranteed, so
the event can be lost if dispatched before the listener is attached. React.StrictMode increases the
chance of this in development due to mount/unmount/remount behavior.

dashboard/src/pages/Dashboard.tsx[35-56]
dashboard/src/hooks/useAutoIngestion.ts[7-33]
dashboard/src/components/IngestionButton.tsx[148-160]
dashboard/src/main.tsx[6-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Auto-ingestion communicates the run id via a one-shot window event. Because listener registration happens in a different component effect, the event can be dispatched before the listener exists, so the UI never starts polling.

### Issue Context
- `useAutoIngestion()` dispatches `auto-ingestion-started` after receiving 202.
- `IngestionButton` only polls if it receives that event.
- There is no fallback to discover an active run if the event is missed.

### Fix Focus Areas
- dashboard/src/hooks/useAutoIngestion.ts[7-38]
- dashboard/src/components/IngestionButton.tsx[148-160]
- dashboard/src/pages/Dashboard.tsx[35-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Session guard blocks retry 🐞 Bug ⛯ Reliability
Description
The auto-ingestion sessionStorage guard is set before the POST succeeds and errors are swallowed. If
the request fails, auto-ingestion won’t retry for the rest of the browser session, leaving the feed
stale.
Code

dashboard/src/hooks/useAutoIngestion.ts[R9-38]

+    if (sessionStorage.getItem(SESSION_KEY)) return
+
+    sessionStorage.setItem(SESSION_KEY, '1')
+
+    fetch(`${MCP_URL}/trigger/ingestion`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        trigger: 'auto',
+        time_window_hours: 24,
+        max_items_per_source: 50,
+      }),
+    })
+      .then(async (res) => {
+        if (res.status === 202) {
+          const data = await res.json()
+          toast.info('Refreshing your feed...', {
+            description: `Run ID: ${data.run_id}`,
+          })
+          window.dispatchEvent(
+            new CustomEvent('auto-ingestion-started', {
+              detail: { run_id: data.run_id },
+            })
+          )
+        }
+        // 409 = already running, silently ignore
+      })
+      .catch(() => {
+        // Fire-and-forget — don't block dashboard load
+      })
Evidence
The code sets SESSION_KEY unconditionally before calling fetch, and the catch handler is
intentionally empty. That makes transient failures permanent for the session because subsequent
mounts return early.

dashboard/src/hooks/useAutoIngestion.ts[8-12]
dashboard/src/hooks/useAutoIngestion.ts[34-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Auto-ingestion sets the session guard before the network request completes. If the request fails, the guard remains and prevents retries for the whole session.

### Issue Context
Current implementation intentionally swallows errors, but should not permanently disable auto-ingestion due to transient failures.

### Fix Focus Areas
- dashboard/src/hooks/useAutoIngestion.ts[8-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. CustomEvent detail not guarded 📘 Rule violation ⛯ Reliability
Description
The event handler assumes e.detail.run_id always exists, which can throw at runtime if the event
is dispatched without the expected payload. This creates a brittle edge case that can break
ingestion progress updates.
Code

dashboard/src/components/IngestionButton.tsx[R150-156]

+    const handler = (e: Event) => {
+      const runId = (e as CustomEvent<{ run_id: string }>).detail.run_id
+      if (phase === 'idle' && runId) {
+        setPhase('starting')
+        startTimeRef.current = Date.now()
+        pollStatus(runId)
+      }
Evidence
Compliance requires explicit handling of null/edge cases; the new handler dereferences
detail.run_id without validating the event shape.

Rule 3: Generic: Robust Error Handling and Edge Case Management
dashboard/src/components/IngestionButton.tsx[150-156]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `auto-ingestion-started` listener assumes the incoming `Event` is always a `CustomEvent` with a `detail.run_id`, which can cause runtime errors if the payload is absent or malformed.

## Issue Context
Browser events are loosely typed at runtime; the handler should defensively validate `detail` and `run_id` before use.

## Fix Focus Areas
- dashboard/src/components/IngestionButton.tsx[150-156]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Unstructured warning log message 📘 Rule violation ✧ Quality
Description
The new Python log line uses an interpolated string rather than structured logging, reducing its
auditability and making it harder to reliably parse/monitor. This may also inadvertently propagate
untrusted input (published_at) into logs without consistent fields.
Code

perception_app/mcp_service/routers/rss.py[R131-133]

+    except Exception as e:
+        logger.warning(f"is_within_time_window: failed to parse '{published_at}': {e}")
+        return False  # Exclude if we can't parse date
Evidence
The compliance checklist requires structured logs; the new warning is a free-form f-string
containing dynamic values, rather than structured key/value fields.

Rule 5: Generic: Secure Logging Practices
perception_app/mcp_service/routers/rss.py[131-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`logger.warning()` is currently emitted as an f-string, which is unstructured and harder to parse/monitor, and embeds untrusted input directly into the message.

## Issue Context
Compliance requires logs to be structured and avoid leaking sensitive/untrusted data in uncontrolled ways. Prefer key/value fields (or `extra=`) so downstream logging can filter on consistent attributes.

## Fix Focus Areas
- perception_app/mcp_service/routers/rss.py[131-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Auto ignores 409 run 🐞 Bug ⛯ Reliability
Description
If the trigger endpoint returns 409 (run already active), auto-ingestion silently ignores it and
never starts polling the active run. Users then won’t see progress for the already-running ingestion
and may attempt a manual run unnecessarily.
Code

dashboard/src/hooks/useAutoIngestion.ts[R22-35]

+      .then(async (res) => {
+        if (res.status === 202) {
+          const data = await res.json()
+          toast.info('Refreshing your feed...', {
+            description: `Run ID: ${data.run_id}`,
+          })
+          window.dispatchEvent(
+            new CustomEvent('auto-ingestion-started', {
+              detail: { run_id: data.run_id },
+            })
+          )
+        }
+        // 409 = already running, silently ignore
+      })
Evidence
Backend explicitly returns 409 with detail.active_run_id and a poll URL. Manual ingestion handles
409 by parsing JSON and informing the user, but auto-ingestion does not parse 409 at all and does
not dispatch an event with the active run id, so the IngestionButton cannot pick it up.

dashboard/src/hooks/useAutoIngestion.ts[22-35]
perception_app/mcp_service/routers/trigger.py[514-524]
dashboard/src/components/IngestionButton.tsx[177-183]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Auto-ingestion ignores HTTP 409 responses even though the backend provides the active run id. This prevents progress UI from attaching to an already-running ingestion.

### Issue Context
Backend returns `detail.active_run_id` on 409.

### Fix Focus Areas
- dashboard/src/hooks/useAutoIngestion.ts[22-35]
- perception_app/mcp_service/routers/trigger.py[514-524]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
7. Cutoff string format risk 🐞 Bug ✓ Correctness
Description
The new Firestore published_at >= cutoff uses a cutoff string from Date.toISOString() (always
includes milliseconds and Z). Since published_at is stored as a string and backend producers use
datetime.isoformat() (often no milliseconds and +00:00), lexicographic range filtering can be
incorrect at boundaries and can inconsistently include/exclude documents if formats vary.
Code

dashboard/src/pages/Articles.tsx[R237-252]

+        const cutoff = new Date(Date.now() - 48 * 60 * 60 * 1000).toISOString()
        let q

        if (selectedCategory === 'all') {
          q = query(
            articlesRef,
+            where('published_at', '>=', cutoff),
            orderBy('published_at', 'desc'),
            limit(100)
          )
        } else {
          q = query(
            articlesRef,
            where('category', '==', selectedCategory),
+            where('published_at', '>=', cutoff),
            orderBy('published_at', 'desc'),
Evidence
Frontend compares string-to-string in Firestore. Backend persists published_at verbatim as a
string. RSS normalization returns dt.isoformat() from a struct_time (no microseconds) which
typically yields strings like YYYY-MM-DDTHH:MM:SS+00:00, while the new cutoff is
YYYY-MM-DDTHH:MM:SS.mmmZ. These differences can break strict lexicographic expectations around
identical-second boundaries and make the inequality filter fragile.

dashboard/src/pages/Articles.tsx[235-254]
perception_app/mcp_service/routers/storage.py[133-149]
perception_app/mcp_service/routers/rss.py[88-92]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The 48h cutoff introduces a string inequality on `published_at`. Since `published_at` is stored as a string with potentially varying ISO formats, lexicographic comparisons can be fragile.

### Issue Context
- Frontend cutoff is produced by `Date.toISOString()`.
- Backend stores `published_at` as a string and RSS normalization uses `datetime.isoformat()`.

### Fix Focus Areas
- dashboard/src/pages/Articles.tsx[235-254]
- perception_app/mcp_service/routers/storage.py[133-151]
- perception_app/mcp_service/routers/rss.py[88-106]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +13 to +38
fetch(`${MCP_URL}/trigger/ingestion`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
trigger: 'auto',
time_window_hours: 24,
max_items_per_source: 50,
}),
})
.then(async (res) => {
if (res.status === 202) {
const data = await res.json()
toast.info('Refreshing your feed...', {
description: `Run ID: ${data.run_id}`,
})
window.dispatchEvent(
new CustomEvent('auto-ingestion-started', {
detail: { run_id: data.run_id },
})
)
}
// 409 = already running, silently ignore
})
.catch(() => {
// Fire-and-forget — don't block dashboard load
})

Choose a reason for hiding this comment

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

Action required

1. useautoingestion() swallows fetch errors 📘 Rule violation ⛯ Reliability

The new auto-ingestion request silently ignores network/JSON/server failures and unexpected HTTP
statuses, making production issues hard to detect and debug. This can leave users with a stale
dashboard with no actionable feedback or telemetry.
Agent Prompt
## Issue description
`useAutoIngestion()` currently ignores non-`202` HTTP responses and swallows all fetch/parse errors in an empty `.catch()`, creating silent failures.

## Issue Context
Auto-ingestion is a critical freshness mechanism for the dashboard. When it fails, we need at least (a) safe user-facing feedback (generic), and (b) internal diagnostics (logging/telemetry) with enough context (status code, request path, correlation/run id if available).

## Fix Focus Areas
- dashboard/src/hooks/useAutoIngestion.ts[13-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +148 to +160
// Listen for auto-ingestion started by useAutoIngestion hook
useEffect(() => {
const handler = (e: Event) => {
const runId = (e as CustomEvent<{ run_id: string }>).detail.run_id
if (phase === 'idle' && runId) {
setPhase('starting')
startTimeRef.current = Date.now()
pollStatus(runId)
}
}
window.addEventListener('auto-ingestion-started', handler)
return () => window.removeEventListener('auto-ingestion-started', handler)
}, [phase, pollStatus])

Choose a reason for hiding this comment

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

Action required

2. Missed auto-ingestion event 🐞 Bug ⛯ Reliability

Auto-ingestion progress can be missed because a one-shot CustomEvent is dispatched from one
component effect while the listener is registered in another component effect, with no guaranteed
ordering. This can leave the IngestionButton idle even though an ingestion run is active (more
likely under React StrictMode dev double-mount).
Agent Prompt
### Issue description
Auto-ingestion communicates the run id via a one-shot window event. Because listener registration happens in a different component effect, the event can be dispatched before the listener exists, so the UI never starts polling.

### Issue Context
- `useAutoIngestion()` dispatches `auto-ingestion-started` after receiving 202.
- `IngestionButton` only polls if it receives that event.
- There is no fallback to discover an active run if the event is missed.

### Fix Focus Areas
- dashboard/src/hooks/useAutoIngestion.ts[7-38]
- dashboard/src/components/IngestionButton.tsx[148-160]
- dashboard/src/pages/Dashboard.tsx[35-56]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +9 to +38
if (sessionStorage.getItem(SESSION_KEY)) return

sessionStorage.setItem(SESSION_KEY, '1')

fetch(`${MCP_URL}/trigger/ingestion`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
trigger: 'auto',
time_window_hours: 24,
max_items_per_source: 50,
}),
})
.then(async (res) => {
if (res.status === 202) {
const data = await res.json()
toast.info('Refreshing your feed...', {
description: `Run ID: ${data.run_id}`,
})
window.dispatchEvent(
new CustomEvent('auto-ingestion-started', {
detail: { run_id: data.run_id },
})
)
}
// 409 = already running, silently ignore
})
.catch(() => {
// Fire-and-forget — don't block dashboard load
})

Choose a reason for hiding this comment

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

Action required

3. Session guard blocks retry 🐞 Bug ⛯ Reliability

The auto-ingestion sessionStorage guard is set before the POST succeeds and errors are swallowed. If
the request fails, auto-ingestion won’t retry for the rest of the browser session, leaving the feed
stale.
Agent Prompt
### Issue description
Auto-ingestion sets the session guard before the network request completes. If the request fails, the guard remains and prevents retries for the whole session.

### Issue Context
Current implementation intentionally swallows errors, but should not permanently disable auto-ingestion due to transient failures.

### Fix Focus Areas
- dashboard/src/hooks/useAutoIngestion.ts[8-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link

@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

🧹 Nitpick comments (3)
dashboard/src/hooks/useAutoIngestion.ts (1)

4-4: MCP_URL is duplicated — extract to a shared constants module.

IngestionButton.tsx (line 5) defines the exact same constant. Any URL change would need to be made in two places.

♻️ Suggested refactor

Create dashboard/src/config/constants.ts:

+export const MCP_URL = 'https://perception-mcp-w53xszfqnq-uc.a.run.app'

Then in both useAutoIngestion.ts and IngestionButton.tsx:

-const MCP_URL = 'https://perception-mcp-w53xszfqnq-uc.a.run.app'
+import { MCP_URL } from '../config/constants'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/hooks/useAutoIngestion.ts` at line 4, MCP_URL is duplicated
between useAutoIngestion.ts and IngestionButton.tsx; extract it into a shared
constant (e.g., export const MCP_URL) in a new module like
dashboard/src/config/constants.ts, then replace the local MCP_URL definitions in
useAutoIngestion.ts and IngestionButton.tsx with imports from that constants
module and remove the duplicated declarations so both files reference the single
exported MCP_URL symbol.
perception_app/mcp_service/routers/rss.py (1)

131-133: Defensive logic is correct; align warning format with the file's structured logging convention.

The logic change (return False) is the right call. However, the new logger.warning on line 132 uses a plain f-string, while every other log statement in this file uses logger.info/error(json.dumps({...})) for structured output. The pipeline lint/test run also surfaces this line as a warning. Keeping the format consistent makes logs easier to parse downstream.

♻️ Suggested fix
-    except Exception as e:
-        logger.warning(f"is_within_time_window: failed to parse '{published_at}': {e}")
-        return False  # Exclude if we can't parse date
+    except Exception as e:
+        logger.warning(json.dumps({
+            "severity": "WARNING",
+            "message": "is_within_time_window: failed to parse published_at",
+            "published_at": published_at,
+            "error": str(e),
+        }))
+        return False  # Exclude if we can't parse date
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@perception_app/mcp_service/routers/rss.py` around lines 131 - 133, The
warning in is_within_time_window uses a plain f-string; change it to the file's
structured logging style by logging a JSON object (e.g., via
logger.warning(json.dumps({...}))) that includes a descriptive message, the
published_at value and the exception details (e) so it matches other
logger.info/error calls in the module; keep the subsequent return False
unchanged.
dashboard/src/components/IngestionButton.tsx (1)

149-160: phase in the dependency array causes the listener to be torn down and re-registered on every phase transition.

During an active ingestion run, phase cycles through several values (startinginitializingloading_sources → … → idle). Including phase in the deps means the auto-ingestion-started listener is removed and re-added on each transition. While the phase === 'idle' guard prevents double-triggering and the event only fires once per session, the repeated re-registrations are unnecessary. The standard fix is a phaseRef so the handler doesn't need to close over phase:

♻️ Suggested refactor
+  const phaseRef = useRef(phase)
+  useEffect(() => { phaseRef.current = phase }, [phase])

   // Listen for auto-ingestion started by useAutoIngestion hook
   useEffect(() => {
     const handler = (e: Event) => {
       const runId = (e as CustomEvent<{ run_id: string }>).detail.run_id
-      if (phase === 'idle' && runId) {
+      if (phaseRef.current === 'idle' && runId) {
         setPhase('starting')
         startTimeRef.current = Date.now()
         pollStatus(runId)
       }
     }
     window.addEventListener('auto-ingestion-started', handler)
     return () => window.removeEventListener('auto-ingestion-started', handler)
-  }, [phase, pollStatus])
+  }, [pollStatus])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/components/IngestionButton.tsx` around lines 149 - 160, The
effect registers an `auto-ingestion-started` handler that closes over `phase`,
causing the listener to be removed/re-added on every phase change; instead, make
`phase` a ref (e.g., `phaseRef`) that you update whenever `phase` changes and
have the `handler` read `phaseRef.current` so the effect can omit `phase` from
its dependency array; keep `pollStatus` stable (or include it if not memoized)
and keep `startTimeRef`, `setPhase`, and `pollStatus` referenced by name so you
update `startTimeRef.current`, call `setPhase('starting')`, and invoke
`pollStatus(runId)` inside the handler using the refs/stable callbacks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dashboard/src/hooks/useAutoIngestion.ts`:
- Around line 22-27: The response from res.json() is untyped so data.run_id may
be undefined and toast could show "Run ID: undefined"; in useAutoIngestion,
define an interface (e.g., AutoIngestionResponse { run_id?: string }) or cast
the JSON to that type, then guard or normalize the value before passing to toast
(e.g., const runId = data?.run_id ?? 'unknown' or only include the Run ID
description when present) so the toast always receives a safe, meaningful string
and you avoid rendering "undefined".

---

Nitpick comments:
In `@dashboard/src/components/IngestionButton.tsx`:
- Around line 149-160: The effect registers an `auto-ingestion-started` handler
that closes over `phase`, causing the listener to be removed/re-added on every
phase change; instead, make `phase` a ref (e.g., `phaseRef`) that you update
whenever `phase` changes and have the `handler` read `phaseRef.current` so the
effect can omit `phase` from its dependency array; keep `pollStatus` stable (or
include it if not memoized) and keep `startTimeRef`, `setPhase`, and
`pollStatus` referenced by name so you update `startTimeRef.current`, call
`setPhase('starting')`, and invoke `pollStatus(runId)` inside the handler using
the refs/stable callbacks.

In `@dashboard/src/hooks/useAutoIngestion.ts`:
- Line 4: MCP_URL is duplicated between useAutoIngestion.ts and
IngestionButton.tsx; extract it into a shared constant (e.g., export const
MCP_URL) in a new module like dashboard/src/config/constants.ts, then replace
the local MCP_URL definitions in useAutoIngestion.ts and IngestionButton.tsx
with imports from that constants module and remove the duplicated declarations
so both files reference the single exported MCP_URL symbol.

In `@perception_app/mcp_service/routers/rss.py`:
- Around line 131-133: The warning in is_within_time_window uses a plain
f-string; change it to the file's structured logging style by logging a JSON
object (e.g., via logger.warning(json.dumps({...}))) that includes a descriptive
message, the published_at value and the exception details (e) so it matches
other logger.info/error calls in the module; keep the subsequent return False
unchanged.

Comment on lines +22 to +27
.then(async (res) => {
if (res.status === 202) {
const data = await res.json()
toast.info('Refreshing your feed...', {
description: `Run ID: ${data.run_id}`,
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

data.run_id is untyped; toast can render "Run ID: undefined" if the response shape changes.

res.json() returns any, so data.run_id silently falls through as undefined if the API response doesn't include that field.

🛡️ Proposed fix
-        if (res.status === 202) {
-          const data = await res.json()
-          toast.info('Refreshing your feed...', {
-            description: `Run ID: ${data.run_id}`,
-          })
-          window.dispatchEvent(
-            new CustomEvent('auto-ingestion-started', {
-              detail: { run_id: data.run_id },
-            })
-          )
-        }
+        if (res.status === 202) {
+          const data = await res.json() as { run_id?: string }
+          if (data.run_id) {
+            toast.info('Refreshing your feed...', {
+              description: `Run ID: ${data.run_id}`,
+            })
+            window.dispatchEvent(
+              new CustomEvent('auto-ingestion-started', {
+                detail: { run_id: data.run_id },
+              })
+            )
+          }
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/src/hooks/useAutoIngestion.ts` around lines 22 - 27, The response
from res.json() is untyped so data.run_id may be undefined and toast could show
"Run ID: undefined"; in useAutoIngestion, define an interface (e.g.,
AutoIngestionResponse { run_id?: string }) or cast the JSON to that type, then
guard or normalize the value before passing to toast (e.g., const runId =
data?.run_id ?? 'unknown' or only include the Run ID description when present)
so the toast always receives a safe, meaningful string and you avoid rendering
"undefined".

jeremylongshore and others added 2 commits February 26, 2026 23:19
Add dual entrypoint docs, codebase gotchas section, dashboard dev
commands, functions directory, pytest markers, and fix agent count.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
is_within_time_window returns False for unparseable dates (excludes
garbage articles). Test was incorrectly asserting True.

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

@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.

🧹 Nitpick comments (1)
CLAUDE.md (1)

193-202: Consider documenting VERTEX_SEARCH_DATA_STORE_ID.

The environment variables section lists the core Vertex AI variables, but based on learnings, VERTEX_SEARCH_DATA_STORE_ID may also be needed for RAG search functionality. Verify if this should be documented here.

Based on learnings, the environment variable VERTEX_SEARCH_DATA_STORE_ID is needed to enable Vertex-managed sessions and RAG search.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 193 - 202, Add documentation for
VERTEX_SEARCH_DATA_STORE_ID under the "Environment Variables" section alongside
VERTEX_PROJECT_ID, VERTEX_LOCATION, and VERTEX_AGENT_ENGINE_ID: state that
VERTEX_SEARCH_DATA_STORE_ID is required for Vertex-managed sessions and RAG
search functionality, provide the expected format/example value, and note any
defaults or where to find/create the datastore in Vertex AI so readers can
configure it correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@CLAUDE.md`:
- Around line 193-202: Add documentation for VERTEX_SEARCH_DATA_STORE_ID under
the "Environment Variables" section alongside VERTEX_PROJECT_ID,
VERTEX_LOCATION, and VERTEX_AGENT_ENGINE_ID: state that
VERTEX_SEARCH_DATA_STORE_ID is required for Vertex-managed sessions and RAG
search functionality, provide the expected format/example value, and note any
defaults or where to find/create the datastore in Vertex AI so readers can
configure it correctly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dadde4d and a155887.

📒 Files selected for processing (1)
  • CLAUDE.md

@jeremylongshore jeremylongshore merged commit 733fdf9 into main Feb 27, 2026
12 checks passed
@jeremylongshore jeremylongshore deleted the feat/dashboard-landing-auto-ingestion branch February 27, 2026 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant