Files
pulse/internal
rcourtman 796a70999b Stop tenant incident stores racing on shared ID counters (#2711)
Each tenant monitor owns its own `IncidentStore`, but incident and
incident-event IDs came from package-level `int64` counters bumped with
`++` under nothing but the calling store's mutex. Two tenants recording
alerts at once raced on them. `go test -race` failed
`TestLeavingMockModeClearsEveryRunningTenantMonitor` on #2698
([run](https://github.com/rcourtman/Pulse/actions/runs/37652317098/job/112919080928))
at `incidents.go:1753`. The remediation record counter in
`remediation.go` had the same shape, and `startPatrolForContext` creates
one remediation log per org.

**Fix**
- All six `internal/ai/memory` ID generators (incident, incident event,
remediation record, memory, incident memory, pattern memory) now mint
through one atomic helper, `nextSequencedID` in `ids.go`.
- The numeric suffix no longer wraps at 1000. Even with atomic counters,
a generator that minted more than a thousand IDs in one wall-clock
second repeated an ID. That matters because `RecordNote` finds an
incident by ID and the incident query deduplicates merged events by ID.
The `<prefix><YYYYMMDDhhmmss>-<n>` layout is unchanged. Only `n` can now
exceed three digits.
- `intToString` lost its last runtime callers and is removed.

**Proof**
- `internal/ai/memory/ids_test.go` mints 2,400 IDs from every generator
on 16 goroutines and records alerts into two tenant stores concurrently.
- Against the `origin/main` runtime files (via `-overlay`), `-race`
reports DATA RACE at `incidents.go:1753/1760` and `remediation.go:355`,
and every generator fails the uniqueness/shape check: four produce
duplicate IDs, and `mem`/`pat-mem` produce a wrapped `-0` suffix.
- With the fix, `go test -race ./internal/ai/memory/` passes, and `go
test -race -count=5 -run
TestLeavingMockModeClearsEveryRunningTenantMonitor
./internal/monitoring/` passes.

**Residual:** across restarts, only the timestamp separates IDs. If the
local clock repeats a second (DST fall-back or a backward step), a
restarted sequence could still meet an earlier suffix. Persisted IDs and
the stored format are unchanged. The `ai-runtime` contract records this.
2026-10-07 23:26:15 +01:00
..