test(provision): contract SSOT + producer-pin for the core→CP provision request #3058

Merged
devops-engineer merged 2 commits from feat/provision-request-contract into main 2026-06-19 04:20:37 +00:00
Member

What

Adds a consumer-driven contract guarding the core→control-plane provision request wire shape, so the two duplicated structs (core cpProvisionRequest ↔ CP wsProvisionRequest, in separate repos) can no longer silently drift.

  • provision_request.contract.json — the SSOT field list for POST /cp/workspaces/provision, each field tagged cp_consumes true/false (+ a required note for dead fields).
  • provision_request_contract_test.goproducer pin: cpProvisionRequest's JSON tags must equal the contract exactly; dead (cp_consumes:false) fields must justify themselves.

Why

core and the CP have no shared compile unit, so a field added on the sender does nothing if the receiver lacks it. That is exactly how template_assets was silently dropped — core fetches+sends config.yaml/prompts on that channel, the deployed CP has no template_assets field, so it's ignored, and SaaS boxes fall back to config_files (the restart re-stub class — see project_saas_restart_re_stub_config / RFC #2843). No test caught it because the only thing exercising the seam was post-merge black-box staging e2e.

The contract records today's reality honestly: template_assets is marked cp_consumes:false with a note flagging the dead channel (cleanup tracked separately) — converting an invisible bug into an explicit, reviewed decision.

Guard behavior (verified)

  • Passes on the current struct/contract.
  • Fails when a wire field is added without updating the contract (tested by injecting a drift field → caught).
  • Pure unit test, no infra — runs in normal Go CI.

Companion (separate PR, molecule-controlplane)

A consumer-completeness test: wsProvisionRequest must have a json tag for every cp_consumes:true field in this contract. Together the two guards make the template_assets failure class impossible to reintroduce silently.

Context

Step 1 of the local-test parity plan (catch contract drift at the seam, in-repo, on the PR that introduces it — instead of via deployed staging e2e). Follow-ups: CP consumer test; a local/docker WorkspaceProvisioner backend with SaaS parity.

🤖 Generated with Claude Code

## What Adds a **consumer-driven contract** guarding the core→control-plane provision request wire shape, so the two duplicated structs (core `cpProvisionRequest` ↔ CP `wsProvisionRequest`, in **separate repos**) can no longer silently drift. - `provision_request.contract.json` — the SSOT field list for `POST /cp/workspaces/provision`, each field tagged `cp_consumes` true/false (+ a required `note` for dead fields). - `provision_request_contract_test.go` — **producer pin**: `cpProvisionRequest`'s JSON tags must equal the contract exactly; dead (`cp_consumes:false`) fields must justify themselves. ## Why core and the CP have no shared compile unit, so a field added on the sender does nothing if the receiver lacks it. That is **exactly how `template_assets` was silently dropped** — core fetches+sends config.yaml/prompts on that channel, the deployed CP has no `template_assets` field, so it's ignored, and SaaS boxes fall back to `config_files` (the restart re-stub class — see `project_saas_restart_re_stub_config` / RFC #2843). No test caught it because the only thing exercising the seam was post-merge black-box staging e2e. The contract records today's reality honestly: `template_assets` is marked `cp_consumes:false` with a note flagging the dead channel (cleanup tracked separately) — converting an invisible bug into an explicit, reviewed decision. ## Guard behavior (verified) - Passes on the current struct/contract. - **Fails** when a wire field is added without updating the contract (tested by injecting a drift field → caught). - Pure unit test, no infra — runs in normal Go CI. ## Companion (separate PR, molecule-controlplane) A **consumer-completeness** test: `wsProvisionRequest` must have a json tag for every `cp_consumes:true` field in this contract. Together the two guards make the template_assets failure class impossible to reintroduce silently. ## Context Step 1 of the local-test parity plan (catch contract drift at the seam, in-repo, on the PR that introduces it — instead of via deployed staging e2e). Follow-ups: CP consumer test; a local/docker `WorkspaceProvisioner` backend with SaaS parity. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
core-devops added 1 commit 2026-06-19 04:06:10 +00:00
test(provision): contract SSOT + producer-pin for the core→CP provision request
CI / Python Lint & Test (pull_request) Successful in 5s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 5s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 8s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 9s
Harness Replays / detect-changes (pull_request) Successful in 11s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 8s
CI / Detect changes (pull_request) Successful in 19s
E2E API Smoke Test / detect-changes (pull_request) Successful in 19s
sop-checklist / review-refire (pull_request_target) Has been skipped
E2E Chat / detect-changes (pull_request) Successful in 23s
PR Diff Guard / PR diff guard (pull_request) Successful in 16s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 17s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 4s
reserved-path-review / reserved-path-review (pull_request_target) Successful in 9s
qa-review / approved (pull_request_target) Failing after 9s
sop-checklist / na-declarations (pull_request) N/A: (none)
CI / Shellcheck (E2E scripts) (pull_request) Successful in 3s
CI / Canvas (Next.js) (pull_request) Successful in 3s
security-review / approved (pull_request_target) Failing after 12s
CI / Canvas Deploy Status (pull_request) Successful in 1s
gate-check-v3 / gate-check (pull_request_target) Failing after 15s
template-delivery-e2e / detect-changes (pull_request) Successful in 17s
sop-checklist / all-items-acked (pull_request_target) Successful in 13s
E2E Chat / E2E Chat (pull_request) Successful in 14s
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 10s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 43s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 47s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 32s
Harness Replays / Harness Replays (pull_request) Successful in 1m23s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m23s
CI / Platform (Go) (pull_request) Has been cancelled
CI / all-required (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Platform Boot (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge user_tasks (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Workspace Requests (core#2606) (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Creates Workspace (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge (compile+skip) (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Platform Agent (pull_request) Waiting to run
sop-checklist / all-items-acked (pull_request) Compensated by status-reaper (non-required pull_request/pull_request_review governance shadow overridden by successful pull_request_target status; see .gitea/scripts/status-reaper.py)
9836de23be
Core and the control-plane define the /cp/workspaces/provision request as two
DUPLICATED structs in two repos with no shared compile unit, so a field added on
the sender silently does nothing if the receiver lacks it. That is exactly how
`template_assets` was dropped (config.yaml/prompts never materialized on SaaS via
that channel) — RFC #2843 / project_saas_restart_re_stub_config.

Adds provision_request.contract.json as the SSOT wire shape, and a producer-pin
test asserting cpProvisionRequest's JSON tags == the contract exactly. Adding or
removing a wire field now FAILS until the contract is updated deliberately, with
an explicit cp_consumes flag per field; cp_consumes:false (a dead sent-but-ignored
field, e.g. template_assets today) must carry a justifying note. A CP-side
companion test (molecule-controlplane) will enforce that every cp_consumes:true
field is present on wsProvisionRequest.

Step 1 of the local-test parity plan: catch contract drift at the seam, in-repo,
on the PR that introduces it — instead of via post-merge black-box staging e2e.

Verified: passes on the current struct; fails when a drift field is injected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
core-devops added 1 commit 2026-06-19 04:09:21 +00:00
docs(contract): repo-explicit guard prose so the byte-identical CP copy reads correctly
CI / Python Lint & Test (pull_request) Successful in 5s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 6s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
sop-checklist / review-refire (pull_request_target) Has been skipped
Harness Replays / detect-changes (pull_request) Successful in 8s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 12s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 8s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
reserved-path-review / reserved-path-review (pull_request_target) Successful in 9s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 13s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 16s
CI / Detect changes (pull_request) Successful in 18s
E2E Chat / detect-changes (pull_request) Successful in 17s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 14s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 6s
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, local-postgres-e2
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 3s
sop-checklist / na-declarations (pull_request) N/A: (none)
PR Diff Guard / PR diff guard (pull_request) Successful in 17s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
gate-check-v3 / gate-check (pull_request_target) Failing after 15s
sop-checklist / all-items-acked (pull_request_target) Successful in 11s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 3s
CI / Canvas (Next.js) (pull_request) Successful in 2s
CI / Canvas Deploy Status (pull_request) Successful in 2s
template-delivery-e2e / detect-changes (pull_request) Successful in 18s
E2E Chat / E2E Chat (pull_request) Successful in 4s
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 2s
E2E API Smoke Test / detect-changes (pull_request) Successful in 32s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 34s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 32s
Harness Replays / Harness Replays (pull_request) Successful in 1m22s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Platform Boot (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge user_tasks (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Workspace Requests (core#2606) (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Creates Workspace (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge (compile+skip) (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Platform Agent (pull_request) Has been cancelled
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m19s
CI / Platform (Go) (pull_request) Successful in 2m55s
CI / all-required (pull_request) Successful in 4s
reserved-path-review / reserved-path-review (pull_request_review) Successful in 7s
qa-review / approved (pull_request_target) Approved via pull_request_review trigger
security-review / approved (pull_request_target) Approved via pull_request_review trigger
qa-review / approved (pull_request_review) Successful in 11s
security-review / approved (pull_request_review) Successful in 11s
audit-force-merge / audit (pull_request_target) Successful in 10s
80f8e0e86e
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent-reviewer-cr2 approved these changes 2026-06-19 04:15:12 +00:00
agent-reviewer-cr2 left a comment
Member

APPROVED. Five-axis review complete on current head 80f8e0e8.

Correctness: the new provision_request.contract.json honestly captures the current core→CP wire shape, including template_assets as cp_consumes:false with a note documenting the dead channel. The producer-side test reflects cpProvisionRequest JSON tags and requires exact equality with the contract field keys, so an added drift field or removed field fails until the contract is deliberately updated. Dead fields must carry a note, preventing silent sent-but-ignored channels.

Robustness/security/performance/readability: pure unit-test/contract addition; no runtime behavior, auth, or provision logic changes. The failure messages are specific enough to guide future producer/consumer updates. Visible core unit/build/all-required contexts are green; staging SaaS contexts are failing, but this PR is pure contract-test and those appear unrelated to this unit guard. No code blockers found.

APPROVED. Five-axis review complete on current head 80f8e0e8. Correctness: the new provision_request.contract.json honestly captures the current core→CP wire shape, including template_assets as cp_consumes:false with a note documenting the dead channel. The producer-side test reflects cpProvisionRequest JSON tags and requires exact equality with the contract field keys, so an added drift field or removed field fails until the contract is deliberately updated. Dead fields must carry a note, preventing silent sent-but-ignored channels. Robustness/security/performance/readability: pure unit-test/contract addition; no runtime behavior, auth, or provision logic changes. The failure messages are specific enough to guide future producer/consumer updates. Visible core unit/build/all-required contexts are green; staging SaaS contexts are failing, but this PR is pure contract-test and those appear unrelated to this unit guard. No code blockers found.
devops-engineer merged commit 99d7d1316b into main 2026-06-19 04:20:37 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#3058