a3c15bc9be
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 6s
CI / Detect changes (pull_request) Successful in 18s
CI / Python Lint & Test (pull_request) Successful in 7s
E2E API Smoke Test / detect-changes (pull_request) Successful in 9s
E2E Chat / detect-changes (pull_request) Successful in 8s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Successful in 46s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Has been skipped
Handlers Postgres Integration / detect-changes (pull_request) Successful in 5s
Harness Replays / detect-changes (pull_request) Successful in 3s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 3s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 3s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 33s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 9s
gate-check-v3 / gate-check (pull_request) Successful in 4s
qa-review / approved (pull_request) Failing after 4s
security-review / approved (pull_request) Failing after 3s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 4s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 4s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m4s
CI / Canvas (Next.js) (pull_request) Successful in 1s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 19s
E2E Chat / E2E Chat (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 4s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Failing after 1m15s
Harness Replays / Harness Replays (pull_request) Successful in 4s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 1m38s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m15s
CI / Platform (Go) (pull_request) Successful in 4m31s
CI / all-required (pull_request) Successful in 7m29s
Follow-up to the kill-DefaultModel + Create-handler-gate commits. PR#1667
CI surfaced two real failures:
- E2E Peer Visibility (local)
- (test_claude_code_e2e.sh, depending on which workflow)
Root cause: the MODEL_REQUIRED gate as initially written 422s every
external workspace too. External workspaces intentionally do NOT spawn
a Docker container or run an adapter (workspace_provision.go:497-498:
"external is a first-class runtime that intentionally does NOT spawn
a Docker container"). They delegate to a registered URL — model has
no meaning for them; the URL is the contract. Requiring it would 422
every legitimate "register my agent at https://..." flow.
Changes:
- `handlers.WorkspaceHandler.Create` (workspace.go): exempt external
workspaces from the MODEL_REQUIRED gate. Two spellings count as
external — `payload.External == true` and
`isExternalLikeRuntime(payload.Runtime)`. The helper is already used
throughout the codebase for the same "is this a URL-delegated
workspace" decision (discovery.go, registry.go, a2a_proxy_helpers.go,
plugins.go, workspace_restart.go).
- New test `TestCreate_ExternalRuntime_NoModel_OK`: pins the exemption.
Uses the test_api.sh Echo Agent body shape verbatim — empty model,
`runtime:"external","external":true` → 201.
- Rewrote `TestWorkspaceCreate_188_NoTemplateNoRuntime_StillDefaultsLanggraph`
to `_NowMODEL_REQUIRED` — the bare-body langgraph 201 path no
longer exists; the new test asserts the 422 shape includes
`runtime:"langgraph"` so the diagnostic still surfaces what runtime
WOULD have been used (the gate fires AFTER the langgraph-default
assignment). Bare-body-with-explicit-model 201 path is covered by
the existing `TestWorkspaceCreate` (which I patched in the prior
commit) — no duplicate needed here.
- `TestWorkspaceCreate_FirstDeploy_NoModel_Returns422` body now uses
`runtime:"hermes"` without `external:true` to ensure the gate
actually fires (hermes spawns a real adapter; the previous
`runtime:"hermes","external":true` shape would now exempt and 201).
- `tests/e2e/test_peer_visibility_mcp_local.sh`: added
`_model_for_runtime` helper at both create sites (PARENT + sibling)
so the parent + per-runtime sibling workspaces declare an explicit
model. Mapping mirrors what the deleted DefaultModel returned plus
the gpt-5.5 / kimi / minimax slugs used in production.
- `tests/e2e/test_claude_code_e2e.sh`: added `"model":"sonnet"` to
both POST bodies (ROOT + CHILD). Both use `runtime:"claude-code"`
which spawns a real adapter, not exempt.
Other E2E scripts inspected:
- `test_api.sh` — already uses `runtime:"external","external":true`,
exempt by the new gate. No change.
- `test_a2a_e2e.sh`:133, `test_activity_e2e.sh`:218,
`test_dev_mode.sh`:72, `test_workspace_abilities_e2e.sh`:93,99,
`test_comprehensive_e2e.sh`:78,105,134,139,144,
`test_notify_attachments_e2e.sh`:95, `test_mcp_stdio_staging.sh`:76 —
these create workspaces without an explicit runtime field. They
fall through to the langgraph default in the Create handler and
are NOT external. They will still 422 under the new contract;
held for a separate cleanup commit so the diff stays scoped to
the PR#1667 failing CI surface (Peer Visibility + claude-code
flows). Tracked for follow-up.
Verified:
go test -count=1 -short -timeout 300s ./internal/handlers/... \
./internal/models/... → OK
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>