Repository navigation
Clarify validator semantics and downstream dedupe expectations - #3
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a new state_resources writer process and script, introduces staging/written stream and writer consumer-group constants, and refactors validator logic to reclaim and centrally handle pending Redis stream entries with updated DB status handling and error semantics. Changes
Sequence DiagramsequenceDiagram
participant Redis as Redis Stream\n(validated)
participant Writer as StateResourcesWriter
participant Staging as Postgres Staging Table
participant DB as Postgres DB
participant StgStream as Redis Stream\n(staging:written)
loop Batch loop
Writer->>Redis: XREADGROUP (fetch batch / pending reclaim)
Redis-->>Writer: messages (ids + payloads)
alt Per message
Writer->>Writer: Parse & validate payload
alt Invalid payload
Writer->>Staging: UPDATE status → failed (with reason)
Writer->>Redis: XACK id
else Valid payload
Writer->>Staging: SELECT staging row by ingest_key
alt row missing or not validated
Writer->>Redis: XACK id (skipped)
else row validated
Writer->>DB: BEGIN
Writer->>DB: UPSERT state_resources (by state_fips)
Writer->>Staging: UPDATE status → written
Writer->>DB: COMMIT
Writer->>StgStream: XADD staging:written ...
Writer->>Redis: XACK id
end
end
end
end
Writer->>Redis: QUIT
Writer->>DB: Close pool
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
backend/src/pipeline/writers/stateResourcesWriter.ts (1)
61-121: Consider reusing the validator's payload schema here.This parser is already looser than
backend/src/pipeline/validators/stateResourcesValidator.ts: it no longer checks HTTP(S) URLs, extrasourceskeys, or the FIPS/abbreviation mapping. Pulling normalization/validation into a shared helper would keep validator and writer semantics from drifting.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/src/pipeline/writers/stateResourcesWriter.ts` around lines 61 - 121, The parseStateResourcePayload implementation diverges from the stricter validator (stateResourcesValidator.ts); update it to reuse the validator's payload schema/normalizer instead of reimplementing checks: import and call the shared validation/normalization function used by backend/src/pipeline/validators/stateResourcesValidator.ts (or extract the validator logic into a new helper like normalizeStateResourcePayload), then in parseStateResourcePayload delegate all checks (HTTP(S) URL format, allowed sources keys, FIPS/abbreviation mapping, citation shape) to that helper and return its normalized payload on success or its error reason on failure; reference parseStateResourcePayload, StateResourcePayload, StateResourceSources and the validator module when locating where to plug in the shared helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/src/pipeline/validators/stateResourcesValidator.ts`:
- Around line 318-320: The current behavior moves rows to "failed" when
redis.xAck() throws after a successful redis.xAdd(STAGING_VALIDATED_STREAM, ...)
and validated DB update, which causes the published validated event to be
skipped by stateResourcesWriter; change this so post-publish ack failures do NOT
transition the DB row to "failed". Specifically, in the code path that calls
redis.xAdd(...) and then redis.xAck(...), catch ack errors separately: do not
perform the existing failed-state rewrite (the logic in the outer catch handling
rows -> "failed" and acking the pending message); instead log the ack error with
context (ingest_key, stream entry id), schedule/attempt a retry for xAck (or
push the entry id to a dedicated retry queue), and leave the DB row in
"validated" so downstream dedupe/consumers can still process the published
event; update handling around STAGING_VALIDATED_STREAM, redis.xAdd, and
redis.xAck to reflect this separation of post-publish ack failures from state
transitions.
In `@backend/src/pipeline/writers/stateResourcesWriter.ts`:
- Around line 328-334: The worker currently only calls redis.xReadGroup(...)
with id: ">" and never reclaims pending messages, so add an XAUTOCLAIM pass
before entering the while (keepRunning) loop to claim and reprocess any pending
entries for the STAGING_STATE_RESOURCES_WRITER_GROUP consumer group for this
consumerName on STAGING_VALIDATED_STREAM; call redis.xAutoClaim (or XAUTOCLAIM
equivalent) to fetch pending IDs (e.g., min-idle-time 0 or configured threshold)
and re-enqueue/process them the same way you handle xReadGroup results, ensuring
xAck still occurs after successful processing and referencing the existing
xReadGroup/xAck flow to integrate claims.
---
Nitpick comments:
In `@backend/src/pipeline/writers/stateResourcesWriter.ts`:
- Around line 61-121: The parseStateResourcePayload implementation diverges from
the stricter validator (stateResourcesValidator.ts); update it to reuse the
validator's payload schema/normalizer instead of reimplementing checks: import
and call the shared validation/normalization function used by
backend/src/pipeline/validators/stateResourcesValidator.ts (or extract the
validator logic into a new helper like normalizeStateResourcePayload), then in
parseStateResourcePayload delegate all checks (HTTP(S) URL format, allowed
sources keys, FIPS/abbreviation mapping, citation shape) to that helper and
return its normalized payload on success or its error reason on failure;
reference parseStateResourcePayload, StateResourcePayload, StateResourceSources
and the validator module when locating where to plug in the shared helper.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 18b87888-e077-4a1c-bc93-991bc96334d6
📒 Files selected for processing (5)
backend/package.jsonbackend/src/config/stateResourcePipeline.tsbackend/src/pipeline/validators/stateResourcesValidator.tsbackend/src/pipeline/writers/stateResourcesWriter.tsbackend/src/scripts/writeStateResources.ts
California is phase-4 state #3. What the survey established: - LegiScan prints the chamber as the state own abbreviation, so the lower chamber is A where it is called the Assembly. All 9,948 CA 2172 Assembly rolls failed to parse as chamber is not H or S; both letters now map to house, our single lower-chamber key. - California puts the measure and its author INSIDE the desc, so the question is a phrase within a near-unique string. Assembly patterns are unanchored (the desc starts with the bill), Senate patterns anchor at ^ (the desc starts with the question). Committee votes are worded as recommendations to the same destinations (Do pass. To consent calendar, Be adopted. To third reading), which is why the kept patterns require the chamber word or the anchor: as written every kept-type match is floor-sized and no committee-sized roll matches. - A Senate desc ending in a bare Reconsider is the vote GRANTING reconsideration, not the question itself (SB 627: 27-10 concurrence, 30-10 grant, 28-11 operative concurrence, all one day) - excluded, anchored at the end so the 91 committee reconsideration rolls stay with the tally cut instead of being stored as audit rows. Measured from the full dataset survey 2026-08-26 (5,057 bills / 19,942 rolls / 160 people): 0 duplicate roll_call_id identity groups, 0 summary-only rolls, 0 tally mismatches. 972 divided floor votes, 442 of them on measures that became law. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Phase-4 state #3. Local voteapp only; PROD UNTOUCHED. Fetch: 5,281 floor / 13,181 committee / 1,433 excluded-measure / 0 unrecorded / 0 duplicate / 0 surfaced. 972 divided floor votes, 442 of them on measures that became law across 227 measures. Crosswalk: 121 entries, 33 mapped, 88 explicit null. Three seatAgrees false proposals are sitting Assembly members running for the Senate (Connolly, Soria, Valencia), each confirmed off current_office. Neither Texas hand-add class applies: no name-variant miss, and no sitting member is running for a non-legislative California office. Fan-out is small and that is OUR roster coverage, not California: median 21 matched members per Assembly roll and 11 per Senate roll, because we hold Nov-2026 elections for only 26 of 80 Assembly districts. Re-import after the rosters fill adds members idempotently. Batch-01 = 20 rolls / 10 measures / 298 records, all ten carrying a stance. Judged from the chaptered text and its Legislative Counsel Digest on leginfo, which carries no sponsor statement of intent. All 20 votes were cast on the enrolled text (version-checked against each bill version list). Reconciled three ways; re-run is 298 unchanged. CODE-FINDINGS records the one defect left unfixed: 80 roll pairs that are identical except for date, which the Texas identity key does not collapse because date is part of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Summary by CodeRabbit
New Features
Chores