Skip to content

test(synth-e2e): add Files API config.yaml round-trip gate (catches #2769 class) - #2773

Merged
HongmingWang-Rabbit merged 1 commit into
stagingfrom
test/synth-e2e-config-write-gate
May 4, 2026
Merged

HongmingWang-Rabbit merged 1 commit into
stagingfrom
test/synth-e2e-config-write-gate

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

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:

  1. `PUT /workspaces//files/config.yaml` with a marker payload that includes the run ID (so stale state can't false-pass).
  2. `GET` it back and asserts the marker line is present.
  3. Fails with an actionable error naming the likely class of regression (path-map vs permission) so the next operator doesn't have to redo today's debugging.

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

  • `bash -n` clean
  • `shellcheck` clean on the added block (pre-existing SC1091 / SC2015 unchanged)
  • Next synth-E2E firing exercises the new gate against staging

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:

  • `aws-actions/configure-aws-credentials` wired into `continuous-synth-e2e.yml`
  • A read-only IAM role provisioned on the AWS side (`ec2:DescribeInstances` only, scoped via OIDC trust to this repo + workflow path)

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

  • Local syntax check
  • CI green on this PR
  • First post-merge cron firing of continuous-synth-e2e is green with new step 7c

🤖 Generated with Claude Code

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
HongmingWang-Rabbit added this pull request to the merge queue May 4, 2026
Merged via the queue into staging with commit 1282c1c May 4, 2026
20 of 21 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the test/synth-e2e-config-write-gate branch May 4, 2026 21:54
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