Skip to content

staging → main: auto-promote cdef893 - #2386

Merged
github-actions[bot] merged 8 commits into
mainfrom
staging
Apr 30, 2026
Merged

staging → main: auto-promote cdef893#2386
github-actions[bot] merged 8 commits into
mainfrom
staging

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Automated promotion of staging (cdef8932) to main. All required staging gates green at this SHA: CI, E2E Staging Canvas, E2E API Smoke, CodeQL.

This PR is auto-generated by .github/workflows/auto-promote-staging.yml whenever every required gate completes green on the same staging SHA. It exists because main's branch protection requires status checks "set by the expected GitHub apps" — direct git push from a workflow can't satisfy that, only PR merges through the queue can.

Merge queue lands this; no human action needed unless gates fail. Reverse-direction sync (the merge commit on main → staging) is handled by auto-sync-main-to-staging.yml.

Hongming Wang and others added 2 commits April 30, 2026 08:27
…ad+Download

The "do request → check err → defer close → forward headers → set
status → io.Copy → log mid-stream errors" tail was duplicated between
Upload and Download. Each handler had ~12 lines that differed only in:

  - the op label in log messages ("upload" vs "download")
  - the set of response headers to forward verbatim
    (Upload: Content-Type only; Download: Content-Type +
    Content-Length + Content-Disposition)

Hoist into ChatFilesHandler.streamWorkspaceResponse(c, op,
workspaceID, forwardURL, req, forwardHeaders). Each call site
reduces to one line. Future changes — request-id forwarding,
observability metric, response-size cap, bytes-streamed log —
go in ONE place rather than two.

Same drift-prevention rationale as resolveWorkspaceForwardCreds
(#2372) and readOrLazyHealInboundSecret (#2376), applied to the
response-streaming layer of the same handlers.

Behavior preserved: existing TestChatUpload_* and TestChatDownload_*
integration tests (8 across both handlers) all pass unchanged. The
log message format is consistent across both handlers now (single
"chat_files {op}: ..." string template) — operators can grep one
prefix for both features instead of separate prefixes per handler.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…onse-helper

refactor(chat_files): extract streamWorkspaceResponse helper for Upload+Download
Hongming Wang and others added 2 commits April 30, 2026 08:41
Pin the 5 public functions adapters and the runtime hot-path import
through ``from platform_auth import``:

  - ``auth_headers`` — every outbound httpx call merges this in
  - ``self_source_headers`` — A2A peer + self-message header builder
  - ``get_token`` — main.py reads on boot to decide register-vs-resume
  - ``save_token`` — main.py persists the platform-issued token
  - ``refresh_cache`` — 401-retry path drops in-process cache (#1877)

A grep across workspace/ shows 14+ runtime modules import these:
main.py, heartbeat.py, a2a_client.py, a2a_tools.py, consolidation.py,
events.py, executor_helpers.py (3 sites), molecule_ai_status.py,
builtin_tools/memory.py (3 sites), builtin_tools/temporal_workflow.py
(2 sites). Renaming any of the five (e.g. ``auth_headers`` →
``bearer_headers``) makes every one of those imports raise ImportError
at workspace boot — the failure surface is deep in heartbeat init,
nowhere near the rename site.

Same drift class as the BaseAdapter signature snapshot (#2378, #2380),
skill_loader gate (#2381), runtime_wedge gate (#2383). Reuses the
``_signature_snapshot.py`` helpers shipped in #2381.

Defense-in-depth: ``test_snapshot_has_required_functions`` asserts
the five names are still present, so removing one even with a
synchronized snapshot edit forces an explicit edit here with a
justification.

``clear_cache`` is intentionally NOT in the snapshot — it's a
test-only helper. Production code MUST NOT depend on it.

Verified red on deliberate rename: ``auth_headers`` →
``bearer_headers`` produces a clean diff of the missing function in
the failure message. Restored before commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e-snapshot

test(platform_auth): module-functions signature snapshot drift gate
@github-actions

Copy link
Copy Markdown
Contributor Author

🔒 Auto-merge disabled — new commit (9b06167) pushed after auto-merge was enabled. The merge queue locks SHAs at entry, so subsequent pushes can race. Verify the new commit and re-enable with gh pr merge --auto.

…space_status enum

Migration 043 (2026-04-25) introduced the workspace_status enum but
omitted two values application code had been writing for days, so every
UPDATE that tried to write either value failed silently in production:

  'awaiting_agent' (since 2026-04-24, commit 1e8b5e0):
    - handlers/workspace.go:333 — external workspace pre-register
    - handlers/registry.go (via PR #2382) — liveness offline transition
    - registry/healthsweep.go (via PR #2382) — heartbeat-staleness sweep

  'hibernating' (since hibernation feature shipped):
    - handlers/workspace_restart.go:271 — DB-level claim before stop

All four/five sites swallowed the enum-cast error. User-visible impact:
external workspaces never transition to a stale state when their agent
disconnects (canvas shows them stuck on 'online'/'degraded' indefinitely),
new external workspaces never advance past 'provisioning', and idle
workspaces never auto-hibernate (resources held forever).

PR #2382 didn't *cause* this — it inherited the gap and added two more
silent-fail paths on top. The pre-existing two had been broken for five
days and went unnoticed because:

  1. sqlmock matches SQL by regex, not against the live enum constraint.
     Every test passed despite the prod-only failure.
  2. The handlers either drop the Exec error entirely (workspace.go:333)
     or log+continue without an alert (the other three).

Fix in three pieces:

  1. migrations/046_*.up.sql — ALTER TYPE workspace_status ADD VALUE
     'awaiting_agent', 'hibernating'. IF NOT EXISTS makes it idempotent
     across re-runs (RunMigrations re-applies until schema_migrations
     records the file). ALTER TYPE ADD VALUE doesn't take a heavy lock
     and commits immediately, safe under live traffic.

  2. migrations/046_*.down.sql — full rename → recreate → cast → drop
     recipe. Postgres has no DROP VALUE so this is the only honest
     rollback. Pre-flights existing rows to compatible values
     (awaiting_agent → offline, hibernating → hibernated) before the
     type swap.

  3. internal/db/workspace_status_enum_drift_test.go — static gate that
     parses every UPDATE/INSERT against `workspaces` in workspace-server/
     internal/, extracts every status literal, and asserts each is in
     the enum union (CREATE TYPE + every ALTER TYPE ADD VALUE). The gate
     runs in unit tests, no DB required, and would have caught both
     omissions on the day they shipped. Pattern matches
     feedback_behavior_based_ast_gates and feedback_mock_at_drifting_layer.

Verification:
  - go test ./internal/db/ -count=1 -race  ✓
  - go vet ./...                           ✓
  - Drift gate flips red if I delete either ADD VALUE from the migration
    (validated via local mutation).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@HongmingWang-Rabbit HongmingWang-Rabbit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Five-axis pass via back-reference. Auto-promote (github-actions[bot]) bundles two already-reviewed staging-merged commits since #319:

  • #2385 refactor(chat_files): extract streamWorkspaceResponse helper for Upload+Download — approved on source PR by hongmingwang-moleculeai. Single-helper dedup of the do-request → defer-close → forward-headers → io.Copy tail; per-handler header set passed in. No behavior delta.
  • #2387 test(platform_auth): module-functions signature snapshot drift gate — approved on source PR by hongmingwang-moleculeai. Same drift-class as #2378 (BaseAdapter), #2380 (adapter dataclasses), #2381 (skill_loader), #2383 (runtime_wedge); pins the five contract functions (auth_headers, self_source_headers, get_token, save_token, refresh_cache) imported across 14+ runtime hot-path files. Test-only addition.

Five axes:

  • Correctness — both source PRs landed green on staging; carrier diff is byte-identical
  • Readability — strong inline rationale; clear_cache correctly excluded as test-only with explicit comment
  • Architecture — reuses shared _signature_snapshot.py helpers; extends established pattern
  • Security — no credentials, no auth-bypass, no SQL string concat; no deleted tests; no .github/workflows/* changes
  • Performance — test-only signature gate; trivial CI cost

Note: mergeStateStatus: BEHIND — main has commits not in staging. Will need a git fetch origin main && git merge origin/main (or rebase) on staging before fast-forward succeeds.

Comment-only per loop policy (substantive carrier on auto-promote). Approval already on source PRs.

HongmingWang-Rabbit and others added 2 commits April 30, 2026 15:55
…num-awaiting-agent

fix(workspaces): add missing 'awaiting_agent' + 'hibernating' to workspace_status enum
…ck workflow_run chain

Root cause (verified 2026-04-30): GITHUB_TOKEN-initiated
workflow_dispatch creates the dispatched run, but the resulting run's
completion event does NOT fire downstream `workflow_run` triggers.

This is the documented "no recursion" rule:
https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow

Evidence (publish-workspace-server-image runs on main):

  run_id      | head_sha  | triggering_actor      | canary | redeploy
  ------------+-----------+-----------------------+--------+----------
  25151545007 | 6ef562e  | HongmingWang-Rabbit   |  YES   |  YES
  25171773918 | 21313dc   | github-actions[bot]   |  NO    |  NO
  25173801008 | 59dec57   | github-actions[bot]   |  NO    |  NO

The 06:52Z run that "worked" was an operator-fired dispatch from the
terminal — actor was the operator's PAT. The two runs that "dropped"
were dispatched by auto-promote-staging.yml's `gh workflow run` step
authenticated via `secrets.GITHUB_TOKEN`, so the actor became
`github-actions[bot]` and the workflow_run cascade was suppressed.

Same workflow file, same dispatch call, same successful publish run
— only the auth token differed.

Fix: mint a molecule-ai GitHub App installation token before the
dispatch step and use it as `GH_TOKEN`. App-initiated dispatches
DO propagate the workflow_run cascade (the App user is a real
identity, not the GITHUB_TOKEN bot pseudonym).

The molecule-ai App (app_id=3398844, installation 124443072) is
already installed on the org with `actions:write` — no new App
needed. Only secrets are missing.

## Required setup before merge

The following repo secrets must be added at
https://github.com/Molecule-AI/molecule-core/settings/secrets/actions
or auto-promote will hard-fail at the new "Mint App token" step:

- `MOLECULE_AI_APP_ID`         = `3398844`
- `MOLECULE_AI_APP_PRIVATE_KEY` = contents of a .pem file generated at
  https://github.com/organizations/Molecule-AI/settings/installations/124443072

(Click "Generate a private key" if one doesn't exist yet.)

## Long-term cleanup

The polling tail step still exists because the auto-merge call
itself uses GITHUB_TOKEN, so the FF push to main doesn't fire
publish-workspace-server-image's `push` trigger naturally. Switching
the auto-merge call to use the SAME App token would eliminate the
polling tail entirely. Tracked in #2357.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor Author

🔒 Auto-merge disabled — new commit (8aba565) pushed after auto-merge was enabled. The merge queue locks SHAs at entry, so subsequent pushes can race. Verify the new commit and re-enable with gh pr merge --auto.

…-dispatch

ci(auto-promote): dispatch publish via molecule-ai App token to unblock workflow_run chain
@github-actions

Copy link
Copy Markdown
Contributor Author

🔒 Auto-merge disabled — new commit (2507cc0) pushed after auto-merge was enabled. The merge queue locks SHAs at entry, so subsequent pushes can race. Verify the new commit and re-enable with gh pr merge --auto.

@github-actions
github-actions Bot merged commit 319f85a into main Apr 30, 2026
54 checks passed
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
CP Deprovision now routes by ?provider=. Without it, a non-AWS workspace
falls through to the AWS terminate path and leaks the box.

Changes:
- Add resolveProvider helper (queries workspaces.compute->>'provider').
- Append &provider= to the DELETE URL in stopInternal when provider is non-empty.
- Add regression tests for both provider-present and provider-absent paths.

Fixes #2386.
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
…osed on lookup error (#2386 sibling-leak)

Researcher found CPProvisioner.IsRunning/status omits 'provider' on its
control-plane call, misrouting non-AWS workspaces to the AWS status path.
Same bug class as deprovision leak #2386/#2387.

Changes:
- Add resolveProvider helper (workspaces.compute->>'provider') mirroring
  resolveInstanceID pattern.
- IsRunning: resolve provider, fail-closed on error (return true, err so
  a2a_proxy stays on alive path), URL-encode with url.Values/q.Encode().
- Regression tests: (a) provider threaded to status query, (b) fail-closed
  on lookup error — no CP call, (c) hostile-slug encoding round-trip.

Diff scoped to cp_provisioner.go + cp_provisioner_test.go only.
Branch off fresh origin/main (no stacking on #2387/#2388).
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
…status call, fail-closed on lookup error (#2386 sibling-leak)' (#2389) from fix/provider-on-isrunning-status into main
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