feat(orchestrator): add GET /metrics endpoint with task and step counters - #96
Merged
Bosun-Josh121 merged 1 commit intoJul 29, 2026
Conversation
…ters The orchestrator runs long-lived on Render with no way to observe health beyond tailing logs. Adds a dependency-free in-process counter module and exposes it as JSON at GET /metrics. Counters cover process uptime and memory, task totals (total/active/ completed/failed/interrupted), step outcomes (executed/failed/timed-out), total USDC released, and a step-duration summary with p50/p95/max. Task transitions are keyed on task_id in a Set rather than a bare count, so they are idempotent: a task that fails mid-flight leaves `active` exactly once and can never sit in both `active` and `failed`. Every executor failure path already funnels through makeFailedResult, so that is where failures are counted. Step durations go into a 1024-entry ring buffer, keeping memory bounded while percentiles track recent behaviour. On startup the counters are seeded from the activity log and stored task results so a redeploy does not zero totals that are still knowable; tasks the log shows as in flight are counted as `interrupted` rather than `active`, since nothing is running in a fresh process.
📝 WalkthroughWalkthroughChangesOrchestrator metrics
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant TasksRoute
participant runTask
participant executeStep
participant MetricsModule
Client->>TasksRoute: submit task
TasksRoute->>runTask: start task
runTask->>MetricsModule: record taskStarted
runTask->>executeStep: execute step
executeStep->>MetricsModule: record step result and latency
executeStep->>MetricsModule: record released USDC
runTask->>MetricsModule: record taskCompleted or taskFailed
Client->>MetricsModule: GET /metrics
MetricsModule-->>Client: JSON metrics snapshot
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@packages/orchestrator/src/executor.ts`:
- Around line 326-328: Move the stepExecuted(latency_ms) call in the successful
execution path to immediately before the successful return, after rateResponse
and step_complete complete. Keep the existing latency calculation and ensure
exceptions continue to reach stepFailed without recording a successful execution
first.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e5abb9af-6abd-4e43-85a2-ab1747dcefed
📒 Files selected for processing (7)
docs/development.mdpackages/orchestrator/src/executor.tspackages/orchestrator/src/metrics.test.tspackages/orchestrator/src/metrics.tspackages/orchestrator/src/server.metrics.test.tspackages/orchestrator/src/server.tspackages/orchestrator/src/task-results.ts
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
GET /metricsto the orchestrator so its health can be observed withouttailing logs, and wires the counters into the task and step lifecycle.
Related issue
Closes #87
Changes
packages/orchestrator/src/metrics.ts: a plain counters object withincrement helpers. No new dependencies.
GET /metricsreturns uptime, memory, task counts (total / active /completed / failed / interrupted), step counts (executed / failed /
timed-out),
usdc_released_total, and a step-duration summary(p50 / p95 / max).
runTask()(task lifecycle) andPlanExecutor(steps and vault releases).
activity-log.jsonandtask-results.jsonso a redeploy does not zero totals that are stillknowable.
Decisions a reviewer should weigh in on
Output format is plain JSON, not Prometheus text exposition. The dashboard
is the main consumer and already speaks JSON everywhere else. Prometheus would
have meant a bespoke serializer for a format nothing in the repo currently
reads. The shape is documented as stable.
Task transitions are keyed on
task_idin aSet, not a bare counter.This makes the "must not linger in both
activeandfailed" requirement holdstructurally: the terminal helpers only count if
Set.deletereturns true, sorepeated or racing calls are no-ops. That is what makes it safe to call
taskFailedat the top ofrunTask's catch (before anything that could itselfthrow) plus a backstop in the
POST /api/taskscatch.Step failures are counted inside
makeFailedResult. All four executorfailure paths already funnel through it exactly once, so one call site covers
them all and future failure paths are counted for free.
steps.executedcounts all attempts, withfailedandtimed_outasnested subsets. The issue wording admits the other reading, so this is spelled
out in the docs.
tasks.interruptedis a field beyond the issue's minimum. Tasks theactivity log still shows in flight belong to a process that has exited, so
counting them as
activewould misreport a fresh process. Consequence,documented:
totaldoes not equal the sum of the other four.Durations use a 1024-entry ring buffer — bounded memory, and percentiles
track recent behaviour rather than being diluted by all history.
steps.executedkeeps counting past the cap; only the sample window is bounded.Testing
Full CI gate run locally, all green:
The 6 skips are the pre-existing vault integration tests that auto-skip without
testnet env vars.
28 of those tests are new:
metrics.test.ts(25) — counter transitions, idempotent terminal calls,interleaved async completion, timeout classification, percentile math,
ring-buffer bounding, and startup seeding.
server.metrics.test.ts(3) — real HTTP round-trip against the Express appasserting status, content-type, and full response shape.
Not run against live testnet: the endpoint is pure in-process state with no
Stellar dependency.
Checklist
npm run lintpassesnpm run typecheckpassesnpm testpassesnpm run format:checkpassesSummary by CodeRabbit
New Features
GET /metricsendpoint providing JSON operational metrics, including uptime, task and step counts, timeout totals, released USDC, duration summaries, and memory usage.Documentation