Skip to content

feat: add daemon cycle duration and skip metrics - #566

Closed
sarah-obasi-analytics wants to merge 1 commit into
TegoLabs:mainfrom
sarah-obasi-analytics:daemon-cycle-duration
Closed

sarah-obasi-analytics wants to merge 1 commit into
TegoLabs:mainfrom
sarah-obasi-analytics:daemon-cycle-duration

Conversation

@sarah-obasi-analytics

Copy link
Copy Markdown
Contributor

What does this PR do?

closes #334

Why?

Does this touch secret-key handling or transaction submission?

  • Yes — see notes above
  • No

Checklist

  • Tests pass (npm test)
  • Type check passes (npx tsc --noEmit)
  • Lint passes (npm run lint)
  • Tests cover the new functionality (TDD preferred — see CONTRIBUTING.md)
  • No unnecessary dependencies added
  • Commit messages follow conventional format
  • No console.log in core logic
  • ADR added if this is a significant design decision (see docs/adr)
  • E2E sandbox tested, if this touches RPC or daemon behavior (see docs/e2e-sandbox.md)

@drips-wave

drips-wave Bot commented Jul 29, 2026

Copy link
Copy Markdown

@sarah-obasi-analytics Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 24c4d72c-e308-40ab-9bfe-56030ced86f7

📥 Commits

Reviewing files that changed from the base of the PR and between 35d9237 and 963eca2.

📒 Files selected for processing (4)
  • src/daemon/loop.ts
  • src/observability/metrics/daemon.ts
  • src/observability/registry.ts
  • tests/daemon/loop.test.ts
 _____________________________________________________________________________________________
< Use the power of command shells. Use the shell when graphical user interfaces don't cut it. >
 ---------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 3

🤖 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/daemon/loop.ts`:
- Around line 127-128: Measure daemon cycle duration from executeCycle() entry
through successful completion rather than using MonitorCycleResult timestamps;
update the duration observation in src/daemon/loop.ts lines 127-128 accordingly.
Update the timing assertion in tests/daemon/loop.test.ts lines 441-454 to verify
the full-cycle duration contract.

In `@src/observability/metrics/daemon.ts`:
- Around line 1-7: Replace the no-op collectors in daemonCycleDuration and
daemonCyclesSkipped with registered histogram and counter metrics, using the
names sorokeep_daemon_cycle_duration_seconds and
sorokeep_daemon_cycles_skipped_total. Preserve the existing observe and inc
interfaces while wiring both collectors into the project’s metrics registry so
their values appear in /metrics.

In `@tests/daemon/loop.test.ts`:
- Around line 441-454: Update the test around startDaemon and
mockRunMonitorCycle to cover the complete executeCycle() boundary, including
delivery, introspection, extensions, aggregation, and callback work rather than
only varying MonitorCycleResult timestamps. Arrange measurable delays or
controlled timestamps for each phase, then assert mockMetrics.observe receives
the total elapsed cycle duration in seconds.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 24c4d72c-e308-40ab-9bfe-56030ced86f7

📥 Commits

Reviewing files that changed from the base of the PR and between 35d9237 and 963eca2.

📒 Files selected for processing (4)
  • src/daemon/loop.ts
  • src/observability/metrics/daemon.ts
  • src/observability/registry.ts
  • tests/daemon/loop.test.ts
📜 Review details
⚠️ CI failures not shown inline (2)

GitHub Actions: CI Pipeline / 0_build-and-test (22.x).txt: feat: add daemon cycle duration and skip metrics

Conclusion: failure

View job details

##[group]Run npm audit --audit-level=high
 �[36;1mnpm audit --audit-level=high�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 # npm audit report
 `@hono/node-server`  <2.0.5
 Severity: moderate
 Node.js Adapter for Hono: Path traversal in `serve-static` on Windows via encoded backslash (`%5C`) - https://github.com/advisories/GHSA-frvp-7c67-39w9
 fix available via `npm audit fix`
 node_modules/@hono/node-server
   `@modelcontextprotocol/sdk`  1.25.0 - 1.29.0
   Depends on vulnerable versions of `@hono/node-server`
   node_modules/@modelcontextprotocol/sdk
 axios  1.0.0 - 1.17.0
 Severity: high
 Axios: Excessive recursion in formDataToJSON can cause denial of service - https://github.com/advisories/GHSA-42h9-826w-cgv3
 Axios: Prototype pollution auth subfields can inject Basic auth - https://github.com/advisories/GHSA-xj6q-8x83-jv6g
 Axios: Deep formToJSON Key Recursion Can Cause Denial of Service - https://github.com/advisories/GHSA-pmv8-rq9r-6j72
 Axios: Fetch adapter `ReadableStream` uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-jqh4-m9w3-8hp9
 Axios: Prototype pollution gadgets can alter axios request construction - https://github.com/advisories/GHSA-mmx7-hfxf-jppx
 Axios: NO_PROXY bypass for 0.0.0.0 local addresses in axios - https://github.com/advisories/GHSA-f4gw-2p7v-4548
 Axios Node HTTP adapter can use an inherited proxy after interceptor config cloning - https://github.com/advisories/GHSA-gcfj-64vw-6mp9
 Axios form serializer maxDepth bypass via {} metatoken - https://github.com/advisories/GHSA-hcpx-6fm6-wx23
 Axios: Nested axios option objects can consume polluted prototype values - https://github.com/advisories/GHSA-7q8q-rj6j-mhjq
 Axios: HTTP/2 streamed uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-mwf2-3pr3-8698
 fix available via `npm audit fix`
 node_modules/axios
   `@stellar/stellar-sdk`  15.0.1 - 16.0.1
   Depends on vulnerable versions of axios
   node_modules/@stellar/stellar-sdk
 brace-expansion  <=5.0.7
 ...

GitHub Actions: CI Pipeline / build-and-test (22.x): feat: add daemon cycle duration and skip metrics

Conclusion: failure

View job details

##[group]Run npm audit --audit-level=high
 �[36;1mnpm audit --audit-level=high�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 # npm audit report
 `@hono/node-server`  <2.0.5
 Severity: moderate
 Node.js Adapter for Hono: Path traversal in `serve-static` on Windows via encoded backslash (`%5C`) - https://github.com/advisories/GHSA-frvp-7c67-39w9
 fix available via `npm audit fix`
 node_modules/@hono/node-server
   `@modelcontextprotocol/sdk`  1.25.0 - 1.29.0
   Depends on vulnerable versions of `@hono/node-server`
   node_modules/@modelcontextprotocol/sdk
 axios  1.0.0 - 1.17.0
 Severity: high
 Axios: Excessive recursion in formDataToJSON can cause denial of service - https://github.com/advisories/GHSA-42h9-826w-cgv3
 Axios: Prototype pollution auth subfields can inject Basic auth - https://github.com/advisories/GHSA-xj6q-8x83-jv6g
 Axios: Deep formToJSON Key Recursion Can Cause Denial of Service - https://github.com/advisories/GHSA-pmv8-rq9r-6j72
 Axios: Fetch adapter `ReadableStream` uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-jqh4-m9w3-8hp9
 Axios: Prototype pollution gadgets can alter axios request construction - https://github.com/advisories/GHSA-mmx7-hfxf-jppx
 Axios: NO_PROXY bypass for 0.0.0.0 local addresses in axios - https://github.com/advisories/GHSA-f4gw-2p7v-4548
 Axios Node HTTP adapter can use an inherited proxy after interceptor config cloning - https://github.com/advisories/GHSA-gcfj-64vw-6mp9
 Axios form serializer maxDepth bypass via {} metatoken - https://github.com/advisories/GHSA-hcpx-6fm6-wx23
 Axios: Nested axios option objects can consume polluted prototype values - https://github.com/advisories/GHSA-7q8q-rj6j-mhjq
 Axios: HTTP/2 streamed uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-mwf2-3pr3-8698
 fix available via `npm audit fix`
 node_modules/axios
   `@stellar/stellar-sdk`  15.0.1 - 16.0.1
   Depends on vulnerable versions of axios
   node_modules/@stellar/stellar-sdk
 brace-expansion  <=5.0.7
 ...
🔇 Additional comments (3)
src/observability/registry.ts (1)

1-5: LGTM!

src/daemon/loop.ts (1)

10-10: LGTM!

Also applies to: 204-204

tests/daemon/loop.test.ts (1)

33-45: LGTM!

Also applies to: 100-101, 456-482

Comment thread src/daemon/loop.ts
Comment on lines +127 to +128
const cycleDurationSec = (result.cycleFinishedAt.getTime() - result.cycleStartedAt.getTime()) / 1000;
daemonCycleDuration.observe(cycleDurationSec);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the full executeCycle() duration consistently.

The implementation and test currently measure only the monitor result’s timestamps, excluding later daemon-cycle work.

  • src/daemon/loop.ts#L127-L128: time from executeCycle() entry through successful completion.
  • tests/daemon/loop.test.ts#L441-L454: assert the full-cycle timing contract rather than only the MonitorCycleResult timestamp difference.
📍 Affects 2 files
  • src/daemon/loop.ts#L127-L128 (this comment)
  • tests/daemon/loop.test.ts#L441-L454
🤖 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 `@src/daemon/loop.ts` around lines 127 - 128, Measure daemon cycle duration
from executeCycle() entry through successful completion rather than using
MonitorCycleResult timestamps; update the duration observation in
src/daemon/loop.ts lines 127-128 accordingly. Update the timing assertion in
tests/daemon/loop.test.ts lines 441-454 to verify the full-cycle duration
contract.

Comment on lines +1 to +7
export const daemonCycleDuration = {
observe: (value: number) => {},
};

export const daemonCyclesSkipped = {
inc: (value?: number) => {},
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Implement real registered metrics instead of no-op stubs.

Both observe and inc discard their inputs, so the daemon calls cannot produce sorokeep_daemon_cycle_duration_seconds or sorokeep_daemon_cycles_skipped_total in /metrics. Replace these with actual histogram/counter collectors using the required metric names and registry integration.

🤖 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 `@src/observability/metrics/daemon.ts` around lines 1 - 7, Replace the no-op
collectors in daemonCycleDuration and daemonCyclesSkipped with registered
histogram and counter metrics, using the names
sorokeep_daemon_cycle_duration_seconds and sorokeep_daemon_cycles_skipped_total.
Preserve the existing observe and inc interfaces while wiring both collectors
into the project’s metrics registry so their values appear in /metrics.

Comment thread tests/daemon/loop.test.ts
Comment on lines +441 to +454
it("records the cycle duration histogram upon completion", async () => {
const start = new Date(1000);
const finish = new Date(3500); // 2.5s duration
mockRunMonitorCycle.mockResolvedValue(makeCycleResult({
cycleStartedAt: start,
cycleFinishedAt: finish,
}));

await startDaemon(db, "testnet", { intervalMs: 5000 });

// Should be called with the duration in seconds
expect(mockMetrics.observe).toHaveBeenCalledTimes(1);
expect(mockMetrics.observe).toHaveBeenCalledWith(2.5);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the duration test cover the full cycle contract.

This test varies only MonitorCycleResult timestamps, so it passes even when delivery, introspection, extensions, aggregation, or callback work is excluded from the histogram. Assert that observation reflects the full executeCycle() boundary instead.

🤖 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/daemon/loop.test.ts` around lines 441 - 454, Update the test around
startDaemon and mockRunMonitorCycle to cover the complete executeCycle()
boundary, including delivery, introspection, extensions, aggregation, and
callback work rather than only varying MonitorCycleResult timestamps. Arrange
measurable delays or controlled timestamps for each phase, then assert
mockMetrics.observe receives the total elapsed cycle duration in seconds.

@gitguardian

gitguardian Bot commented Jul 29, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
- - Generic High Entropy Secret ded54f4 tests/commands/guard-cli-export-import.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

AbdulmalikAlayande added a commit that referenced this pull request Aug 1, 2026
…#334)

PR #566's registry.ts diff replaced the established prom-client
Registry pattern (and would have deleted the budget/TTL/cost metrics
already wired into it) with an unrelated namespace-object design, and
its metrics/daemon.ts was a non-functional stub (observe/inc were both
no-ops). Its daemon/loop.ts diff was based on a very old snapshot of
the file and, beyond the instrumentation, would have reintroduced a
real bug: resetting cycleInFlight inside stopDaemon() — exactly the
re-entrance-guard race the current code deliberately avoids (see the
"Do NOT reset cycleInFlight here" comment in stopDaemon) — plus it
silently deleted the metrics-port wiring from #569, the feeSponsorSecret
option, and the per-network poll-interval override.

Implemented the same two metrics narrowly against the current loop.ts
instead:

- src/observability/metrics/daemon.ts: sorokeep_daemon_cycle_duration_seconds
  (Histogram) and sorokeep_daemon_cycles_skipped_total (Counter), both
  labeled by network to match the codebase's existing per-metric
  labeling convention (enforced by an existing regression test in
  budget.test.ts).
- registry.ts: two registration lines, no changes to collectAllMetrics
  needed — these are event-driven (observed at the point of occurrence),
  not recomputed from the DB on scrape.
- daemon/loop.ts: exactly the two instrumentation calls the issue asked
  for (histogram.observe() using the cycle's own cycleStartedAt/
  cycleFinishedAt, and counter.inc() at the existing "Skipping tick"
  line) — no other changes to executeCycle, scheduledTick, or the
  re-entrance guard.
- Added two tests to the existing (not replaced) loop.test.ts suite,
  using the real prom-client objects rather than mocks.

Verified: tsc clean, full suite 1279/1279, npm audit clean, build
succeeds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@AbdulmalikAlayande

Copy link
Copy Markdown
Collaborator

Thanks for tackling this — the metrics themselves (cycle duration histogram, skip counter) are exactly what the issue asked for.

Closing manually rather than merging: your registry.ts replaced the prom-client Registry pattern already established by earlier-merged metrics with an unrelated namespace-object design (and metrics/daemon.ts's observe/inc were no-op stubs, not real prom-client objects). More importantly, daemon/loop.ts was based on a stale snapshot — beyond the instrumentation, it would have reintroduced a real re-entrance-guard bug (resetting cycleInFlight inside stopDaemon(), which the current code deliberately avoids — see the comment there) and silently removed the metrics-port wiring, feeSponsorSecret, and the per-network poll-interval override that had landed since your branch was created.

Implemented the same two metrics narrowly on top of current main (commit fc5e4c9): sorokeep_daemon_cycle_duration_seconds and sorokeep_daemon_cycles_skipped_total, both labeled by network (matching an existing codebase-wide labeling convention), wired via real prom-client objects — plus exactly the two instrumentation call-sites the issue described, nothing else in loop.ts touched.

Verified: tsc clean, full suite 1279/1279, npm audit clean, build succeeds, and tested via 2 new tests exercising the real (non-mocked) metric objects through the actual startDaemon/scheduledTick code paths.

AbdulmalikAlayande added a commit that referenced this pull request Aug 2, 2026
… the HTTP server (#540, #339)

PR #540's registry.ts diff was, again, a complete standalone
reimplementation (computeMetrics/formatPrometheus hand-rolling its own
SQL queries and Prometheus serializer, bypassing prom-client and every
already-registered metric entirely) — the exact same collision pattern
as #543/#566/#591/#542. The issue's own step 1 ("extract the
metric-computation logic... reusable by both the HTTP route and this
command") was actually already satisfied by the real registry.ts —
collectAllMetrics(db) + register.metrics() is exactly that shared
logic, already used by the /metrics route.

Wrote src/commands/metrics.ts fresh against the real registry:
collectAllMetrics(db) then register.metrics() for the default
Prometheus-text output, or register.getMetricsAsJSON() (prom-client's
own structured format) for --json — no new serialization logic
needed. Registered in src/cli/program.ts (the current entry point;
src/index.ts is now just a thin wrapper around createProgram()).

Added tests asserting the command's default output matches what
collectAllMetrics + register.metrics() produce directly for the same
DB state (the acceptance criteria's actual claim), that --json
produces valid parseable JSON with the expected metric/label shape,
and the empty-database case.

Verified: tsc clean, full suite 1300/1300, npm audit clean, build
succeeds, and manually smoke-tested both `sorokeep metrics` and
`sorokeep metrics --json` against the compiled CLI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AbdulmalikAlayande added a commit that referenced this pull request Aug 2, 2026
…#334)

PR #566's registry.ts diff replaced the established prom-client
Registry pattern (and would have deleted the budget/TTL/cost metrics
already wired into it) with an unrelated namespace-object design, and
its metrics/daemon.ts was a non-functional stub (observe/inc were both
no-ops). Its daemon/loop.ts diff was based on a very old snapshot of
the file and, beyond the instrumentation, would have reintroduced a
real bug: resetting cycleInFlight inside stopDaemon() — exactly the
re-entrance-guard race the current code deliberately avoids (see the
"Do NOT reset cycleInFlight here" comment in stopDaemon) — plus it
silently deleted the metrics-port wiring from #569, the feeSponsorSecret
option, and the per-network poll-interval override.

Implemented the same two metrics narrowly against the current loop.ts
instead:

- src/observability/metrics/daemon.ts: sorokeep_daemon_cycle_duration_seconds
  (Histogram) and sorokeep_daemon_cycles_skipped_total (Counter), both
  labeled by network to match the codebase's existing per-metric
  labeling convention (enforced by an existing regression test in
  budget.test.ts).
- registry.ts: two registration lines, no changes to collectAllMetrics
  needed — these are event-driven (observed at the point of occurrence),
  not recomputed from the DB on scrape.
- daemon/loop.ts: exactly the two instrumentation calls the issue asked
  for (histogram.observe() using the cycle's own cycleStartedAt/
  cycleFinishedAt, and counter.inc() at the existing "Skipping tick"
  line) — no other changes to executeCycle, scheduledTick, or the
  re-entrance guard.
- Added two tests to the existing (not replaced) loop.test.ts suite,
  using the real prom-client objects rather than mocks.

Verified: tsc clean, full suite 1279/1279, npm audit clean, build
succeeds.
AbdulmalikAlayande added a commit that referenced this pull request Aug 2, 2026
… the HTTP server (#540, #339)

PR #540's registry.ts diff was, again, a complete standalone
reimplementation (computeMetrics/formatPrometheus hand-rolling its own
SQL queries and Prometheus serializer, bypassing prom-client and every
already-registered metric entirely) — the exact same collision pattern
as #543/#566/#591/#542. The issue's own step 1 ("extract the
metric-computation logic... reusable by both the HTTP route and this
command") was actually already satisfied by the real registry.ts —
collectAllMetrics(db) + register.metrics() is exactly that shared
logic, already used by the /metrics route.

Wrote src/commands/metrics.ts fresh against the real registry:
collectAllMetrics(db) then register.metrics() for the default
Prometheus-text output, or register.getMetricsAsJSON() (prom-client's
own structured format) for --json — no new serialization logic
needed. Registered in src/cli/program.ts (the current entry point;
src/index.ts is now just a thin wrapper around createProgram()).

Added tests asserting the command's default output matches what
collectAllMetrics + register.metrics() produce directly for the same
DB state (the acceptance criteria's actual claim), that --json
produces valid parseable JSON with the expected metric/label shape,
and the empty-database case.

Verified: tsc clean, full suite 1300/1300, npm audit clean, build
succeeds, and manually smoke-tested both `sorokeep metrics` and
`sorokeep metrics --json` against the compiled CLI.
AbdulmalikAlayande added a commit that referenced this pull request Aug 2, 2026
…le phases (#572, #341)

PR #572's daemon/loop.ts and core/monitor.ts diffs were based on a
stale snapshot of both files, predating everything merged into them
this wave — taking them as-is would have deleted the metrics-port
wiring (#569), daemonCycleDuration/daemonCyclesSkipped instrumentation
(#566), the /readyz RPC client construction (#542), and — critically —
reintroduced a real bug: resetting cycleInFlight inside stopDaemon()
in loop.ts, the exact re-entrance-guard race the current code
deliberately avoids. In monitor.ts, it would have deleted the fan-out
delivery feature (#541) and the per-config enabled check (#580).

Its package.json diff also downgraded @stellar/stellar-sdk and
dropped hono/nodemailer entirely.

Ported the actual tracing work — a self-contained tracing.ts module
(tracer provider setup, OTLP/in-memory exporter config via env vars,
defensive span-error/end helpers) needed no changes and was taken as
written, since the issue's own scope kept it isolated from
registry.ts/metrics. Manually re-applied the span instrumentation
against the current, unmodified control flow of both files:

- daemon/loop.ts: a DaemonCycle parent span wrapping executeCycle,
  with Monitor/Deliver/CostAggregation child spans (renamed from the
  issue's "Auto-Extend" — the third phase actually wraps
  aggregateDailyCostSnapshots; real auto-extension happens inside
  runMonitorCycle's own "Monitor" phase, so labeling it Auto-Extend
  would have been actively misleading). No changes to cycleInFlight,
  stopDaemon, or scheduledTick's skip logic — span calls only.
- core/monitor.ts: a process-contract span per contract inside the
  existing loop, tagged with contract.id. No changes to processContract
  itself (fan-out, enabled check untouched).
- Fixed a real gap in the ported code: initTracing() was never called
  before getTracer() in the original diff, meaning OTLP/in-memory
  exporter configuration would never actually take effect in
  production — getTracer()'s lazy fallback would silently lock in an
  uninstrumented provider on first use. Added the missing call.
- Added tracing tests to the existing loop.test.ts and monitor.test.ts
  suites (not a separate stale-mocked file) verifying the acceptance
  criteria directly: a parent span with Monitor/Deliver/CostAggregation
  children, error status recorded on a failed Monitor span, no
  measurable behavior change when tracing is off, and one
  process-contract span per contract.

Verified: tsc clean, full suite 1304/1304, npm audit clean, build
succeeds, and manually smoke-tested real span creation/parent-child
linking/in-memory export end-to-end against the compiled module.
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.

feat(observability): expose daemon-cycle-duration histogram metric

2 participants