test(synth-e2e): add Files API config.yaml round-trip gate (catches #2769 class) - #2773
Merged
Merged
Conversation
Today's user-visible bug ("PUT /workspaces/<id>/files/config.yaml: 500
… install: cannot create directory '/opt/configs': Permission denied",
fixed in #2769) shipped to production and was caught only when an
operator opened the Canvas Config tab and clicked Save & Restart on
a claude-code workspace. Two compounding root causes:
1. Path-map fall-through: claude-code wasn't in
workspaceFilePathPrefix, so it fell through to the /opt/configs
default — a path the workspace EC2 doesn't have (cloud-init only
creates /configs).
2. Permission: /configs is root-owned, but the SSH-as-ubuntu install
command had no sudo prefix, so the write would have failed with
EACCES even with the right path.
The synth E2E provisions a fresh workspace every cron firing but
never PUTs a file via the Files API. So neither failure mode could
fail the canary.
Add a new step 7c (between terminal-diagnose and A2A) that:
- PUTs a known marker into config.yaml on each provisioned workspace
- GETs it back and asserts the marker is present
- Fails with an actionable message that names the likely class of
regression (path map vs permission) so the next operator doesn't
have to re-discover today's debugging path
The marker includes the run ID so stale state from a prior canary
can't false-pass.
Why round-trip (not just PUT-and-200): a 200 from PUT only proves the
SSH install succeeded somewhere on disk; the GET-back proves the file
landed at the path the runtime actually reads from (i.e., that the
host:/configs → container:/configs bind-mount sees it). Without the
GET, a future bug that writes to a non-bind-mounted host path would
silently no-op from the runtime's POV but pass the gate.
Deferred (separate PR, requires AWS-creds wiring): a parallel gate
that aws ec2 describe-instances on the workspace EC2 and asserts the
attached IamInstanceProfile.Arn — would directly catch the #466 IAM
profile gap class. Punted because it needs aws-actions/configure-aws-
credentials added to continuous-synth-e2e.yml + a read-only IAM role
provisioned on the AWS side. Tracked as task #301.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HongmingWang-Rabbit
requested a review
from hongmingwang-moleculeai
as a code owner
May 4, 2026 21:44
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.
Why
Today's user-visible bug — `PUT /workspaces//files/config.yaml: 500 install: cannot create directory '/opt/configs': Permission denied`, fixed in #2769 — shipped to production and was caught only when an operator opened the Canvas Config tab and clicked Save & Restart. The synth E2E provisions a fresh workspace every cron firing but never PUTs a file via the Files API, so neither failure mode (wrong path, missing sudo) could fail the canary.
What this adds
A new step 7c in `tests/e2e/test_staging_full_saas.sh` (between terminal-diagnose and A2A) that, for each provisioned workspace:
Why round-trip, not just PUT
A 200 from PUT only proves the SSH install succeeded somewhere on disk. The GET-back proves the file landed at the path the runtime actually reads from (i.e., that the `host:/configs → container:/configs` bind-mount sees it). Without the GET, a future bug that writes to a non-bind-mounted host path would silently no-op from the runtime's POV but pass the gate.
Verification
Deferred to follow-up
A parallel gate that calls `aws ec2 describe-instances` on the workspace EC2 and asserts the attached `IamInstanceProfile.Arn` matches `molecule-workspace-bootstrap` — would directly catch the #466 IAM profile gap class. Punted because it needs:
Tracked as task #301. The IAM rollout itself was end-to-end verified earlier via mid-flight monitor catch on synth-E2E run 25343935880 (both workspaces showed `molecule-workspace-bootstrap` profile + SSM `Online`); the gate just makes that observable per-run.
Test plan
🤖 Generated with Claude Code