Skip to content

Implement PRD-001/002: service registration, telemetry ingestion, SSE stream - #6

Merged
thenotoriousllama merged 3 commits into
mainfrom
feature/portal-realignment-impl
Jul 2, 2026
Merged

Implement PRD-001/002: service registration, telemetry ingestion, SSE stream#6
thenotoriousllama merged 3 commits into
mainfrom
feature/portal-realignment-impl

Conversation

@thenotoriousllama

@thenotoriousllama thenotoriousllama commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements hivedoctor as the single source of truth for hive health + telemetry (ADR-0001/0002), part of the portal + telemetry realignment initiative driven by /the-smoker against library/initiatives/portal-and-telemetry-realignment.md (the-apiary superproject).

  • PRD-001: extended registry schema (telemetryDbPath, additive, backward-compatible), the runtime SQLite status/metrics/log contract, and the ~1s read-only poll-and-merge loop with per-service fault isolation and registry-reload triggers.
  • PRD-002: the single GET /events SSE stream (fleet-telemetry) on the existing loopback status page, the schema-tolerant metrics reader (works across honeycomb's 3-counter and hivenectar's 5-counter tables), and windowed/bounded reads.

Security

security-worker-bee found one medium finding: an unconstrained telemetryDbPath let a poisoned registry entry point hivedoctor at an arbitrary user-readable SQLite file and broadcast its contents over the unauthenticated loopback SSE stream. Remediated (ad2174a): containment under ~/.honeycomb/telemetry/ via the existing assertWithinBase helper, falling back to health-probe-only on escape. 3 new containment tests added.

Quality

quality-worker-bee independently re-ran typecheck/tests and re-verified the security fix by hand-tracing the containment logic. Verdict: PASS. All ACs implemented except one deliberately out-of-scope item (PRD-001a AC-3, plural DB paths, excluded by the pinned cross-repo contract so 3 sibling repos could build in parallel against one literal shape).

Test plan

  • npm run typecheck clean
  • npm test: 566/569 passing (3 pre-existing, unrelated failures, confirmed via parent-commit checkout+rerun, not a regression)
  • Zero new runtime dependencies (node:sqlite, node:http built-ins only)

Full acceptance-criteria ledger: library/ledger/EXECUTION_LEDGER.md in the-apiary superproject (companion PRs open there and in honeycomb, hivenectar, the-hive).

Summary by CodeRabbit

  • New Features
    • Added a live fleet telemetry events stream at GET /events using Server-Sent Events (fleet-telemetry).
    • Services can now optionally report SQLite-backed telemetry (health, metrics, and recent logs) when available, while health-probe-only mode still works when it isn’t.
  • Bug Fixes
    • Improved fail-soft polling and SSE behavior so telemetry/probe errors or client disconnects don’t interrupt updates.
    • Hardened start/stop shutdown flow to prevent resource leaks and ensure clean teardown.

…ream

Extends the static registry with an optional telemetryDbPath (PRD-001a), adds
a read-only SQLite reader over the service_status/service_metrics/service_logs
contract (PRD-001b reader side), a ~1s poll-and-merge loop that probes /health
and merges it with each service's telemetry into an in-memory fleet model with
per-service fault isolation (PRD-001c), and a single fleet-telemetry SSE stream
at GET /events mounted onto the existing status page (PRD-002/002a). The
poll loop's schema-tolerant metrics reader and windowed log reads keep memory
bounded per PRD-002c. Zero new dependencies: node:sqlite and node:http only.
Security review finding (medium): an unconstrained telemetryDbPath in
the registry file let hivedoctor open ANY user-readable SQLite file
and, if it had Contract-B-shaped tables, broadcast its contents on the
unauthenticated loopback SSE stream (/events). coerceTelemetryDbPath
now enforces containment under the trusted ~/.honeycomb/telemetry/
root via the existing assertWithinBase helper (the same defense-in-
depth pattern healthUrl's loopback restriction already uses); a path
that escapes falls back to undefined (health-probe-only), never a
crash, mirroring coerceHealthUrl's fallback-on-invalid-input posture.

Updated the test that previously asserted the vulnerable behavior
(an arbitrary absolute path 'preserved verbatim') to instead assert
containment, and added 3 new tests: an out-of-root path rejected, a
'..' traversal rejected, and a legitimate nested subdirectory path
preserved. 566/569 tests pass (3 pre-existing, unrelated failures,
confirmed earlier this session); typecheck clean.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d2c17fec-8ba7-43a3-ab2e-55c07a125969

📥 Commits

Reviewing files that changed from the base of the PR and between ad2174a and 4eca002.

📒 Files selected for processing (7)
  • src/compose/index.ts
  • src/ingestion/poll-loop.ts
  • src/registry.ts
  • src/status-page/server.ts
  • tests/compose/telemetry-wiring.test.ts
  • tests/ingestion/poll-loop.test.ts
  • tests/registry.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/status-page/server.ts
  • tests/compose/telemetry-wiring.test.ts
  • tests/registry.test.ts
  • src/ingestion/poll-loop.ts
  • src/registry.ts
  • tests/ingestion/poll-loop.test.ts
  • src/compose/index.ts

📝 Walkthrough

Walkthrough

This PR adds a telemetry ingestion path from per-daemon SQLite files into an SSE /events stream, with registry support for telemetry DB paths, status-page routing, composition-root wiring, and tests.

Changes

Telemetry Ingestion and SSE Feature

Layer / File(s) Summary
Registry telemetryDbPath field
src/registry.ts, tests/registry.test.ts
Adds optional telemetryDbPath to DaemonEntry, with path coercion/validation and coverage for accepted and rejected inputs.
Telemetry schema and SQLite reader
src/telemetry/schema.ts, src/telemetry/sqlite-reader.ts, tests/ingestion/poll-loop.test.ts
Defines shared telemetry types and a read-only SQLite reader for status, metrics, and bounded logs, with reader-side tests.
Poll-and-merge loop
src/ingestion/poll-loop.ts, tests/ingestion/poll-loop.test.ts
Implements the polling loop that probes health, merges telemetry into fleet snapshots, tracks subscribers, and handles reload/close behavior.
SSE /events handler
src/ingestion/sse.ts, tests/ingestion/sse.test.ts
Adds the SSE handler that emits fleet-telemetry snapshots and cleans up on disconnect, backpressure, or write failures.
Status page /events routing
src/status-page/server.ts
Adds optional onEvents routing so GET /events can be served when wired.
Composition root wiring and lifecycle
src/compose/index.ts, tests/compose/telemetry-wiring.test.ts
Constructs and exposes the telemetry loop, wires SSE into the status page, and starts/stops/closes it with the main lifecycle.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant StatusPageServer
  participant telemetryPollLoop
  participant TelemetryDbReader

  Client->>StatusPageServer: GET /events
  StatusPageServer->>telemetryPollLoop: onSnapshot subscribe
  telemetryPollLoop->>TelemetryDbReader: readStatus/readMetrics/readNewLogs
  TelemetryDbReader-->>telemetryPollLoop: telemetry rows
  telemetryPollLoop-->>StatusPageServer: FleetTelemetryEvent
  StatusPageServer-->>Client: SSE fleet-telemetry frame
Loading

Possibly related issues

Possibly related PRs

  • legioncodeinc/hivedoctor#2 — Shares the same registry/compose/status-page wiring surface that this telemetry SSE path builds on.

Poem

A bunny hops through telemetry glow,
Through status pages, tick by tick,
Logs and health now flow,
In one soft stream, quick and slick. 🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: registry-based service registration, telemetry ingestion, and the new SSE stream.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/portal-realignment-impl

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/compose/index.ts`:
- Around line 707-715: The telemetry polling setup in telemetryPollLoop is
bypassing the injected CreateHiveDoctorOptions.probe by hard-coding probeHealth
inside createPollLoop. Update the probe argument in this createPollLoop call to
use the provided injected probe for telemetry entries as well, matching the
existing seam used elsewhere. Keep the wiring consistent so telemetry health
checks respect the caller-supplied probe instead of always calling probeHealth.

In `@src/ingestion/poll-loop.ts`:
- Around line 188-195: The poll loop currently trusts any DB under the root and
attributes its data to entry.name without verifying the DB’s own
ServiceStatusRow.name. In src/ingestion/poll-loop.ts inside the loop that calls
readStatus, readMetrics, and readNewLogs, compare the returned status name
against the registry entry name and treat any mismatch as malformed. If the
names do not match, skip caching and forwarding all rows for that DB so
mispointed telemetryDbPath values cannot cross-wire services.
- Around line 297-305: The reload logic in reload() only removes runtimes for
deleted names, so a same-name entry that changes telemetryDbPath keeps reusing
the old cached runtime and its lastLogId. Update the runtime cache handling in
src/ingestion/poll-loop.ts so reload() also evicts or resets the matching entry
when its telemetryDbPath changes or disappears, forcing the next run to reopen
the DB instead of reusing the stale one. Use the existing reload(), runtime map,
closeHandle(), and the daemon entry’s telemetryDbPath to locate and invalidate
the cached runtime.

In `@src/registry.ts`:
- Around line 204-210: The path guard in registry.ts validates resolve(expanded)
but returns expanded, so a relative telemetryDbPath can later be reopened under
a different cwd in poll-loop.ts. Update the logic in the path
expansion/validation flow around assertWithinBase so the exact returned path is
the same path that was validated, and reject any non-absolute post-~ input
before returning it.

In `@src/status-page/server.ts`:
- Around line 257-265: The 404 fallback in the status-page server no longer
matches the public routes because `/events` is missing from the discovered route
list. Update the 404 response construction in `server.ts` so the route metadata
includes `/events` whenever `onEvents` is wired, keeping the fallback payload in
sync with the `onEvents` handler and the existing `"/"` and `"/status.json"`
entries.

In `@tests/compose/telemetry-wiring.test.ts`:
- Around line 125-137: The telemetry wiring test starts the status page directly
via doctor.statusPage.start() but only calls doctor.stop(), which does not shut
it down unless doctor.start() was used. Update this test to explicitly stop the
status page by calling the status page shutdown/stop method on the
doctor.statusPage instance after the fetch assertion, so the HTTP server is
always cleaned up and doesn’t leak into later tests.
- Around line 159-176: Move the explicit doctor.telemetryPollLoop.close()
cleanup into a finally block around the tick/assertion sequence so it runs even
when expect(event.services) fails; keep the same telemetryPollLoop and
doctor.stop() cleanup path, but ensure the SQLite-backed handle is always closed
before tmp dir teardown to avoid the Windows EBUSY race.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8338780c-648b-43a3-b47c-18d99f4ec424

📥 Commits

Reviewing files that changed from the base of the PR and between 18a8224 and ad2174a.

📒 Files selected for processing (11)
  • src/compose/index.ts
  • src/ingestion/poll-loop.ts
  • src/ingestion/sse.ts
  • src/registry.ts
  • src/status-page/server.ts
  • src/telemetry/schema.ts
  • src/telemetry/sqlite-reader.ts
  • tests/compose/telemetry-wiring.test.ts
  • tests/ingestion/poll-loop.test.ts
  • tests/ingestion/sse.test.ts
  • tests/registry.test.ts

Comment thread src/compose/index.ts
Comment thread src/ingestion/poll-loop.ts
Comment thread src/ingestion/poll-loop.ts
Comment thread src/registry.ts Outdated
Comment thread src/status-page/server.ts
Comment on lines +125 to +137
it("wires /events onto the existing status page and it emits a well-formed fleet-telemetry frame", async () => {
const doctor = buildDoctor();
doctor.statusPage.start();
const port = await waitForPort(doctor);

const { statusCode, body } = await fetchEventsFirstFrame(port);
expect(statusCode).toBe(200);
expect(body).toContain("event: fleet-telemetry");
expect(body).toContain('"services"');
expect(body).toContain('"logs"');

await doctor.stop();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Stop the status page explicitly in this test.

Line 127 starts doctor.statusPage directly, but Line 136 calls doctor.stop(), which is a no-op unless doctor.start() ran. This leaves the HTTP server open across tests.

Suggested fix
 	it("wires /events onto the existing status page and it emits a well-formed fleet-telemetry frame", async () => {
 		const doctor = buildDoctor();
 		doctor.statusPage.start();
-		const port = await waitForPort(doctor);
-
-		const { statusCode, body } = await fetchEventsFirstFrame(port);
-		expect(statusCode).toBe(200);
-		expect(body).toContain("event: fleet-telemetry");
-		expect(body).toContain('"services"');
-		expect(body).toContain('"logs"');
-
-		await doctor.stop();
+		try {
+			const port = await waitForPort(doctor);
+			const { statusCode, body } = await fetchEventsFirstFrame(port);
+			expect(statusCode).toBe(200);
+			expect(body).toContain("event: fleet-telemetry");
+			expect(body).toContain('"services"');
+			expect(body).toContain('"logs"');
+		} finally {
+			doctor.statusPage.stop();
+		}
 	});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("wires /events onto the existing status page and it emits a well-formed fleet-telemetry frame", async () => {
const doctor = buildDoctor();
doctor.statusPage.start();
const port = await waitForPort(doctor);
const { statusCode, body } = await fetchEventsFirstFrame(port);
expect(statusCode).toBe(200);
expect(body).toContain("event: fleet-telemetry");
expect(body).toContain('"services"');
expect(body).toContain('"logs"');
await doctor.stop();
});
it("wires /events onto the existing status page and it emits a well-formed fleet-telemetry frame", async () => {
const doctor = buildDoctor();
doctor.statusPage.start();
try {
const port = await waitForPort(doctor);
const { statusCode, body } = await fetchEventsFirstFrame(port);
expect(statusCode).toBe(200);
expect(body).toContain("event: fleet-telemetry");
expect(body).toContain('"services"');
expect(body).toContain('"logs"');
} finally {
doctor.statusPage.stop();
}
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/compose/telemetry-wiring.test.ts` around lines 125 - 137, The telemetry
wiring test starts the status page directly via doctor.statusPage.start() but
only calls doctor.stop(), which does not shut it down unless doctor.start() was
used. Update this test to explicitly stop the status page by calling the status
page shutdown/stop method on the doctor.statusPage instance after the fetch
assertion, so the HTTP server is always cleaned up and doesn’t leak into later
tests.

Comment thread tests/compose/telemetry-wiring.test.ts Outdated
Comment on lines +159 to +176
const event = await doctor.telemetryPollLoop.tick();
expect(event.services).toEqual([
{
name: "honeycomb",
health: "ok",
lastSeen: "2026-07-01T00:00:00.000Z",
metrics: { actionsTaken: 4, filesProcessed: 2, memoriesCreated: 1 },
deeplake: { connected: true, lastCommunicationAt: "2026-07-01T00:00:00.000Z" },
telemetryFault: null,
},
]);

// stop() without a matching start() is a documented no-op (mirrors every other
// pre-existing test in this repo that drives a loop directly without the full
// lifecycle); close the poll loop's cached read-only handle explicitly so the tmp
// dir cleanup below does not race an open SQLite file on Windows.
doctor.telemetryPollLoop.close();
await doctor.stop();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Close the SQLite-backed poll loop in a finally block.

The explicit doctor.telemetryPollLoop.close() runs only after the assertion on Line 160. When that assertion fails, the DB handle stays open and Windows cleanup hits the EBUSY unlink reported in CI.

Suggested fix
-		const event = await doctor.telemetryPollLoop.tick();
-		expect(event.services).toEqual([
-			{
-				name: "honeycomb",
-				health: "ok",
-				lastSeen: "2026-07-01T00:00:00.000Z",
-				metrics: { actionsTaken: 4, filesProcessed: 2, memoriesCreated: 1 },
-				deeplake: { connected: true, lastCommunicationAt: "2026-07-01T00:00:00.000Z" },
-				telemetryFault: null,
-			},
-		]);
-
-		// stop() without a matching start() is a documented no-op (mirrors every other
-		// pre-existing test in this repo that drives a loop directly without the full
-		// lifecycle); close the poll loop's cached read-only handle explicitly so the tmp
-		// dir cleanup below does not race an open SQLite file on Windows.
-		doctor.telemetryPollLoop.close();
-		await doctor.stop();
+		try {
+			const event = await doctor.telemetryPollLoop.tick();
+			expect(event.services).toEqual([
+				{
+					name: "honeycomb",
+					health: "ok",
+					lastSeen: "2026-07-01T00:00:00.000Z",
+					metrics: { actionsTaken: 4, filesProcessed: 2, memoriesCreated: 1 },
+					deeplake: { connected: true, lastCommunicationAt: "2026-07-01T00:00:00.000Z" },
+					telemetryFault: null,
+				},
+			]);
+		} finally {
+			doctor.telemetryPollLoop.close();
+			await doctor.stop();
+		}
 	});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const event = await doctor.telemetryPollLoop.tick();
expect(event.services).toEqual([
{
name: "honeycomb",
health: "ok",
lastSeen: "2026-07-01T00:00:00.000Z",
metrics: { actionsTaken: 4, filesProcessed: 2, memoriesCreated: 1 },
deeplake: { connected: true, lastCommunicationAt: "2026-07-01T00:00:00.000Z" },
telemetryFault: null,
},
]);
// stop() without a matching start() is a documented no-op (mirrors every other
// pre-existing test in this repo that drives a loop directly without the full
// lifecycle); close the poll loop's cached read-only handle explicitly so the tmp
// dir cleanup below does not race an open SQLite file on Windows.
doctor.telemetryPollLoop.close();
await doctor.stop();
try {
const event = await doctor.telemetryPollLoop.tick();
expect(event.services).toEqual([
{
name: "honeycomb",
health: "ok",
lastSeen: "2026-07-01T00:00:00.000Z",
metrics: { actionsTaken: 4, filesProcessed: 2, memoriesCreated: 1 },
deeplake: { connected: true, lastCommunicationAt: "2026-07-01T00:00:00.000Z" },
telemetryFault: null,
},
]);
} finally {
doctor.telemetryPollLoop.close();
await doctor.stop();
}
🧰 Tools
🪛 GitHub Actions: CI / 0_HiveDoctor gate (ubuntu-latest).txt

[error] 160-160: Vitest assertion failed (createHiveDoctor telemetry wiring PRD-001/PRD-002: registered entry with telemetryDbPath feeds telemetry poll loop end to end). Expected services health "ok" but received "unreachable".

🪛 GitHub Actions: CI / HiveDoctor gate (ubuntu-latest)

[error] 160-160: vitest run failed: AssertionError in 'a registered entry with telemetryDbPath feeds the telemetry poll loop end to end' (expected services[0].health 'ok', received 'unreachable').

🪛 GitHub Check: HiveDoctor gate (macos-latest)

[failure] 160-160: tests/compose/telemetry-wiring.test.ts > createHiveDoctor telemetry wiring (PRD-001/PRD-002) > a registered entry with telemetryDbPath feeds the telemetry poll loop end to end
AssertionError: expected [ { name: 'honeycomb', …(5) } ] to deeply equal [ { name: 'honeycomb', …(5) } ]

  • Expected
  • Received

@@ -2,11 +2,11 @@
{
"deeplake": {
"connected": true,
"lastCommunicationAt": "2026-07-01T00:00:00.000Z",
},

  • "health": "ok",
    
  • "health": "unreachable",
    "lastSeen": "2026-07-01T00:00:00.000Z",
    "metrics": {
      "actionsTaken": 4,
      "filesProcessed": 2,
      "memoriesCreated": 1,
    

❯ tests/compose/telemetry-wiring.test.ts:160:26

🪛 GitHub Check: HiveDoctor gate (ubuntu-latest)

[failure] 160-160: tests/compose/telemetry-wiring.test.ts > createHiveDoctor telemetry wiring (PRD-001/PRD-002) > a registered entry with telemetryDbPath feeds the telemetry poll loop end to end
AssertionError: expected [ { name: 'honeycomb', …(5) } ] to deeply equal [ { name: 'honeycomb', …(5) } ]

  • Expected
  • Received

@@ -2,11 +2,11 @@
{
"deeplake": {
"connected": true,
"lastCommunicationAt": "2026-07-01T00:00:00.000Z",
},

  • "health": "ok",
    
  • "health": "unreachable",
    "lastSeen": "2026-07-01T00:00:00.000Z",
    "metrics": {
      "actionsTaken": 4,
      "filesProcessed": 2,
      "memoriesCreated": 1,
    

❯ tests/compose/telemetry-wiring.test.ts:160:26

🪛 GitHub Check: HiveDoctor gate (windows-latest)

[failure] 160-160: tests/compose/telemetry-wiring.test.ts > createHiveDoctor telemetry wiring (PRD-001/PRD-002) > a registered entry with telemetryDbPath feeds the telemetry poll loop end to end
AssertionError: expected [ { name: 'honeycomb', …(5) } ] to deeply equal [ { name: 'honeycomb', …(5) } ]

  • Expected
  • Received

@@ -2,11 +2,11 @@
{
"deeplake": {
"connected": true,
"lastCommunicationAt": "2026-07-01T00:00:00.000Z",
},

  • "health": "ok",
    
  • "health": "unreachable",
    "lastSeen": "2026-07-01T00:00:00.000Z",
    "metrics": {
      "actionsTaken": 4,
      "filesProcessed": 2,
      "memoriesCreated": 1,
    

❯ tests/compose/telemetry-wiring.test.ts:160:26

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/compose/telemetry-wiring.test.ts` around lines 159 - 176, Move the
explicit doctor.telemetryPollLoop.close() cleanup into a finally block around
the tick/assertion sequence so it runs even when expect(event.services) fails;
keep the same telemetryPollLoop and doctor.stop() cleanup path, but ensure the
SQLite-backed handle is always closed before tmp dir teardown to avoid the
Windows EBUSY race.

Sources: Linters/SAST tools, Pipeline failures

…lemetry poll, name-mismatch guard, reload eviction, path validation, test cleanup

- compose: telemetry poll loop honors the injected CreateHiveDoctorOptions.probe
  seam instead of hard-coding probeHealth (fixes the failing telemetry-wiring CI test)
- poll-loop: reject a telemetry DB whose service_status.name does not match the
  registry entry (treated as malformed; nothing cached or forwarded)
- poll-loop: reload() also evicts a same-name runtime whose telemetryDbPath
  changed or disappeared, so the next tick reopens the new DB from the start
- registry: coerceTelemetryDbPath rejects relative post-tilde paths and returns
  the exact resolved absolute path it validated (no cwd-dependent reopen)
- status page: 404 fallback lists /events when the onEvents handler is wired
- telemetry-wiring tests: wrap in try/finally so the status page server and the
  SQLite-backed poll loop handle never leak on a failed assertion (Windows EBUSY)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant