Finalize domain prompt profile roadmap

This commit is contained in:
2026-08-01 14:27:39 +00:00
parent c5ec4f83b2
commit 117c5336ba
3 changed files with 80 additions and 30 deletions

View File

@@ -1,6 +1,6 @@
# Domain-Specific Prompt Profiles Roadmap
Status: Accepted; implementation plan ready.
Status: Implemented.
## Purpose
@@ -10,9 +10,9 @@ These logical profiles should give each report an appropriate default while
allowing operators to replace any definition through the existing configured
profile source.
This roadmap defines the scope, policy, and intended end state. The companion
[implementation plan](domain-profiles-implementation.md) owns the ordered work
needed to reach that state.
This roadmap records the scope, policy, and implemented end state. The
companion [implementation plan](implementation.md) records the ordered work
and verification used to reach it.
## User Intent
@@ -29,22 +29,22 @@ The feature is intended to provide three related benefits:
guarantee. A locally hosted lightweight model may still generate slowly on the
available hardware.
## Current State
## Pre-Implementation Baseline
Daily, Today, Tomorrow, and Hourly each declare Promptkit's
`gemini-flash-latest` profile as their prompt default. The optional
`promptkit.profile` setting overrides that default for every selected report in
Before implementation, Daily, Today, Tomorrow, and Hourly each declared
Promptkit's `gemini-flash-latest` profile as their prompt default. The optional
`promptkit.profile` setting overrode that default for every selected report in
an invocation.
Weatherreporter accepts either `promptkit.profile_file` or
`promptkit.profile_dir` and passes that source to Promptkit. A matching external
profile can override a Promptkit built-in profile, and the configured local
backend can support profiles that select `backend: local`. Endpoint-only
OpenAI-compatible profiles can also provide their own endpoint.
Weatherreporter accepted either `promptkit.profile_file` or
`promptkit.profile_dir` and passed that source to Promptkit. A matching external
profile could override a Promptkit built-in profile, and the configured local
backend could support profiles that select `backend: local`. Endpoint-only
OpenAI-compatible profiles could also provide their own endpoint.
Weatherreporter does not currently own or embed execution profiles. Promptkit
v0.5.0 now provides the fallback-profile layer needed to add them without
changing the existing operator-source precedence.
Weatherreporter did not own or embed execution profiles. Promptkit v0.5.0
provided the fallback-profile layer used to add them without changing the
existing operator-source precedence.
## Prerequisite
@@ -56,7 +56,7 @@ passed the repository test suite and an operator smoke test. Weatherreporter
must continue to use only Promptkit's public API rather than depending on its
internal packages or reproducing its profile repository behavior.
## Desired End State
## Implemented End State
Weatherreporter embeds usable definitions for these exact logical profile IDs:
@@ -204,7 +204,7 @@ The purpose is to choose an appropriate default for each tier, not to add a
permanent benchmark framework or live-provider requirement to the ordinary
test suite. Repository tests remain offline and deterministic.
## Scope
## Implemented Scope
The completed feature includes:
@@ -260,9 +260,9 @@ An external same-ID override is an operator-owned compatibility commitment.
Weatherreporter may evolve its embedded definitions, but it must not rewrite or
silently merge an operator file.
## Completion Criteria
## Completion Record
The roadmap is complete when:
The following conditions are satisfied:
- a tagged Promptkit dependency supports the required fallback layer;
- every operational prompt selects its assigned logical profile at exact

View File

@@ -1,19 +1,14 @@
# Domain-Specific Prompt Profiles Implementation Plan
Status: Ready for implementation.
Status: Completed.
## Purpose And Authority
This document gives a coding agent the ordered work needed to implement the
This document records the completed implementation of the
[domain-specific prompt profiles roadmap](domain-profiles.md). The roadmap is
authoritative for scope, user intent, policy choices, and the desired end
state. This plan is authoritative for implementation sequence, verification,
and stage exit gates.
The target implementer is a `gpt-5.6-terra` coding agent using high reasoning.
Complete the stages in order. Each stage is scoped for one implementation
prompt and must leave the repository coherent, tested, and reviewable before
the next stage begins.
authoritative for scope, user intent, policy choices, and the implemented end
state. This plan records the implementation sequence, verification, and exit
gates used to reach it.
This plan follows the repository's
[architecture](../policy/architecture.md),

View File

@@ -12,7 +12,10 @@ import (
"time"
promptkit "gitea.maximumdirect.net/eric/promptkit"
"gitea.maximumdirect.net/eric/weatherreporter/internal/app"
appconfig "gitea.maximumdirect.net/eric/weatherreporter/internal/config"
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptexec"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
)
type fakeClient struct {
@@ -158,6 +161,44 @@ func TestMaintainedWeatherLightLocalProfileExampleInspectsOffline(t *testing.T)
assertProfile(t, adapter, "weather-light", "", "weather-local")
}
func TestApplicationPreflightResolvesEmbeddedAndOverriddenProfilesOffline(t *testing.T) {
lookupEnv := func(string) (string, bool) { return "test-key", true }
inspect := func(t *testing.T, adapter *Adapter, id report.ID, profile string, wantID string, wantBackend string, wantModel string) {
t.Helper()
result, err := app.InspectPromptExecution(context.Background(), app.PromptInspectionRequest{
Resolved: resolvedPromptProfile(t, id),
Executor: adapter,
Promptkit: appconfig.PromptkitConfig{Profile: profile},
LookupEnv: lookupEnv,
})
if err != nil {
t.Fatalf("InspectPromptExecution() error = %v", err)
}
if result.ProfileID != wantID || result.BackendID != wantBackend || result.ModelName != wantModel {
t.Fatalf("inspection = %#v, want profile/backend/model %q/%q/%q", result, wantID, wantBackend, wantModel)
}
}
embedded, err := newAdapterForTest(Config{}, &fakeClient{})
if err != nil {
t.Fatalf("newAdapterForTest(embedded) error = %v", err)
}
inspect(t, embedded, report.Hourly, "", "weather-light", "openrouter", "deepseek/deepseek-v4-flash")
inspect(t, embedded, report.Daily, "", "weather-balanced", "openrouter", "~google/gemini-flash-latest")
inspect(t, embedded, report.Today, "", "weather-balanced", "openrouter", "~google/gemini-flash-latest")
inspect(t, embedded, report.Tomorrow, "", "weather-balanced", "openrouter", "~google/gemini-flash-latest")
inspect(t, embedded, report.Daily, "weather-deep", "weather-deep", "openrouter", "~anthropic/claude-sonnet-latest")
override, err := newAdapterForTest(Config{ProfileFile: writeProfileFile(t, `id: weather-light
endpoint: https://local.example/v1
model: local-weather
`)}, &fakeClient{})
if err != nil {
t.Fatalf("newAdapterForTest(override) error = %v", err)
}
inspect(t, override, report.Hourly, "", "weather-light", "", "local-weather")
}
func TestProfileResolutionFallsThroughOnlyWhenTheConfiguredIDIsAbsent(t *testing.T) {
absentAdapter, err := New(Config{ProfileDirectory: testProfileDirectory(t, `id: other-profile
backend: openrouter
@@ -487,6 +528,20 @@ func writeProfileFile(t *testing.T, profile string) string {
return path
}
func resolvedPromptProfile(t *testing.T, id report.ID) report.Resolved {
t.Helper()
now := time.Date(2026, 5, 29, 12, 0, 0, 0, time.UTC)
request := report.ResolveRequest{Now: now, Location: time.UTC}
if id == report.Daily {
request.Date = now
}
resolved, err := report.DefaultRegistry().Resolve(id, request)
if err != nil {
t.Fatalf("Resolve(%q) error = %v", id, err)
}
return resolved
}
func testExecuteRequest() promptexec.ExecuteRequest {
return promptexec.ExecuteRequest{
PromptID: "weather.daily_generated_text",