fix(concierge): provision kind=platform with the platform-agent template (#30/#2970) #3029
@@ -0,0 +1,27 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.moleculesai.app/molecule-ai/molecule-core/workspace-server/internal/models"
|
||||
)
|
||||
|
||||
// RFC §5.7 / #30: a kind='platform' concierge with no explicit template must
|
||||
// resolve to the platform-agent template (its identity), not the generic
|
||||
// claude-code-default config.
|
||||
func TestConciergeTemplateOrDefault(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, kind, template, want string
|
||||
}{
|
||||
{"platform empty -> platform-agent", models.KindPlatform, "", "platform-agent"},
|
||||
{"platform blank -> platform-agent", models.KindPlatform, " ", "platform-agent"},
|
||||
{"platform explicit kept", models.KindPlatform, "custom", "custom"},
|
||||
{"workspace empty stays empty", "workspace", "", ""},
|
||||
{"workspace seo-agent kept", "workspace", "seo-agent", "seo-agent"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := conciergeTemplateOrDefault(c.kind, c.template); got != c.want {
|
||||
t.Errorf("%s: conciergeTemplateOrDefault(%q,%q)=%q want %q", c.name, c.kind, c.template, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -587,9 +587,9 @@ func installPlatformAgent(ctx context.Context, database *sql.DB, platformID, nam
|
||||
// provisioning state. The integration tests use unique names per fixture
|
||||
// to avoid cross-test collision (CR-A RC 10610).
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO workspaces (id, name, kind, tier, status, runtime, parent_id)
|
||||
VALUES ($1, $2, 'platform', 0, 'offline', 'claude-code', NULL)
|
||||
ON CONFLICT (id) DO UPDATE SET kind = 'platform', runtime = 'claude-code', parent_id = NULL
|
||||
INSERT INTO workspaces (id, name, kind, tier, status, runtime, parent_id, template)
|
||||
VALUES ($1, $2, 'platform', 0, 'offline', 'claude-code', NULL, 'platform-agent')
|
||||
ON CONFLICT (id) DO UPDATE SET kind = 'platform', runtime = 'claude-code', parent_id = NULL, template = 'platform-agent'
|
||||
`, platformID, name); err != nil {
|
||||
return fmt.Errorf("upsert platform agent: %w", err)
|
||||
}
|
||||
|
||||
@@ -286,6 +286,25 @@ func workspaceMemoryNamespace(workspaceID string) string {
|
||||
return fmt.Sprintf("workspace:%s", workspaceID)
|
||||
}
|
||||
|
||||
// conciergeTemplateOrDefault forces the platform-agent template for a
|
||||
// kind='platform' concierge when no explicit template is set. RFC §5.7: the
|
||||
// concierge identity (config.yaml + prompts/concierge.md + mcp_servers.yaml) is
|
||||
// delivered "like any other runtime template" via the platform-agent template
|
||||
// entry. But the platform-agent workspace row was upserted with no `template`
|
||||
// (platform_agent.go installPlatformAgent), so payload.Template was empty and
|
||||
// the identity resolved to the GENERIC claude-code-default config — the
|
||||
// concierge booted online but with no persona ("doesn't know it's the platform
|
||||
// agent", #30/#2970). Forcing "platform-agent" here makes the asset fetcher pull
|
||||
// the concierge identity for every concierge provision/restart, new or existing,
|
||||
// without depending on the row's template column being backfilled. An explicit
|
||||
// template (set by a future caller) still wins.
|
||||
func conciergeTemplateOrDefault(kind, template string) string {
|
||||
if kind == models.KindPlatform && strings.TrimSpace(template) == "" {
|
||||
return "platform-agent"
|
||||
}
|
||||
return template
|
||||
}
|
||||
|
||||
func (h *WorkspaceHandler) buildProvisionerConfig(
|
||||
ctx context.Context,
|
||||
workspaceID, templatePath string,
|
||||
@@ -416,7 +435,7 @@ func (h *WorkspaceHandler) buildProvisionerConfig(
|
||||
// not duplicated across first-provision + restart paths.
|
||||
// nil fetcher = "no fetcher wired" (self-host default;
|
||||
// falls through to the local TemplatePath path).
|
||||
TemplateIdentity: templateIdentityForTemplateOrRuntime(payload.Template, payload.Runtime),
|
||||
TemplateIdentity: templateIdentityForTemplateOrRuntime(conciergeTemplateOrDefault(kind, payload.Template), payload.Runtime),
|
||||
TemplateAssetFetcher: h.giteaTemplateFetcher,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user