Add a small dependency-free user-agent classifier for broad browser and OS families. Edge variants share one label; unsupported or absent input becomes Other. No versions, device/bot detection, or growing parser framework.
Classify inside the existing worker's recordVisit task immediately before the single insert. HTTP capture and the raw queued event shape remain unchanged.
Add versioned migration 0002_visit_classification for stored browser and os labels. Existing visits and raw headers are preserved; existing labels start as Other, with no automatic backfill job.
Distinguish raw NewVisit input from a prepared ClassifiedVisit database insert.
Make the native SQLite dialect advertise transactional DDL through Kysely's public adapter API. This keeps migration schema changes and bookkeeping atomic rather than leaving a partial multi-statement upgrade after failure.
Scope
Classification only. No parser dependency, second insert, enrichment queue/marker, service, batching, retention-policy changes, invalid/base-visit collection, dashboard, visit-row UI, or export feature. Those analytics requirements remain separate work.
Classification is intentionally approximate: browsers may conceal their identity, and unsupported cases may fall into another broad family or Other.
Verification
Separate final readability and correctness re-reviews are clean after the approved migration-atomicity correction.
pnpm verify: schema drift, all workspace builds/types, formatting/ESLint, 420 unit tests and 7 integration tests passed; Svelte reports zero errors/warnings.
The existing real-worker HTTP/lock/drain story now checks that submitted Firefox/Linux user-agent data produces stored family labels; no additional HTTP story was added.
Offline pruned production artifact tested outside the repository: the worker resolves inside the deployment, persists Firefox/Linux labels, and exits naturally on SIGTERM with code 0.
DB package lint, Changesets status, and Git whitespace checks passed.
Agent-run Docker/Compose verification was not performed; container checks remain owner-only. Remote CI is separate from the passing local checks. Transactional migrations do not claim coordination between independently started processes.
## Summary
- Add a small dependency-free user-agent classifier for broad browser and OS families. Edge variants share one label; unsupported or absent input becomes `Other`. No versions, device/bot detection, or growing parser framework.
- Classify inside the existing worker's `recordVisit` task immediately before the single insert. HTTP capture and the raw queued event shape remain unchanged.
- Add versioned migration `0002_visit_classification` for stored `browser` and `os` labels. Existing visits and raw headers are preserved; existing labels start as `Other`, with no automatic backfill job.
- Distinguish raw `NewVisit` input from a prepared `ClassifiedVisit` database insert.
- Make the native SQLite dialect advertise transactional DDL through Kysely's public adapter API. This keeps migration schema changes and bookkeeping atomic rather than leaving a partial multi-statement upgrade after failure.
## Scope
Classification only. No parser dependency, second insert, enrichment queue/marker, service, batching, retention-policy changes, invalid/base-visit collection, dashboard, visit-row UI, or export feature. Those analytics requirements remain separate work.
Classification is intentionally approximate: browsers may conceal their identity, and unsupported cases may fall into another broad family or `Other`.
## Verification
- Separate final readability and correctness re-reviews are clean after the approved migration-atomicity correction.
- `pnpm verify`: schema drift, all workspace builds/types, formatting/ESLint, **420 unit tests and 7 integration tests passed**; Svelte reports zero errors/warnings.
- Focused cases cover overlapping browser/OS tokens, Edge-family collapse, missing/unsupported input, legacy-row preservation, and failure → atomic rollback → successful migration retry.
- The existing real-worker HTTP/lock/drain story now checks that submitted Firefox/Linux user-agent data produces stored family labels; no additional HTTP story was added.
- Offline pruned production artifact tested outside the repository: the worker resolves inside the deployment, persists `Firefox`/`Linux` labels, and exits naturally on SIGTERM with code 0.
- DB package lint, Changesets status, and Git whitespace checks passed.
Agent-run Docker/Compose verification was not performed; container checks remain owner-only. Remote CI is separate from the passing local checks. Transactional migrations do not claim coordination between independently started processes.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Other. No versions, device/bot detection, or growing parser framework.recordVisittask immediately before the single insert. HTTP capture and the raw queued event shape remain unchanged.0002_visit_classificationfor storedbrowserandoslabels. Existing visits and raw headers are preserved; existing labels start asOther, with no automatic backfill job.NewVisitinput from a preparedClassifiedVisitdatabase insert.Scope
Classification only. No parser dependency, second insert, enrichment queue/marker, service, batching, retention-policy changes, invalid/base-visit collection, dashboard, visit-row UI, or export feature. Those analytics requirements remain separate work.
Classification is intentionally approximate: browsers may conceal their identity, and unsupported cases may fall into another broad family or
Other.Verification
pnpm verify: schema drift, all workspace builds/types, formatting/ESLint, 420 unit tests and 7 integration tests passed; Svelte reports zero errors/warnings.Firefox/Linuxlabels, and exits naturally on SIGTERM with code 0.Agent-run Docker/Compose verification was not performed; container checks remain owner-only. Remote CI is separate from the passing local checks. Transactional migrations do not claim coordination between independently started processes.