feat(bf-5151): propagate canonical identity fields to blob JSON projections

Complete the identity-field surface started by 1446ccf (which added
PersonName/AssignedColor/IdentityResolved to the four primary tracked-blob
structs). Add the same three canonical fields to the two struct types that
serialize tracked blobs for external consumers, and propagate them from the
source structs:

  - api.Track (internal/api/tracks.go)        -> /api/tracks JSON; sourced
                                                 from signal.TrackedBlob in
                                                 listTracks
  - dashboard.blobJSON (internal/dashboard/hub.go) -> /ws/dashboard feed;
                                                 sourced from tracking.Blob in
                                                 BroadcastLocUpdate

/api/blobs needs no change: it serializes signal.TrackedBlob directly
(pm.GetTrackedBlobs), which gained the fields in 1446ccf.

Per the task scope the fields are left at their zero values for existing
blobs (empty strings + nil *bool, all omitempty -> undefined in JS). A
follow-up bead wires the BLE identity sidecar to populate them. The
camelCase keys match the dashboard Blob interface in
dashboard/types/spaxel.d.ts.

The Tier-1 leak-fix targets from bf-1q3m (explainability.BlobSnapshot,
volume.BlobPos) are intentionally untouched: their identity flows via a
separate mechanism (the identityMap BLEMatch side-channel / PersonID), and
adding blob-struct identity fields there is step (a) of a cohesive
add+populate+fold fix that belongs to the follow-up bead (bf-1q3m §6),
not this fields-as-undefined bead.

Verified: gofmt clean, go vet ./... clean, go test ./... passes (api,
dashboard, signal, tracker, tracking, automation re-run with -count=1).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-07-06 17:19:49 -04:00
parent a612584841
commit 01a415dd5c
4 changed files with 27 additions and 5 deletions

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
86b903f090127c4e921e0cd421900f8c616f5818
1446ccf65fe5366db9a1ac0e1adef608548473d5

View file

@ -23,7 +23,15 @@ type Track struct {
PersonColor string `json:"person_color,omitempty"`
IdentityConfidence float64 `json:"identity_confidence,omitempty"`
IdentitySource string `json:"identity_source,omitempty"`
Posture string `json:"posture,omitempty"`
// Canonical identity fields (bf-5151). camelCase JSON keys are the canonical
// API-facing names; the snake_case PersonLabel/PersonColor above are retained
// as deprecated aliases. Propagated from signal.TrackedBlob in listTracks —
// zero today because the source struct is not yet populated (a follow-up bead
// wires the BLE identity sidecar).
PersonName string `json:"personName,omitempty"`
AssignedColor string `json:"assignedColor,omitempty"`
IdentityResolved *bool `json:"identityResolved,omitempty"` // tri-state: nil=unattempted, &true=resolved, &false=failed
Posture string `json:"posture,omitempty"`
}
// TrackedBlob is an alias for signal.TrackedBlob.
@ -113,6 +121,9 @@ func (h *TracksHandler) listTracks(w http.ResponseWriter, r *http.Request) {
PersonColor: b.PersonColor,
IdentityConfidence: b.IdentityConfidence,
IdentitySource: b.IdentitySource,
PersonName: b.PersonName,
AssignedColor: b.AssignedColor,
IdentityResolved: b.IdentityResolved,
Posture: b.Posture,
}
}

View file

@ -550,7 +550,15 @@ type blobJSON struct {
PersonColor string `json:"person_color,omitempty"`
IdentityConfidence float64 `json:"identity_confidence,omitempty"`
IdentitySource string `json:"identity_source,omitempty"`
Replay bool `json:"replay"` // true when blob is from time-travel replay
// Canonical identity fields (bf-5151). camelCase JSON keys are the canonical
// dashboard-facing names; the snake_case PersonLabel/PersonColor above are
// retained as deprecated aliases. Propagated from tracking.Blob in
// BroadcastLocUpdate below — zero today because the source struct is not yet
// populated (a follow-up bead wires the BLE identity sidecar).
PersonName string `json:"personName,omitempty"`
AssignedColor string `json:"assignedColor,omitempty"`
IdentityResolved *bool `json:"identityResolved,omitempty"` // tri-state: nil=unattempted, &true=resolved, &false=failed
Replay bool `json:"replay"` // true when blob is from time-travel replay
}
// BroadcastLocUpdate sends localisation results to all dashboard clients.
@ -576,6 +584,9 @@ func (h *Hub) BroadcastLocUpdate(blobs []tracking.Blob) {
PersonColor: b.PersonColor,
IdentityConfidence: b.IdentityConfidence,
IdentitySource: b.IdentitySource,
PersonName: b.PersonName,
AssignedColor: b.AssignedColor,
IdentityResolved: b.IdentityResolved,
}
}