## 1. Schema & Queries

- [x] 1.1 `db/schema.sql`: idempotent ALTERs `tags.sms_allocated/used/period_start`; `url_shortened` table; `make db-up`
- [x] 1.2 `queries.sql`: `InsertShortCode`, `GetShortCode` (:one), `GetScanByID` (:one), `AddSmsUsed` (:exec, `sms_used = sms_used + 1`); `make generate`; build

## 2. Metering

- [x] 2.1 `scan.go` shouldAlert: credit check (`SmsAllocated > 0 && SmsUsed >= SmsAllocated` → false); after successful alert send → `AddSmsUsed`
- [x] 2.2 `FinderContact`: same credit check + increment on successful send

## 3. Location Link

- [x] 3.1 Short-code generator + `InsertShortCode` at alert time (when location present); `alertMessage` uses `where-woof.com/s/{code}`
- [x] 3.2 `ServeShort` handler (`GET /s/{code}` → scan → location page); `location.html` template (map iframe + finder phone + tag link + time); register route + template set

## 4. Admin

- [x] 4.1 TagResource: `sms_allocated` (numeric, editable), `sms_used` (view), `sms_period_start` (view)

## 5. Verification

- [x] 5.1 New suite: metered tag with credits → alert + credit consumed; exhausted → no alert (scan recorded); unmetered (0) → alerts; alert SMS contains `where-woof.com/s/`; short link → location page 200 with map + finder details; unknown code → not found
- [x] 5.2 Regressions (22+19+12+6) green; deploy frontend + admin; live check
- [x] 5.3 `openspec validate sms-metering-location-link`; commit
