Skip to content

Tracker: ApplyTrackerFacts reducer scaffolding + shared observer skeleton extraction #112

@harshitsinghbhandari

Description

@harshitsinghbhandari

Tracker v1 (ports.Tracker + backend/internal/adapters/tracker/github) has been on a shelf since PR #36 — zero callers in backend/internal/{daemon,service,session_manager,lifecycle,cdc,cli} (grep confirms). #35 tracks the full observer loop. Before that observer can be built, two pieces of scaffolding need to land so the work can be done in small reviewable chunks instead of one large PR.

Part 1 — ports.TrackerObservation DTO + lifecycle.Manager.ApplyTrackerFacts

Mirror the SCM lane shape:

  • ports.TrackerObservation carrying Fetched bool, ObservedAt time.Time, Provider/Host/Repo, the normalized issue facts (state, assignee, title, comments), and a Changed TrackerChanged{State, Assignee, Comments} discriminator. See backend/internal/ports/scm_observations.go:54-89 as the template.
  • lifecycle.Manager.ApplyTrackerFacts(ctx, sessionID, ports.TrackerObservation) error, mirroring ApplySCMObservation at backend/internal/lifecycle/reactions.go:77-82. Initial reactions: issue closed externally → MarkTerminated; assignee changed away from AO → log only (no reaction yet); new bot mention in a comment → nudge once.
  • Unit tests in backend/internal/lifecycle/manager_test.go covering each reaction branch.

Landing this scaffolding alone, with no observer wired, is intentional: it locks the contract so adapter authors (and #35's observer) have something concrete to satisfy.

Part 2 — Extract shared observer skeleton

Today backend/internal/observe/scm/observer.go carries ~1185 LOC and several pieces are observer-pattern-general, not SCM-specific:

  • The Start(ctx) <-chan struct{} + loop (lines 161-184) — immediate first poll inside the goroutine, ticker, ctx-done exit.
  • The optional credentialChecker interface + checkCredentials (lines 60-62, 400-424) — lazy first-poll credential gate with disabled flag.
  • The bounded ObserverCache shape + FIFO eviction helpers (lines 78-113, 1133-1186) — cacheSetString, cacheSetTime, cacheSetBool, evictStrings. Three near-identical helpers; a generic cacheSet[V any] collapses them.

Extracting these into backend/internal/observe/observer.go (or backend/internal/observe/runtime/) lets the tracker observer reuse the same shape. Without this, #35 will fork a near-copy of observe/scm/observer.go and the two will drift on every fix.

Acceptance

  • ports.TrackerObservation + TrackerChanged exist with field-level documentation matching the SCM DTO style.
  • lifecycle.Manager.ApplyTrackerFacts exists as a working reducer with the three initial reactions covered by unit tests.
  • Shared observer helpers (goroutine supervisor, credential gate, bounded FIFO cache) live in a shared package and the SCM observer is migrated to consume them. SCM behavior unchanged — its existing 21-test suite must stay green.
  • go build ./... && go test -race ./... clean.

Why scope this together

The DTO + reducer is small but useless without the observer; the skeleton extraction is mechanical but only worth doing once we have a second consumer (Tracker) on the horizon. Doing both in one PR keeps the diff focused on "make Tracker observer cheap to build."

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    coreCore Functionalitylcm-smLifecycle + Session Manager lane

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions