fix: allows nullable source_id in taxonomy#88
Conversation
WalkthroughThis PR introduces a canonical "no source" bucket for taxonomy scopes where 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/models/taxonomy.go`:
- Line 64: The change to make TaxonomyScope.SourceID optional with the omitempty
tag creates a filtering problem in the list-runs path where empty source_id
values are dropped, making it impossible to distinguish between "no filter
applied" and "filter by empty source_id". Update the filter contract across the
handler, service, and repository layers for the list-runs operation to use a
tri-state filter mechanism (such as a pointer to string (*string) or an explicit
presence flag) instead of a plain string filter. This ensures that empty-string
filtering is preserved and passed through the entire stack, allowing callers to
properly scope run history to the no-source bucket while maintaining consistent
tenant access rules across all resource access paths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: fc25f77f-26bb-4355-98dc-2bfe074cc837
📒 Files selected for processing (6)
internal/models/taxonomy.gointernal/repository/taxonomy_repository.gointernal/service/id_validation.gointernal/service/taxonomy_service.gomigrations/010_taxonomy_schema.sqltests/taxonomy_no_source_test.go
Problem
feedback_records.source_idis nullable in the base Hub schema (001_initial_schema.sql), so feedback can be ingested with no attributed source. But taxonomy treatedsource_idas required everywhere, so those records could never get a taxonomy — they were silently invisible to discovery and rejected at run creation.Approach
Taxonomy now supports a nullable source scope instead of forcing
feedback_records.source_idtoNOT NULL(which would be a broader ingestion contract change).Key decision: rather than making
source_idnullable inside the composite PK/FK/UNIQUE constraints — whereNULL != NULLbreaks key matching and forces asource_key/surrogate-PK workaround — taxonomy canonicalizes "no source" to the empty string''at the Go boundary. The Go layer always sends astring, neverNULL, sosource_idstaysNOT NULLin taxonomy tables and''is a valid, comparable key value.NULLstays confined tofeedback_records, and only the queries that read it use null-safe matching.Changes
migrations/010_taxonomy_schema.sql— dropped the three*_source_id_requiredblank-check constraints (taxonomy_runs,taxonomy_active_runs,taxonomy_node_events).source_idstaysNOT NULL(satisfied by''); composite keys unchanged. Added a header note documenting the empty-string convention. (Edited in place — taxonomy is unreleased,no new migration needed.)
internal/models/taxonomy.go—TaxonomyScope.SourceIDvalidation relaxed fromrequired,min=1toomitempty,no_null_bytes,max=255.internal/service/id_validation.go— addednormalizeOptionalIdentifier(empty →""); shared checks extracted intonormalizeIdentifierValue.internal/service/taxonomy_service.go—normalizeTaxonomyScopenow treatssource_idas optional.internal/repository/taxonomy_repository.go—ListFieldOptionsremoved thesource_id IS NOT NULL AND btrim(...) <> ''filter and now groups on and emitsCOALESCE(NULLIF(btrim(source_id), ''), '')soNULLand blank collapse into a single "no source" bucket;CountScopeInputandGetRunInputmatchfeedback_recordswithNULLIF(btrim(fr.source_id), '') IS NOT DISTINCT FROM NULLIF(btrim($3), '').Tests
tests/taxonomy_no_source_test.go(new) —NULL+ blank feedback collapse into one bucket; empty-source scope null-safe matches both rows; run creation and the in-progress guard work with the''scope.tests/taxonomy_schema_test.go(existing) — still passes, confirming the constraint drop didn't break schema relationships/cascades.Verification
go build ./...,go vet ./...,golangci-lint— clean.Out of scope / follow-ups
feedback_records.source_idstays nullable — ingestion contract unchanged.openapi.yaml— no taxonomy schemas there yet (unreleased), nothing to update.ListRunsfilter still treats emptysource_idas "no filter" (history query). Listing only no-source runs would need an explicit flag — not added here.source_idbucket as "No source" / "Unattributed"; that label lives in the frontend repo. The API now emits an emptysource_idfor the bucket.