Skip to content
This repository was archived by the owner on Jun 9, 2026. It is now read-only.

chore(positions): add diagnostic logging to filing-fee lookup - #168

Open
dealvz wants to merge 1 commit into
developfrom
chore/filing-fee-diagnostic-logging
Open

chore(positions): add diagnostic logging to filing-fee lookup#168
dealvz wants to merge 1 commit into
developfrom
chore/filing-fee-diagnostic-logging

Conversation

@dealvz

@dealvz dealvz commented May 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Logging-only follow-up to #165. Surfaces which branch the filing-fee lookup took so we can debug null-fee responses in dev without DB access.

This commit was on the original branch but pushed after #165 merged, so it didn't make it in. Cherry-picking now to unblock active filing-fee debugging on dev.

What it logs

debug level (visible in dev, off in prod unless explicitly enabled). Every lookupFilingFee call emits one of:

  • outcome: 'skipped' with reason: 'no_place_id' or 'no_name' — the Position row doesn't have the fields we need to look up a Race.
  • outcome: 'no_race_match'placeId was set, but no Race row matched placeId + positionNames. Also logs sampleRacePositionNames (up to 5 races' position_names arrays for the same placeId) so we can see what names actually exist and decide whether to loosen the join.
  • outcome: 'matched' — race(s) found. Logs which race we picked, whether it had filing_requirements / salary, and what extractFilingFee returned (so multi_value / no_match / pct_of_salary_unresolvable show up here too).

Why now

Current state: PR #165 is merged but every filing-fee response in dev comes back null — including for positions where we expected real data (e.g. position c61611f1-7d9d-666f-c759-8fce3c3fc457, "Denver City Council - At Large"). The API response alone can't distinguish "Position has no placeId" from "Race join produced zero rows" — both render as identical-looking nulls. This commit makes the distinction visible in logs.

Risk / blast radius

  • Behavior: zero change — only adds this.logger.debug(...) calls.
  • Performance: one additional small query (SELECT positionNames FROM Race WHERE placeId = ... LIMIT 5) on the no_race_match branch only. Doesn't fire on the success path.
  • Test coverage: existing 45-test suite passes; no test changes needed since logging output is a side effect.

Test plan

  • Merge + wait for dev redeploy.
  • Hit GET /v1/positions/c61611f1-7d9d-666f-c759-8fce3c3fc457?includeDistrict=true&includeTurnout=true&electionDate=2027-04-06&includeFilingFee=true against https://election-api-dev.goodparty.org.
  • Check dev logs for a FilingFeeLookup line. The outcome field tells us which branch fired.

🤖 Generated with Claude Code


Note

Low Risk
Low risk: behavior is unchanged aside from additional debug logs and a small extra query only when no races match, with minimal performance impact.

Overview
Adds structured debug-level diagnostics to PositionsService.lookupFilingFee to make null filing-fee responses explainable.

The lookup now logs when it is skipped (missing placeId/name), when there is no race match (including a small sample of Race.positionNames for the placeId), and when a race is matched (including chosen race attributes and the extractFilingFee result).

Reviewed by Cursor Bugbot for commit cdfd665. Configure here.

Surfaces which branch fired when the lookup returns a null fee:

- `skipped` — position has no placeId or no name
- `no_race_match` — placeId + name produced zero races; also logs up to
  5 sample positionNames arrays for the same placeId so we can see what
  the data actually has and adjust the join
- `matched` — race(s) found; logs which race we picked + what extractor
  said (so multi_value / no_match / pct_unresolvable show up too)

Logging is `debug` level — visible in dev, off in prod unless explicitly
enabled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@delegate-reviewer delegate-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 blocker. Reply @delegate review after fixing.

Comment on lines +307 to +311
const sampleNames = await this.client.race.findMany({
where: { placeId: position.placeId },
select: { positionNames: true },
take: 5,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The diagnostic sampleNames query executes unconditionally on every races.length === 0 path, including production traffic. The database round-trip happens regardless of whether the runtime log level will ever emit the debug message. NestJS Logger has no level-check guard you can call before building the payload, so the only correct fix is to remove the extra query and log only what you already have.

Suggested change
const sampleNames = await this.client.race.findMany({
where: { placeId: position.placeId },
select: { positionNames: true },
take: 5,
})
this.logger.debug({
event: 'FilingFeeLookup',
outcome: 'no_race_match',
positionId: position.id,
positionName: position.name,
placeId: position.placeId,
})

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant