fix(concierge): consolidate identity prompt on system-prompt.md (kill filename split) #3239

Closed
core-devops wants to merge 1 commits from fix/concierge-prompt-filename-ssot into main
Member

Problem (found live on the test2 concierge)

The platform-agent template ships its system prompt as prompts/concierge.md (per config.yamlprompt_files), but core assumed system-prompt.md in two places. That filename split left every prod concierge with a broken identity:

  1. Name never substitutedsubstituteConciergeName keyed on configFiles["system-prompt.md"], which the template doesn't deliver → {{CONCIERGE_NAME}} stayed literal in the running system prompt (verified on test2: grep -c CONCIERGE_NAME = 1).
  2. Spurious restart every bootconciergeIdentityPresent probed only /configs/system-prompt.md to decide "seeded vs vanilla". A correctly-seeded concierge (has prompts/concierge.md, no system-prompt.md) read as vanillaMaybeProvisionPlatformAgentOnBoot logged "MISSING concierge identity" and restarted it on every CP boot.

Fix — consolidate on system-prompt.md (the SSOT filename ordinary workspaces, the runtime fallback, and core's probe already share), and make core robust so it can't silently re-break

  • substituteConciergeName now follows the {{CONCIERGE_NAME}} placeholder across all delivered config files (no-op where absent) — no hardcoded filename.
  • conciergeIdentityPresent checks the runtime's full candidate set (system-prompt.md, then legacy prompts/concierge.md) → transition-safe for existing concierges until they re-provision.

Companion

Template PR flips molecule-ai-workspace-template-platform-agent to ship system-prompt.md (so there is one filename end-to-end).

Tests

Two prove-fail tests (fail against pre-fix source, pass after):

  • substitution in the real prompts/concierge.md layout
  • identity probe recognises identity in either filename (incl. the legacy transition case)
    Full internal/handlers suite green; go build/go vet clean.
## Problem (found live on the `test2` concierge) The platform-agent template ships its system prompt as **`prompts/concierge.md`** (per `config.yaml` → `prompt_files`), but core assumed **`system-prompt.md`** in two places. That filename split left every prod concierge with a broken identity: 1. **Name never substituted** — `substituteConciergeName` keyed on `configFiles["system-prompt.md"]`, which the template doesn't deliver → `{{CONCIERGE_NAME}}` stayed literal in the running system prompt (verified on `test2`: `grep -c CONCIERGE_NAME` = 1). 2. **Spurious restart every boot** — `conciergeIdentityPresent` probed only `/configs/system-prompt.md` to decide "seeded vs vanilla". A correctly-seeded concierge (has `prompts/concierge.md`, no `system-prompt.md`) read as *vanilla* → `MaybeProvisionPlatformAgentOnBoot` logged "MISSING concierge identity" and restarted it on every CP boot. ## Fix — consolidate on `system-prompt.md` (the SSOT filename ordinary workspaces, the runtime fallback, and core's probe already share), and make core robust so it can't silently re-break - `substituteConciergeName` now follows the **`{{CONCIERGE_NAME}}` placeholder** across *all* delivered config files (no-op where absent) — no hardcoded filename. - `conciergeIdentityPresent` checks the runtime's **full candidate set** (`system-prompt.md`, then legacy `prompts/concierge.md`) → transition-safe for existing concierges until they re-provision. ## Companion Template PR flips `molecule-ai-workspace-template-platform-agent` to ship `system-prompt.md` (so there is one filename end-to-end). ## Tests Two **prove-fail** tests (fail against pre-fix source, pass after): - substitution in the real `prompts/concierge.md` layout - identity probe recognises identity in either filename (incl. the legacy transition case) Full `internal/handlers` suite green; `go build`/`go vet` clean.
core-devops added 1 commit 2026-06-24 18:44:21 +00:00
fix(concierge): consolidate identity prompt on system-prompt.md (kill filename split)
CI / Python Lint & Test (pull_request) Successful in 6s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 8s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 7s
Block integration-tester contamination artifacts / Block staging-trigger / invalid manifest contamination (pull_request) Successful in 9s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 7s
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) / E2E Peer Visibility (local) (pull_request) Has been skipped
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 14s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 7s
sop-checklist / all-items-acked (pull_request) acked: 0/9 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +6 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 11s
gate-check-v3 / gate-check (pull_request_target) Failing after 14s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 23s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 20s
PR Diff Guard / PR diff guard (pull_request) Successful in 20s
E2E Chat / detect-changes (pull_request) Successful in 28s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 31s
E2E API Smoke Test / detect-changes (pull_request) Successful in 33s
E2E Chat / E2E Chat (pull_request) Successful in 4s
template-delivery-e2e / detect-changes (pull_request) Successful in 28s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
CI / Canvas (Next.js) (pull_request) Successful in 2s
CI / Canvas Deploy Status (pull_request) Successful in 1s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 39s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 51s
Harness Replays / Harness Replays (pull_request) Successful in 1m26s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m20s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 2m3s
CI / Platform (Go) (pull_request) Successful in 3m31s
CI / all-required (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 6m44s
qa-review / approved (pull_request_target) Approved via pull_request_review trigger
qa-review / approved (pull_request_review) Successful in 9s
reserved-path-review / reserved-path-review (pull_request_target) Approved via pull_request_review trigger
security-review / approved (pull_request_target) Approved via pull_request_review trigger
reserved-path-review / reserved-path-review (pull_request_review) Successful in 10s
security-review / approved (pull_request_review) Successful in 11s
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) / Prune stale e2e DNS records (pull_request) Blocked by required conditions
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
E2E Staging SaaS (full lifecycle) / E2E Staging Plugin Install Lifecycle (pull_request) Waiting to run
audit-force-merge / audit (pull_request_target) Has been skipped
8c65711670
The platform-agent template shipped its system prompt as prompts/concierge.md
(config.yaml prompt_files), but core assumed system-prompt.md in two places — a
divergence that left every prod concierge with a broken identity:

1. substituteConciergeName keyed on configFiles["system-prompt.md"] → the real
   prompts/concierge.md never had {{CONCIERGE_NAME}} filled → the concierge ran
   with a literal placeholder (observed live on test2).
2. conciergeIdentityPresent probed only /configs/system-prompt.md → a correctly
   seeded concierge (prompts/concierge.md, no system-prompt.md) read as "vanilla"
   → MaybeProvisionPlatformAgentOnBoot restarted it on every CP boot.

Consolidate on system-prompt.md (the SSOT filename ordinary workspaces, the
runtime fallback, and core's probe already share) and make core robust so this
cannot silently re-break:

- substituteConciergeName now follows the {{CONCIERGE_NAME}} placeholder across
  ALL delivered config files, not a hardcoded filename (no-op where absent).
- conciergeIdentityPresent checks the runtime's full candidate set
  (system-prompt.md, then legacy prompts/concierge.md) → transition-safe for
  existing concierges until they re-provision.

Companion template change flips molecule-ai-workspace-template-platform-agent to
ship system-prompt.md.

Prove-fail tests added for both paths (fail against pre-fix source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
core-devops requested review from agent-reviewer-cr2 2026-06-24 19:35:30 +00:00
core-devops requested review from agent-researcher 2026-06-24 19:35:30 +00:00
core-devops requested review from core-security 2026-06-24 19:35:31 +00:00
molecule-code-reviewer approved these changes 2026-06-24 23:22:25 +00:00
molecule-code-reviewer left a comment
Member

APPROVE — genuine review. substituteConciergeName loop mutates map VALUES only (Go-spec-safe under range, never adds/removes keys), nil-map guarded; conciergeIdentityPresent candidate-set probe preserves the fail-closed re-seed default. Both prove-fail tests confirmed to FAIL against merge-base 14a4bd04 and pass on branch. No new regression (only pre-existing network/manifest test failures). Companion of #3246.

APPROVE — genuine review. substituteConciergeName loop mutates map VALUES only (Go-spec-safe under range, never adds/removes keys), nil-map guarded; conciergeIdentityPresent candidate-set probe preserves the fail-closed re-seed default. Both prove-fail tests confirmed to FAIL against merge-base 14a4bd04 and pass on branch. No new regression (only pre-existing network/manifest test failures). Companion of #3246.
core-security approved these changes 2026-06-24 23:22:27 +00:00
core-security left a comment
Member

APPROVE (security). {{CONCIERGE_NAME}} substitution is value-only within the already-allowlisted config set (cannot add files). Name origin payload.Name is validated — rejects \n/\r and YAML-special {}[]|>*&!, len<=255 — blocking placeholder/YAML injection; blast radius self-scoped to the org own concierge. No fail-closed gate weakened; no secrets in diff.

APPROVE (security). {{CONCIERGE_NAME}} substitution is value-only within the already-allowlisted config set (cannot add files). Name origin payload.Name is validated — rejects \n/\r and YAML-special {}[]|>*&!, len<=255 — blocking placeholder/YAML injection; blast radius self-scoped to the org own concierge. No fail-closed gate weakened; no secrets in diff.
agent-reviewer-cr2 approved these changes 2026-06-24 23:29:26 +00:00
agent-reviewer-cr2 left a comment
Member

qa 5-axis review on current head 8c65711670: APPROVE.

Correctness: concierge name substitution now follows the {{CONCIERGE_NAME}} placeholder across delivered config files instead of assuming system-prompt.md, which covers the real prompts/concierge.md layout and future prompt splits. The boot identity probe checks both system-prompt.md and legacy prompts/concierge.md, so already-seeded concierges are not misclassified as vanilla during transition. Robustness: substitution is idempotent and no-op for files without the marker; probe failure still falls back to the safe reseed path. Security: no auth/secret expansion and no new file read surface beyond fixed /configs prompt candidates. Performance: small map scan and two bounded ExecRead probes. Readability: comments and prove-fail tests clearly describe the filename-split regression and transition behavior.

qa 5-axis review on current head 8c657116700172678a5afb17e9f634f37b4e08ad: APPROVE. Correctness: concierge name substitution now follows the {{CONCIERGE_NAME}} placeholder across delivered config files instead of assuming system-prompt.md, which covers the real prompts/concierge.md layout and future prompt splits. The boot identity probe checks both system-prompt.md and legacy prompts/concierge.md, so already-seeded concierges are not misclassified as vanilla during transition. Robustness: substitution is idempotent and no-op for files without the marker; probe failure still falls back to the safe reseed path. Security: no auth/secret expansion and no new file read surface beyond fixed /configs prompt candidates. Performance: small map scan and two bounded ExecRead probes. Readability: comments and prove-fail tests clearly describe the filename-split regression and transition behavior.
Member

Closing as superseded by #3248 (merged). #3239's two goals are already on main via #3248: conciergeIdentityPresent now uses placeholder-absence, and substituteConciergeName was refactored to (prompt,name). The concierge identity-substitution substance is delivered end-to-end by #3246 (asset-channel allowlist) + #3248 (subst/probe) + template #7 (ships system-prompt.md) + runtime #177 (base owns the prompt). This PR is mergeable=false (behind main after the de-bake wave) and re-implementing the same fix; closing to avoid a redundant rebase. Live-confirmed by the staging create_workspace e2e.

Closing as **superseded by #3248** (merged). #3239's two goals are already on `main` via #3248: `conciergeIdentityPresent` now uses placeholder-absence, and `substituteConciergeName` was refactored to (prompt,name). The concierge identity-substitution substance is delivered end-to-end by #3246 (asset-channel allowlist) + #3248 (subst/probe) + template #7 (ships system-prompt.md) + runtime #177 (base owns the prompt). This PR is mergeable=false (behind main after the de-bake wave) and re-implementing the same fix; closing to avoid a redundant rebase. Live-confirmed by the staging create_workspace e2e.
Some optional checks failed
CI / Python Lint & Test (pull_request) Successful in 6s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 8s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 7s
Block integration-tester contamination artifacts / Block staging-trigger / invalid manifest contamination (pull_request) Successful in 9s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 7s
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) / E2E Peer Visibility (local) (pull_request) Has been skipped
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
Required
Details
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 14s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 7s
sop-checklist / all-items-acked (pull_request) acked: 0/9 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +6 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 11s
gate-check-v3 / gate-check (pull_request_target) Failing after 14s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 23s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 20s
PR Diff Guard / PR diff guard (pull_request) Successful in 20s
E2E Chat / detect-changes (pull_request) Successful in 28s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 31s
E2E API Smoke Test / detect-changes (pull_request) Successful in 33s
E2E Chat / E2E Chat (pull_request) Successful in 4s
template-delivery-e2e / detect-changes (pull_request) Successful in 28s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
CI / Canvas (Next.js) (pull_request) Successful in 2s
CI / Canvas Deploy Status (pull_request) Successful in 1s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 39s
Required
Details
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 51s
Harness Replays / Harness Replays (pull_request) Successful in 1m26s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m20s
Required
Details
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 2m3s
CI / Platform (Go) (pull_request) Successful in 3m31s
CI / all-required (pull_request) Successful in 4s
Required
Details
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 6m44s
qa-review / approved (pull_request_target) Approved via pull_request_review trigger
Required
qa-review / approved (pull_request_review) Successful in 9s
reserved-path-review / reserved-path-review (pull_request_target) Approved via pull_request_review trigger
Required
security-review / approved (pull_request_target) Approved via pull_request_review trigger
Required
reserved-path-review / reserved-path-review (pull_request_review) Successful in 10s
security-review / approved (pull_request_review) Successful in 11s
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) / Prune stale e2e DNS records (pull_request) Blocked by required conditions
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
E2E Staging SaaS (full lifecycle) / E2E Staging Plugin Install Lifecycle (pull_request) Waiting to run
audit-force-merge / audit (pull_request_target) Has been skipped

Pull request closed

Sign in to join this conversation.
5 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#3239