spaxel/notes/bf-55rp-primary-types.md
jedarden d99f42d3bf docs(bf-55rp): re-verify primary tracked-blob Go types & construction sites
Inventory of the 5 primary tracked-blob Go types + the api.TrackedBlob
alias, re-verified against HEAD c4d42e8. Corrects two stale line numbers
(automation.TrackedBlob :2213->:2303, signal.TrackedBlob :5384->:5494)
and one stale field list (tracking.Blob 2D no longer carries PersonName/
AssignedColor/IdentityResolved — identity fields now match tracker.Blob).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-06 14:56:35 -04:00

14 KiB
Raw Blame History

Primary Tracked-Blob Go Types — Inventory & Construction Sites (bf-55rp)

Scope: the FIVE primary tracked-blob Go types that make up the tracked-blob lifecycle, plus the api.TrackedBlob alias. Re-verification of notes/bf-3ldj-findings.md (§13) and notes/bf-4bhd.md (Blob Type Definitions + Patterns 12) against current HEAD.

Verified against: c4d42e8 (c4d42e8 test(bf-4b1c): differential lock-in for geometry placement and fusion peaks) on 2026-07-06.

This bead is documentation only — no Go source was modified.


0. TL;DR

Question Answer
How many primary tracked-blob Go types? 5 + the api.TrackedBlob alias
How many DIRECT struct-literal construction sites (production)? 5 — exactly one per primary type
Did anything move since bf-3ldj / bf-4bhd? Yes — 2 sites. automation.TrackedBlob :2213 → :2303; signal.TrackedBlob (sigproc) :5384 → :5494
Did any type definition move? No — all 6 definitions (5 structs + 1 alias) are at their previously reported file:line
Did any field list drift? Yes — tracking.Blob (2D). The 3 identity fields PersonName, AssignedColor, IdentityResolved have been removed; the 2D type's identity field set is now identical to the 3D tracker.Blob. See §3.1.
Is the api.TrackedBlob alias still pure? Yestype TrackedBlob = signal.TrackedBlob (no separate struct)

1. The six definitions (file:line, verified against HEAD)

# Type Package Definition site Kind
1 tracking.Blob (2D, legacy floor tracker) mothership/internal/tracking internal/tracking/tracker.go:21 struct
2 tracker.Blob (3D, identity-bearing) mothership/internal/tracker internal/tracker/tracker.go:36 struct
3 fusion.Blob (peak) mothership/internal/fusion internal/fusion/fusion.go:36 struct
4 automation.TrackedBlob mothership/internal/automation internal/automation/engine.go:1337 struct
5 signal.TrackedBlob mothership/internal/signal internal/signal/processor.go:587 struct
6 api.TrackedBlob (alias of #5) mothership/internal/api internal/api/tracks.go:30 type alias

All six file:line entries match bf-4bhd.md / bf-3ldj-findings.md exactly — none of the definitions moved.

1.1 The api.TrackedBlob alias relationship

// internal/api/tracks.go:29-30
// TrackedBlob is an alias for signal.TrackedBlob.
type TrackedBlob = signal.TrackedBlob

This is a pure Go type alias (=, not a named type wrapping the struct). Consequences:

  • api.TrackedBlob and signal.TrackedBlob are the same type — no conversion needed.
  • Any field added/removed on signal.TrackedBlob (processor.go:587) automatically surfaces under the api.TrackedBlob name. No separate edit at tracks.go:30.
  • The TracksProvider interface (tracks.go:33) returns []signal.TrackedBlob, which is interchangeable with []api.TrackedBlob.

2. Every DIRECT struct-literal construction site (verified against HEAD)

"Direct struct-literal" = a &Blob{} / Blob{} / TrackedBlob{} literal that brings a new value of one of the five types into existence. Snapshot deep-copies (out[i] = *b, make+copy) are dereferences / slice copies, not literals — out of scope here and correctly excluded from bf-4bhd's Pattern 3 as separate operations.

A whole-repo sweep (grep -rn "Blob{\|TrackedBlob{" mothership/, test files excluded, projection-type literals filtered out) returns exactly five production construction sites — one per primary type:

# Type built Site (HEAD) Site in prior reports Pattern Built from
C1 fusion.Blob internal/fusion/fusion.go:260 :260 Blob{} value (single line) fusion grid peak
A2 tracking.Blob (2D) internal/tracking/tracker.go:160 :160 &Blob{} pointer unmatched 2D measurement
A1 tracker.Blob (3D) internal/tracker/tracker.go:162 :162 &Blob{} pointer unmatched 3D measurement
E2 automation.TrackedBlob cmd/mothership/main.go:2303 :2213 → moved +90 TrackedBlob{} value tracked blob (conversion)
E1 signal.TrackedBlob (sigproc) cmd/mothership/main.go:5494 :5384 → moved +110 TrackedBlob{} value fusion.Blob peak (conversion)

No other production direct-literal sites exist for any of the five types. The two main.go sites are the only ones that moved; the two tracker sites and the fusion site are unchanged.

2.1 Literal text (verified, verbatim)

C1 — fusion.Blob peak emission (fusion/fusion.go:260), inside (*Engine).Fuse:

blobs[i] = Blob{X: p[0], Y: p[1], Z: p[2], Confidence: p[3]}

Value-type per grid peak. No identity — peaks are pre-identity. This is the raw measurement that originates every blob downstream.

A2 — tracking.Blob (2D) track spawn (tracking/tracker.go:160), inside (*Tracker).Update:

b := &Blob{
    ID:       t.nextID,
    X:        meas[0],
    Z:        meas[1],
    Weight:   meas[2],
    LastSeen: now,
    Trail:    [][2]float64{{meas[0], meas[1]}},
    ukf:      NewUKF(meas[0], meas[1]),
}

Identity fields exist on the struct (see §3.1) but are not set in the literal — left at Go zero values, populated later by the BLE-matching layer.

A1 — tracker.Blob (3D) track spawn (tracker/tracker.go:162), inside (*Tracker).Update:

b := &Blob{
    ID: t.nextID,
    X:  m[0], Y: m[1], Z: m[2],
    Weight:   m[3],
    LastSeen: now,
    Trail:    [][3]float64{{m[0], m[1], m[2]}},
    Posture:  PostureUnknown,
    ukf:      NewUKF(m[0], m[1], m[2]),
}

Identity fields exist on the struct but are not set in the literal — populated later by applyIdentity (tracker/identity.go, the 3D identity-matching layer).

E2 — automation.TrackedBlob conversion (cmd/mothership/main.go:2303), in the live 10 Hz loop, loop over tracked blobs:

autoBlobs[i] = automation.TrackedBlob{
    ID:         b.ID,
    X:          b.X,
    Y:          b.Y,
    Z:          b.Z,
    VX:         b.VX,
    VY:         b.VY,
    VZ:         b.VZ,
    Confidence: b.Weight,
}

The source blob has identity, but automation.TrackedBlob (see §3.4) has no identity fields — identity is dropped at this boundary. (This is the long-standing "automation leak" flagged in bf-3ldj §5 Tier 1; re-confirmed unchanged at HEAD.)

E1 — signal.TrackedBlob (sigproc) conversion (cmd/mothership/main.go:5494), inside (*blobTracker).track, built from a fusion.Blob peak pk:

b := sigproc.TrackedBlob{
    ID:     id,
    X:      pk.X,
    Y:      pk.Y,
    Z:      pk.Z,
    Weight: pk.Confidence,
}
// velocity filled in afterward from bt.prev[id]:
//   b.VX = (pk.X - pb.X) / dt ; b.VY = ... ; b.VZ = ...

Built from a pre-identity peak, so the signal.TrackedBlob identity fields (which do exist — see §3.5) are left zero here by design.


3. Field lists (verified against HEAD) + the one drift

3.1 tracking.Blob (2D) — field list DRIFTED (correction to bf-4bhd.md)

internal/tracking/tracker.go:21. Current struct:

type Blob struct {
    ID       int
    X        float64
    Z        float64
    VX       float64
    VZ       float64
    Weight   float64
    LastSeen time.Time
    Trail    [][2]float64
    ukf      *UKF

    // Identity fields (populated by BLE-to-blob matching)
    PersonID           string    `json:"person_id,omitempty"`
    PersonLabel        string    `json:"person_label,omitempty"`
    PersonColor        string    `json:"person_color,omitempty"`
    IdentityConfidence float64   `json:"identity_confidence,omitempty"`
    IdentitySource     string    `json:"identity_source,omitempty"`
    IdentityLastSeen   time.Time `json:"-"`
    Posture            Posture   `json:"posture,omitempty"`
}

⚠️ Correction to notes/bf-4bhd.md §"1. tracking.Blob". The prior report listed PersonName, AssignedColor, IdentityResolved as fields of this struct, and the literal as initializing PersonName:"", AssignedColor:"", IdentityResolved:false. bf-3ldj §1.2 already half-corrected this ("the literal sets none of them"). As of HEAD, those three fields no longer exist on the struct at all — they have been removed. The 2D type's identity field set is now identical to the 3D tracker.Blob: PersonID / PersonLabel / PersonColor / IdentityConfidence / IdentitySource / IdentityLastSeen. The 2D-vs-3D identity-field divergence noted in bf-4bhd's "Key Findings" has been closed.

3.2 tracker.Blob (3D, identity-bearing) — unchanged

internal/tracker/tracker.go:36. Current struct (fields match bf-4bhd; json tags added since, immaterial to the field set):

type Blob struct {
    ID         int
    X, Y, Z    float64
    VX, VY, VZ float64
    Weight     float64
    Posture    Posture
    LastSeen   time.Time
    Trail      [][3]float64

    // Identity fields
    PersonID           string    `json:"person_id,omitempty"`
    PersonLabel        string    `json:"person_label,omitempty"`
    PersonColor        string    `json:"person_color,omitempty"`
    IdentityConfidence float64   `json:"identity_confidence,omitempty"`
    IdentitySource     string    `json:"identity_source,omitempty"`
    IdentityLastSeen   time.Time `json:"-"`

    ukf *UKF // internal — nil in copies returned to callers
}

Identity-matching lives in tracker/identity.go (applyIdentity / clearIdentity), operating on this 3D type only.

3.3 fusion.Blob (peak) — unchanged

internal/fusion/fusion.go:36:

type Blob struct {
    X, Y, Z    float64 // world-space position (metres)
    Confidence float64 // normalised [0..1]
}

No identity, no velocity, no ID — the pre-identity raw peak.

3.4 automation.TrackedBlob — unchanged

internal/automation/engine.go:1337:

type TrackedBlob struct {
    ID         int
    X, Y, Z    float64
    VX, VY, VZ float64
    Confidence float64
}

No identity fields. This is why the E2 conversion (main.go:2303) drops identity.

3.5 signal.TrackedBlob — unchanged

internal/signal/processor.go:587:

type TrackedBlob struct {
    ID         int
    X, Y, Z    float64
    VX, VY, VZ float64
    Weight     float64
    // Identity fields
    PersonID           string  `json:"person_id,omitempty"`
    PersonLabel        string  `json:"person_label,omitempty"`
    PersonColor        string  `json:"person_color,omitempty"`
    IdentityConfidence float64 `json:"identity_confidence,omitempty"`
    IdentitySource     string  `json:"identity_source,omitempty"`
    Posture            string  `json:"posture,omitempty"`
}

Identity fields present (so the type can carry identity), but the E1 conversion builds from a pre-identity peak and leaves them zero. api.TrackedBlob (tracks.go:30) is this exact type under an alias.


4. Re-location methodology (acceptance criterion)

Moved sites were re-located, exactly as the task prescribes, with:

grep -rn "Blob{$\|&Blob{$\|TrackedBlob{$" mothership/

Result (test files included this time, for completeness):

mothership/internal/tracker/tracker.go:162:    b := &Blob{          # A1 (3D, prod)
mothership/cmd/mothership/main.go:2303:       autoBlobs[i] = automation.TrackedBlob{   # E2 (prod, MOVED)
mothership/cmd/mothership/main.go:5494:       b := sigproc.TrackedBlob{                # E1 (prod, MOVED)
mothership/internal/api/tracks_test.go:49:    blobs := []TrackedBlob{                  # TEST (out of scope)
mothership/internal/tracking/tracker.go:160:  b := &Blob{          # A2 (2D, prod)

The end-of-line $ anchor misses the single-line fusion literal, so a second pass with the broader grep -rn "Blob{\|TrackedBlob{" mothership/ (filtering projection types and tests) catches it:

mothership/internal/fusion/fusion.go:260: blobs[i] = Blob{X: p[0], Y: p[1], Z: p[2], Confidence: p[3]}   # C1 (prod)

Re-run these two greps before editing any of these sitesmain.go is a large, frequently-edited file and the two conversion lines drift by tens of lines between beads.


5. Acceptance criteria status

  • Each of the 5 primary types has its struct definition recorded with file:line, verified against current HEAD — §1 table + §3 verbatim structs.
  • Every direct struct-literal construction site listed (file:line + pattern: &Blob{} / Blob{} / TrackedBlob{}) — §2 table + §2.1 verbatim literals; whole-repo sweep confirms exactly five production sites, no others.
  • The api.TrackedBlob alias relationship to signal.TrackedBlob is noted — §1.1.
  • Re-locate moved sites with grep -rn "Blob{$\|&Blob{$\|TrackedBlob{$" mothership/ — §4; two moved sites identified and corrected (:2213→:2303, :5384→:5494).
  • Findings written to notes/bf-55rp-primary-types.md — this file.

6. Net changes since the source reports (the delta this re-verification surfaces)

  1. Two construction sites moved (no semantic change):
    • automation.TrackedBlob: main.go:2213 → :2303
    • signal.TrackedBlob (sigproc): main.go:5384 → :5494
  2. One field-list drift in tracking.Blob (2D): PersonName, AssignedColor, IdentityResolved removed; identity field set now matches tracker.Blob (3D) exactly.
  3. All six type definitions (5 structs + 1 alias) are at their previously reported file:line; the alias is still a pure = alias.
  4. The automation identity leak (E2 drops identity because automation.TrackedBlob has no identity fields) and the peak-origin zero-identity of E1 are both unchanged and remain the open work items tracked in bf-3ldj §5.

7. Provenance

Source report Used for Status at HEAD
notes/bf-3ldj-findings.md §13 Type defs, primary literals, identity-flow context §1 defs valid; one literal line number stale (E2); field-set note for 2D now fully resolved
notes/bf-4bhd.md (Blob Type Definitions + Patterns 12) Type defs, &Blob{}/Blob{} patterns, alias note §1 defs valid; 2D field list stale (§3.1); E2/E1 line numbers stale
This report (bf-55rp) Re-verification + corrections + alias documentation Verified against c4d42e8, 2026-07-06