Skip to content

fix(harness-runner): switch from non-existent /heartbeat-history to /activity - #2265

Merged
hongmingwang-moleculeai merged 1 commit into
stagingfrom
fix/harness-runner-activity-endpoint
Apr 29, 2026
Merged

fix(harness-runner): switch from non-existent /heartbeat-history to /activity#2265
hongmingwang-moleculeai merged 1 commit into
stagingfrom
fix/harness-runner-activity-endpoint

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

The runner was speculatively calling GET /workspaces/:id/heartbeat-history — that endpoint doesn't exist on workspace-server. Local dev returned 404; tenant deployments returned 28KB of Next.js HTML (canvas-proxy fallback intercepting unmatched paths). Neither was useful trace data.

GET /workspaces/:id/activity is the existing endpoint that reads activity_logs — already records a2a_send / a2a_receive / task_update / agent_log / error events with duration_ms + status, which is exactly the data RFC #2251 §V1.0 step 6 ("platform-side transition" check) needs.

Test plan

  • bash -n syntax check passes
  • GET /workspaces/<uuid>/activity?since_secs=60 returns 200 with [] against the local platform — endpoint exists, no SaaS skip needed
  • Renamed emitted event from heartbeat_traceactivity_trace in both the fetch and the interpretation block at the bottom

Linked

…activity

The runner was speculatively calling `/workspaces/:id/heartbeat-history` —
that endpoint doesn't exist on workspace-server. On local dev it 404'd;
on tenant builds the platform's :8080 canvas-proxy fallback intercepted
it and returned 28KB of Next.js HTML which then landed in the JSON event
log. Neither outcome was useful trace data.

`GET /workspaces/:id/activity` is the existing endpoint that reads
activity_logs. That table already records the events the RFC §V1.0
step 6 'platform-side transition' check needs (a2a_send / a2a_receive /
task_update / agent_log / error, plus duration_ms + status). Rename
the runner's fetch + emitted event accordingly.

Verified: GET /workspaces/<uuid>/activity?since_secs=60 returns 200
with `[]` against the local platform; no SaaS skip needed since the
endpoint exists in both environments.

Refs: molecule-core#2256 (V1.0 gate #1 measurement comment).
Merged via the queue into staging with commit 480b057 Apr 29, 2026
20 checks passed
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
… id is BYOK-routable (cp#529)' (#2265) from fix/cp529-enforcer-test-unbreak-main into main
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
…fered runtime×provider gated (keyless, required-lane)

Closes the provider-routing-correctness coverage hole identified in the
regression-coverage audit: many offered (runtime → provider) pairs — hermes's
17 name-only BYOK arms, claude-code's zai/deepseek/xiaomi-mimo, openclaw's
byok-openai/byok-minimax/groq/openrouter/custom, codex's byok-minimax, etc. —
are pure prefix-routing resolved by DeriveProvider(runtime, modelId) and had
ZERO test. A regression in the routing table (wrong provider, dropped arm, bad
regex) shipped silently and wedged tenant agents at boot.

DeriveProvider + ModelPrefixMatch resolve a model id to a provider with NO
upstream call — fully keyless — so the ENTIRE offered routing table is gateable
in the REQUIRED CI / all-required lane with zero secrets.

derive_provider_matrix_test.go is SSOT-DRIVEN (not hardcoded): it iterates
LoadManifest().Runtimes (the same registry production reads) and, for every
runtime × every offered model/provider arm, asserts (a) DeriveProvider resolves
to the EXACT expected provider (computed from the SSOT), (b) the (runtime, model)
is registration-valid (the validateRegisteredModelForRuntime predicate), and
(c) no offered id silently resolves to the wrong arm or falls through.

  - exact-listed arms: every model id iterated off the SSOT, expected provider
    computed from native declaration order (first-declared wins the codex/
    anthropic "one id, two auth arms" shape). A newly-added model is auto-covered.
  - name-only arms (zero models, pure prefix BYOK): each probed with a
    representative BYOK id its regex must own. The matrix REQUIRES a representative
    for every name-only arm in the SSOT — "added an arm, forgot routing/sample"
    fails RED. A dead representative (provider removed) also fails RED.

Coverage: 5 runtimes, 43 (runtime×provider) arms across 29 distinct providers,
53 exact-listed (runtime×model) assertions + 29 name-only BYOK routing probes.

Known-tricky forms pinned as explicit assertions so a regression names its class:
the #2263/#2274 colon-vs-slash-vs-bare MiniMax triple on claude-code (bare→minimax,
slash→platform, colon→unregistered), openai-namespaced-rejected-on-claude-code
(#2265 class), groq→groq, hermes anthropic//gemini//openai://minimax: →
byok-* (NOT platform — cp#529 billing safety), codex gpt default→openai-subscription
vs OPENAI_API_KEY→openai-api, google-adk platform: vs bare gemini.

Watch-it-fail proven: adding minimax:MiniMax-M2.7 to claude-code's platform arm
(pointing the colon BYOK form at platform) reds the matrix naming the exact
mismatch ("= platform, want an unregistered/unrouteable ERROR"); reverted → green.

go build ./... and go vet ./internal/providers/ clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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