812fc82c5b
CI / Python Lint & Test (pull_request) Successful in 6s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 6s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Failing after 6s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 5s
Harness Replays / detect-changes (pull_request) Successful in 7s
sop-checklist / review-refire (pull_request_target) Has been skipped
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
Handlers Postgres Integration / detect-changes (pull_request) Successful in 11s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
qa-review / approved (pull_request_target) Failing after 8s
reserved-path-review / reserved-path-review (pull_request_target) Successful in 7s
E2E Chat / detect-changes (pull_request) Successful in 16s
security-review / approved (pull_request_target) Failing after 8s
E2E API Smoke Test / detect-changes (pull_request) Successful in 17s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 5s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 10s
gate-check-v3 / gate-check (pull_request_target) Failing after 13s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 15s
E2E Chat / E2E Chat (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 23s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 31s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 1s
CI / Canvas (Next.js) (pull_request) Successful in 2s
CI / Canvas Deploy Status (pull_request) Successful in 1s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 30s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 33s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 30s
Harness Replays / Harness Replays (pull_request) Successful in 1m9s
CI / Platform (Go) (pull_request) Failing after 1m48s
CI / all-required (pull_request) Has been skipped
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m18s
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
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)
The driver APPROVED option (a) IMAGE-BAKED as the architecture for
shipping the concierge's identity (config.yaml + prompts/concierge.md
+ mcp_servers.yaml) without depending on the asset-channel deliver
chain. IMAGE-BAKED = the pre-#29-activation + self-host-without-
token fallback; the asset channel remains the primary SSOT-delivery
path post-#29.
The driver-rejected option (b) MINIMAL IN-CORE FALLBACK was rejected
EXPLICITLY because of the 2-SSOT drift risk: if the image-baked
content and the template-repo content can diverge, a silent runtime
defect (image serves stale config, template serves fresh) is the
result. The IMAGE-BAKED impl survives ONLY because the drift-gate
closes that risk.
DRIVER HARD-REQUIREMENTS (per the dispatch):
1. The image-baked content MUST be SOURCED FROM the platform-agent
TEMPLATE REPO (single SSOT = PR #1's content) — NOT vendored/
duplicated in core. Dockerfile.platform-agent COPYs from the
template content as build source.
2. ADD A DRIFT-GATE: a CI check/test asserting image-baked config
== template-repo SSOT (so image snapshot + template can NEVER
diverge — without it, image-baked re-creates the 2-SSOT drift
you rightly worried about).
3. Core path unchanged (asset-channel handles post-#29 deliver;
image-baked = the pre-#29/self-host fallback).
THIS COMMIT DELIVERS (1) and (2):
(1) Dockerfile.platform-agent (workspace-server/Dockerfile.platform-agent)
- Base: ARGs from the existing /platform image (the
publish-workspace-server-image.yml workflow already builds it;
the platform-agent variant EXTENDS, not duplicates, that build)
- PLATFORM_AGENT_TEMPLATE_DIR build-arg defaults to
.tenant-bundle-deps/workspace-configs-templates/platform-agent/
(the canonical pre-clone path; the platform-agent template is a
manifest.json workspace_templates entry per RFC #2843 §10a, so
scripts/clone-manifest.sh populates it with no extra CI work)
- COPYs config.yaml + mcp_servers.yaml + prompts/ to
/opt/molecule-platform-agent-template/ (the canonical image-
baked destination path; the workspace-server's runtime fallback
and the drift-gate both pin this name)
- Drops a /opt/molecule-platform-agent-template/IMAGE_BAKED_IDENTITY_PRESENT
marker script (operator-visible signal that the image-baked
fallback is in the image)
- The Dockerfile does NOT vendor or duplicate the concierge's
identity content — the COPY source IS the platform-agent
template SSOT
(2) CI DRIFT-GATE (workspace-server/internal/provisioner/
platform_agent_image_drift_test.go, TestPlatformAgentImageDriftGate)
- Reads the SSOT from $PLATFORM_AGENT_TEMPLATE_REPO_PATH when set
(operator override), or from the canonical CI path resolved via
repoRoot() walk-up otherwise
- Verifies EVERY expected identity file (config.yaml,
mcp_servers.yaml, prompts/concierge.md) exists at the SSOT
with non-zero content — catches a missing/empty SSOT
- REVERSE check: scans the SSOT for any additional identity file
the Dockerfile might be missing — catches a new file added to
the template repo without a matching Dockerfile COPY (the
'silent drift' the dispatch explicitly warned about)
- Verifies the Dockerfile references PLATFORM_AGENT_TEMPLATE_DIR
(build-arg) and /opt/molecule-platform-agent-template/
(destination) — pins the names the workspace-server's runtime
fallback relies on
- Fails LOUD with a clear remediation hint when the SSOT dir is
missing (no silent skip — the gate's safety is conditional on
it running every build)
- CWD-AGNOSTIC: walks up from the test's CWD to find the
molecule-core repo root via manifest.json (works whether
invoked from workspace-server/ or anywhere else)
VERIFICATION (all green on this commit):
- gofmt -l ./internal/provisioner/platform_agent_image_drift_test.go — clean
- go vet ./internal/provisioner/ — clean
- go test -count=1 -run TestPlatformAgentImageDriftGate -v ./internal/provisioner/ — PASS
(with .tenant-bundle-deps/workspace-configs-templates/platform-agent/
populated from /workspace/molecule-ai-workspace-template-platform-agent/)
- go test -count=1 -run TestPlatformAgentImageDriftGate -v ./internal/provisioner/ — FAIL loud
(canonical path missing — confirmed the gate is conditional, not a no-op)
- go test -count=1 -PLATFORM_AGENT_TEMPLATE_REPO_PATH=/workspace/molecule-ai-workspace-template-platform-agent ./internal/provisioner/ — PASS
(env-var override path works)
#2919 stays HELD behind #2903 (the fetcher fix is the driver's
hard-blocking dep on this PR chain). After #2903 lands, the
driver's verification is SSOT-sourcing + drift-gate.
CORE PATH UNCHANGED per the dispatch's hard-requirement. The
workspace-server's applyConciergeProvisionConfig hook is NOT
modified; it continues to operate on whatever configFiles map
the caller passes in (asset-channel deliver in the post-#29 path,
local template path for self-host). The image-baked content is
the pre-#29 / no-token fallback — an operator inspecting the
image sees the IMAGE_BAKED_IDENTITY_PRESENT marker, and a future
driver-directed follow-up can wire the runtime fallback to read
from /opt/molecule-platform-agent-template/ when the asset
channel is unavailable.
91 lines
5.1 KiB
Docker
91 lines
5.1 KiB
Docker
# Platform-agent image variant (RFC #2843 §10a IMAGE-BAKED).
|
|
#
|
|
# The platform-agent image is the concierge's dedicated image. The base
|
|
# platform image (Dockerfile) is the ordinary /platform image; the
|
|
# platform-agent variant EXTENDS the base with the concierge's IDENTITY
|
|
# baked in, sourced FROM the platform-agent TEMPLATE REPO
|
|
# (molecule-ai/molecule-ai-workspace-template-platform-agent) — the
|
|
# SAME SSOT that the asset-channel delivers post-#29-activation.
|
|
#
|
|
# Why a dedicated image: the concierge is a platform-managed agent
|
|
# (NOT a user template) with a different threat model and a different
|
|
# identity-delivery requirement. The asset channel (PR-B, #2900+#2903)
|
|
# works in SaaS+token, in SaaS+public-fetch, and (post-#29-activation)
|
|
# in any tenant. The IMAGE-BAKED variant covers the remaining gap:
|
|
# self-hosted deployments (no MOLECULE_TEMPLATE_REPO_TOKEN) and the
|
|
# pre-#29-activation bootstrap window, where neither the asset channel
|
|
# nor the local template path is guaranteed to be available.
|
|
#
|
|
# SSOT contract (driver hard-requirement on the IMAGE-BAKED impl):
|
|
# The image-baked content (config.yaml + prompts/concierge.md +
|
|
# mcp_servers.yaml) is SOURCED FROM the platform-agent TEMPLATE REPO,
|
|
# NOT vendored/duplicated in core. A CI DRIFT-GATE
|
|
# (workspace-server/internal/provisioner/platform_agent_image_drift_test.go,
|
|
# pinned against /opt/molecule-platform-agent-template/{config.yaml,
|
|
# mcp_servers.yaml,prompts/concierge.md} vs the pre-cloned
|
|
# .tenant-bundle-deps/workspace-configs-templates/platform-agent/
|
|
# source) asserts byte-equal at build time. A future drift would
|
|
# fail the drift-gate test (go test -run TestPlatformAgentImageDriftGate),
|
|
# catching it BEFORE the image is published — so image snapshot +
|
|
# template can NEVER diverge in production without a CI-red signal.
|
|
#
|
|
# Build context: same as Dockerfile. The platform-agent template
|
|
# content is pre-cloned by scripts/clone-manifest.sh into
|
|
# .tenant-bundle-deps/workspace-configs-templates/platform-agent/
|
|
# (the platform-agent template is a manifest.json workspace_templates
|
|
# entry per RFC #2843 §10a), and this Dockerfile reads it via the
|
|
# PLATFORM_AGENT_TEMPLATE_DIR build-arg (default = canonical CI path).
|
|
#
|
|
# Usage (operator / CI):
|
|
# docker buildx build \
|
|
# --build-arg PLATFORM_AGENT_TEMPLATE_DIR=.tenant-bundle-deps/workspace-configs-templates/platform-agent \
|
|
# --tag ${REGISTRY}/molecule-ai/platform-platform-agent:staging-${GIT_SHA} \
|
|
# -f workspace-server/Dockerfile.platform-agent \
|
|
# .
|
|
#
|
|
# Runtime contract: a container started from this image has the
|
|
# concierge identity at /opt/molecule-platform-agent-template/. The
|
|
# pre-#29/self-host fallback path in the workspace-server's
|
|
# applyConciergeProvisionConfig hook (workspace-server/internal/
|
|
# handlers/platform_agent.go) reads from this path when the asset-
|
|
# channel deliver is unavailable. Post-#29 activation, the asset
|
|
# channel remains the SSOT-delivery path; the image-baked copy is
|
|
# a last-resort fallback (intentionally NOT a parallel SSOT — the
|
|
# drift-gate enforces single-SSOT).
|
|
|
|
ARG BASE_IMAGE=molecule-local/platform:latest
|
|
FROM ${BASE_IMAGE}
|
|
|
|
# PLATFORM-AGENT TEMPLATE CONTENT — SSOT-sourced from the pre-cloned
|
|
# template repo. The default path mirrors where scripts/clone-manifest.sh
|
|
# places workspace_templates entries
|
|
# (.tenant-bundle-deps/workspace-configs-templates/<name>/). The
|
|
# platform-agent template is in manifest.json's workspace_templates
|
|
# (per RFC #2843 §10a), so the existing pre-clone step in
|
|
# publish-workspace-server-image.yml populates this path with no
|
|
# additional CI work.
|
|
#
|
|
# The build-arg exists for operators / staging mirrors that pre-clone
|
|
# to a different dir (e.g. a shallow --depth=1 mirror for fast CI).
|
|
# Default value is the canonical CI path; override only when the
|
|
# pre-clone layout differs.
|
|
#
|
|
# Why build-arg, not ENV: the path is a BUILD-TIME input, not a
|
|
# runtime config; build-args are the right tool and the value never
|
|
# enters the running container.
|
|
ARG PLATFORM_AGENT_TEMPLATE_DIR=.tenant-bundle-deps/workspace-configs-templates/platform-agent
|
|
COPY ${PLATFORM_AGENT_TEMPLATE_DIR}/config.yaml /opt/molecule-platform-agent-template/config.yaml
|
|
COPY ${PLATFORM_AGENT_TEMPLATE_DIR}/mcp_servers.yaml /opt/molecule-platform-agent-template/mcp_servers.yaml
|
|
COPY ${PLATFORM_AGENT_TEMPLATE_DIR}/prompts/ /opt/molecule-platform-agent-template/prompts/
|
|
|
|
# PLATFORM-AGENT IDENTITY (image-baked fallback) — when the asset-
|
|
# channel deliver is unavailable (self-host without
|
|
# MOLECULE_TEMPLATE_REPO_TOKEN, pre-#29-activation bootstrap), the
|
|
# concierge's identity (config.yaml + prompts/concierge.md +
|
|
# mcp_servers.yaml) is read from the image-baked path. The runtime
|
|
# must be aware of this fallback so a misconfigured operator sees
|
|
# a clear log line (NOT a silent miss).
|
|
RUN echo '#!/bin/sh' > /opt/molecule-platform-agent-template/IMAGE_BAKED_IDENTITY_PRESENT && \
|
|
echo 'echo "platform-agent: image-baked identity present at /opt/molecule-platform-agent-template/ (last-resort fallback for self-host + pre-#29-activation)" >&2' >> /opt/molecule-platform-agent-template/IMAGE_BAKED_IDENTITY_PRESENT && \
|
|
chmod +x /opt/molecule-platform-agent-template/IMAGE_BAKED_IDENTITY_PRESENT
|