From 7f327758d82d3bfcc4ee353de2eb9252cd51c8cf Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Fri, 6 Nov 2026 01:01:13 +0000 Subject: [PATCH] Migrate composed PromptKit consumer --- docs/roadmap/implementation.md | 4 +- .../examples/composed/fixtures/after.yml | 2 - .../examples/composed/fixtures/before.yml | 2 - .../ckprompt/examples/composed/main.go | 32 +++++---- .../ckprompt/examples/composed/main_test.go | 68 ++++++++++++++++--- 5 files changed, 79 insertions(+), 29 deletions(-) diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index a1e3cee..fae5839 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # PromptKit v0.18.0 implementation plan -Status: Stages 1–4 complete; Stages 5–7 remain. +Status: Stages 1–5 complete; Stages 6–7 remain. ## Authority and execution @@ -177,6 +177,8 @@ package tests to guide corrections; full final validation is Stage 7. ## Stage 5: Migrate the composed consumer and its tests +**Complete.** The composed suite passes with identified lists, the native bundle, and explicit local smoke targets; the aggregate report was inspected. + 1. In `examples/composed/main.go`, convert the root resources and `field-notes` collection resources assembled by `loadPromptEngine` to identified lists. Preserve qualified stored references such as `field-notes/demo` and diff --git a/integrations/ckprompt/examples/composed/fixtures/after.yml b/integrations/ckprompt/examples/composed/fixtures/after.yml index e4cec20..e1c77fd 100644 --- a/integrations/ckprompt/examples/composed/fixtures/after.yml +++ b/integrations/ckprompt/examples/composed/fixtures/after.yml @@ -2,11 +2,9 @@ default_campaign: autumn campaigns: spring: prompt_id: briefing - prompt_version: "1" profile_id: spring model: spring-model-after autumn: prompt_id: briefing - prompt_version: "1" profile_id: autumn model: autumn-model diff --git a/integrations/ckprompt/examples/composed/fixtures/before.yml b/integrations/ckprompt/examples/composed/fixtures/before.yml index 2bd5742..9fc0c68 100644 --- a/integrations/ckprompt/examples/composed/fixtures/before.yml +++ b/integrations/ckprompt/examples/composed/fixtures/before.yml @@ -2,11 +2,9 @@ default_campaign: spring campaigns: spring: prompt_id: briefing - prompt_version: "1" profile_id: spring model: spring-model autumn: prompt_id: briefing - prompt_version: "1" profile_id: autumn model: autumn-model diff --git a/integrations/ckprompt/examples/composed/main.go b/integrations/ckprompt/examples/composed/main.go index a015713..798dfe1 100644 --- a/integrations/ckprompt/examples/composed/main.go +++ b/integrations/ckprompt/examples/composed/main.go @@ -8,18 +8,17 @@ import ( "os" "strconv" - configkit "go.maximumdirect.net/configkit" - promptkit "go.maximumdirect.net/promptkit" "go.maximumdirect.net/ckconfig" "go.maximumdirect.net/ckprompt" commandkit "go.maximumdirect.net/commandkit" + configkit "go.maximumdirect.net/configkit" + promptkit "go.maximumdirect.net/promptkit" ) type campaign struct { - PromptID string `yaml:"prompt_id"` - PromptVersion string `yaml:"prompt_version"` - ProfileID string `yaml:"profile_id"` - Model string `yaml:"model"` + PromptID string `yaml:"prompt_id"` + ProfileID string `yaml:"profile_id"` + Model string `yaml:"model"` } type appConfiguration struct { @@ -90,8 +89,8 @@ func loadApplicationConfig(ctx context.Context, selection ckconfig.Selection, in source := configkit.Mapping("composed application settings", map[string]any{ "default_campaign": "spring", "campaigns": map[string]any{ - "spring": map[string]any{"prompt_id": "briefing", "prompt_version": "1", "profile_id": "spring", "model": "spring-model"}, - "autumn": map[string]any{"prompt_id": "briefing", "prompt_version": "1", "profile_id": "autumn", "model": "autumn-model"}, + "spring": map[string]any{"prompt_id": "briefing", "profile_id": "spring", "model": "spring-model"}, + "autumn": map[string]any{"prompt_id": "briefing", "profile_id": "autumn", "model": "autumn-model"}, }, }) if selection.Config.Set { @@ -128,10 +127,10 @@ func loadPromptEngine(ctx context.Context, selection ckconfig.Selection, input * "collections": map[string]any{ "field-notes": map[string]any{ "release_version": "1", - "backends": map[string]any{"demo": map[string]any{"endpoint": "https://example.invalid/v1"}}, - "profiles": map[string]any{"assistant": map[string]any{"backend": "field-notes/demo", "model": "example-model"}}, - "prompts": map[string]any{"briefing": map[string]any{ - "version": "1", + "backends": []any{map[string]any{"id": "demo", "endpoint": "https://example.invalid/v1"}}, + "profiles": []any{map[string]any{"id": "assistant", "backend": "field-notes/demo", "model": "example-model"}}, + "prompts": []any{map[string]any{ + "id": "briefing", "default_profile": "field-notes/assistant", "inputs": []any{map[string]any{"name": "topic", "required": true, "content_type": "text/plain", "description": "Topic to brief"}}, "messages": []any{map[string]any{"role": "user", "content": "Write a concise briefing about {{.topic}}."}}, @@ -139,15 +138,18 @@ func loadPromptEngine(ctx context.Context, selection ckconfig.Selection, input * }}, }, }, - "profiles": map[string]any{chosen.ProfileID: map[string]any{ + "backends": []any{map[string]any{"id": "local", "endpoint": "https://example.invalid/v1"}}, + "profiles": []any{map[string]any{ + "id": chosen.ProfileID, + "backend": "local", "endpoint": "https://example.invalid/v1", "model": chosen.Model, "extra_params": map[string]any{ "campaign": name, }, }}, - "prompts": map[string]any{chosen.PromptID: map[string]any{ - "version": chosen.PromptVersion, + "prompts": []any{map[string]any{ + "id": chosen.PromptID, "default_profile": chosen.ProfileID, "inputs": []any{map[string]any{"name": "topic", "required": true, "content_type": "text/plain", "description": "Topic to brief"}}, "messages": []any{map[string]any{"role": "user", "content": "Write a concise briefing about {{.topic}}. {{if .mode}}{{input \"notes\"}}{{end}}"}}, diff --git a/integrations/ckprompt/examples/composed/main_test.go b/integrations/ckprompt/examples/composed/main_test.go index 574d8ec..d3c2369 100644 --- a/integrations/ckprompt/examples/composed/main_test.go +++ b/integrations/ckprompt/examples/composed/main_test.go @@ -4,16 +4,17 @@ import ( "bytes" "context" "errors" + "fmt" "io" "reflect" "strings" "testing" - configkit "go.maximumdirect.net/configkit" - promptkit "go.maximumdirect.net/promptkit" "go.maximumdirect.net/ckconfig" "go.maximumdirect.net/ckprompt" commandkit "go.maximumdirect.net/commandkit" + configkit "go.maximumdirect.net/configkit" + promptkit "go.maximumdirect.net/promptkit" "go.yaml.in/yaml/v4" ) @@ -105,11 +106,41 @@ func TestComposedPromptKitAndDomainCommandsRemainAvailable(t *testing.T) { if err != nil { t.Fatal(err) } + engine, err := loadPromptEngine(context.Background(), ckconfig.Selection{}, &inspectionInput{Campaign: commandkit.Supplied[string]{Value: "spring", Set: true}}, nil) + if err != nil { + t.Fatal(err) + } + inspection, err := engine.InspectConfiguration() + if err != nil { + t.Fatal(err) + } + var effective bytes.Buffer + if err := inspection.WriteEffective(&effective); err != nil { + t.Fatal(err) + } + var config struct { + Profiles []struct { + ID string `yaml:"id"` + } `yaml:"profiles"` + } + if err := yaml.Unmarshal(effective.Bytes(), &config); err != nil { + t.Fatal(err) + } + index := -1 + for i, profile := range config.Profiles { + if profile.ID == "spring" { + index = i + break + } + } + if index < 0 { + t.Fatal("selected profile missing from configuration") + } commands := [][]string{ {"config", "validate", "--scope", "all", "--campaign", "spring", "--limit=0"}, {"config", "show", "--scope", "promptkit", "--campaign", "spring"}, {"config", "sources", "--scope", "promptkit", "--campaign", "spring"}, - {"config", "explain", "--scope", "promptkit", "--path", "/profiles/spring/model", "--campaign", "spring"}, + {"config", "explain", "--scope", "promptkit", "--path", fmt.Sprintf("/profiles/%d/model", index), "--campaign", "spring"}, {"promptkit", "show", "prompt", "briefing", "--campaign", "spring", "--format", "yaml"}, {"promptkit", "show", "profile", "spring", "--campaign", "spring"}, {"session", "restore", "session-42", "--campaign", "spring"}, @@ -146,12 +177,15 @@ func TestComposedRequirementsDiscoveryUsesSelectedCampaign(t *testing.T) { if err := yaml.Unmarshal(output.Bytes(), &report); err != nil { t.Fatalf("decode requirements report %q: %v", output.String(), err) } - for _, key := range []string{"prompt_id", "version", "profile_id", "complete", "inputs", "variables", "issues"} { + for _, key := range []string{"prompt_id", "profile_id", "complete", "inputs", "variables", "issues"} { if _, found := report[key]; !found { t.Errorf("requirements report is missing %q: %#v", key, report) } } - if report["prompt_id"] != "briefing" || report["version"] != "1" || report["profile_id"] != "spring" { + if _, exists := report["version"]; exists { + t.Fatalf("unexpected prompt version: %#v", report) + } + if report["prompt_id"] != "briefing" || report["profile_id"] != "spring" { t.Fatalf("requirements report metadata = %#v", report) } }) @@ -267,9 +301,25 @@ func TestPromptKitScopeDiffAndCatalogsUseSelectedCampaign(t *testing.T) { if outcome.Kind != commandkit.OutcomeSuccess { t.Fatalf("PromptKit diff outcome = %#v", outcome) } - value := composedReportValue(t, output.Bytes(), "changes", "profiles", "spring", "model") - if value == nil || value.Value != "spring-model-after" { - t.Fatalf("PromptKit diff spring model = %#v; report %q", value, output.String()) + var diff struct { + Changes struct { + Profiles []struct { + ID string `yaml:"id"` + Model string `yaml:"model"` + } `yaml:"profiles"` + } `yaml:"changes"` + } + if err := yaml.Unmarshal(output.Bytes(), &diff); err != nil { + t.Fatal(err) + } + foundChange := false + for _, profile := range diff.Changes.Profiles { + if profile.ID == "spring" && profile.Model == "spring-model-after" { + foundChange = true + } + } + if !foundChange { + t.Fatalf("PromptKit diff missing spring model: %q", output.String()) } output.Reset() @@ -283,7 +333,7 @@ func TestPromptKitScopeDiffAndCatalogsUseSelectedCampaign(t *testing.T) { } foundPrompt := false for _, prompt := range prompts { - if prompt["id"] == "briefing" && prompt["version"] == "1" { + if prompt["id"] == "briefing" && len(prompt) == 1 { foundPrompt = true break }