feat(telemetry): complete bb-buff.1.3 - Backend Liveness Refactor

STORY:
The session backend needed to aggregate agent health from a live
telemetry stream rather than static bead metadata. This refactor
makes liveness signals real-time and accurate.

COLLABORATION:
We extended the ActivityEvent model with a native 'heartbeat' kind,
updated extendActivityLease() to emit through the activity bus, and
refactored getAgentLivenessMap() to prioritize heartbeat activity
history over stale bead metadata.

DELIVERABLES:
- ActivityEvent extended with 'heartbeat' kind
- extendActivityLease() emits heartbeats through activity bus
- getAgentLivenessMap() prefers telemetry over static metadata
- Registry APIs support projectRoot injection for testing
- Tests verify preference logic via TDD

VERIFICATION:
- 93/93 tests PASSING
- Heartbeat override verified in isolated temp projects

CLOSES: bb-buff.1.3
BLOCKS: bb-buff.3.2, bb-buff.3.3, bb-buff.2.1
This commit is contained in:
zenchantlive 2026-02-15 21:14:05 -08:00
parent 0016b57e37
commit 4ee550c333
36 changed files with 1380 additions and 541 deletions

View file

@ -14,6 +14,7 @@ export interface ReadIssuesOptions {
projectSource?: ProjectSource;
projectAddedAt?: string | null;
preferBd?: boolean;
skipAgentFilter?: boolean;
}
export function resolveIssuesJsonlPathCandidates(projectRoot: string = process.cwd()): string[] {
@ -108,8 +109,8 @@ async function readIssuesViaBd(options: ReadIssuesOptions, project: ReturnType<t
.filter((issue) => {
// Exclude tombstones
if (issue.status === 'tombstone' && !options.includeTombstones) return false;
// Exclude agent identities from mission lists
if (issue.labels.includes('gt:agent')) return false;
// Exclude agent identities from mission lists unless skipping filter (for watcher/diffing)
if (issue.labels.includes('gt:agent') && !options.skipAgentFilter) return false;
return true;
})
.map((issue) => ({
@ -141,6 +142,7 @@ export async function readIssuesFromDisk(options: ReadIssuesOptions = {}): Promi
const jsonl = await readTextFileWithRetry(issuesPath);
return parseIssuesJsonl(jsonl, {
includeTombstones: options.includeTombstones ?? false,
skipAgentFilter: options.skipAgentFilter ?? false,
}).map((issue) => ({
...issue,
project,