From 6c5dec761ce164b1cabd1234e303e95c025d5b78 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Fri, 9 Oct 2026 00:38:01 +0000 Subject: [PATCH] Inspect requirements by profile identity --- docs/internal/runner.md | 13 +-- internal/usecase/profile_inspection.go | 42 ++++++++-- internal/usecase/requirements_inspection.go | 4 +- .../usecase/requirements_inspection_test.go | 79 +++++++++++++++++++ 4 files changed, 125 insertions(+), 13 deletions(-) diff --git a/docs/internal/runner.md b/docs/internal/runner.md index 1797bf1..2b840a8 100644 --- a/docs/internal/runner.md +++ b/docs/internal/runner.md @@ -44,11 +44,14 @@ collaborators. The root [`Engine.InspectPrompt`](../../engine.go) GoDoc owns the public operation's exact contract. `InspectRequirements` uses the same prompt selection boundary, then selects the -request profile or prompt default for identity and obtains the exact captured -requirement snapshot. It analyzes that snapshot with the supplied caller -variables and normalized direct-session bypass; it does not resolve target -readiness or enter the preparation pipeline. Missing snapshot capability is a -prompt-load failure rather than a source fallback. The root +request profile or prompt default through an optional identity-only repository +capability, then obtains the exact captured requirement snapshot. Internal +repositories without that capability use the full-profile selection boundary; +both paths preserve the exact selected ID, and the fallback retains its +profile/target validity checks. It analyzes the snapshot with the supplied +caller variables and normalized direct-session bypass; it does not resolve +target readiness or enter the preparation pipeline. Missing snapshot +capability is a prompt-load failure rather than a source fallback. The root [`Engine.InspectRequirements`](../../requirements.go) GoDoc owns the public operation's exact contract. diff --git a/internal/usecase/profile_inspection.go b/internal/usecase/profile_inspection.go index 4516917..e1c3a56 100644 --- a/internal/usecase/profile_inspection.go +++ b/internal/usecase/profile_inspection.go @@ -15,16 +15,27 @@ type resolvedProfileSelection struct { profile *domain.ExecutionProfile } +type profileIdentityRepository interface { + GetProfileIdentity(context.Context, string) (string, error) +} + +func (r *Runner) profileLookupID(profileID string) (string, error) { + if strings.TrimSpace(profileID) == "" { + return "", fmt.Errorf("%w: profile id is required", ErrInvalidRequest) + } + if r == nil || r.profiles == nil { + return "", fmt.Errorf("%w: profile repository is not configured", ErrProfileLoad) + } + return profileID, nil +} + func (r *Runner) resolveProfileSelection( ctx context.Context, profileID string, ) (*resolvedProfileSelection, error) { - lookupID := profileID - if strings.TrimSpace(lookupID) == "" { - return nil, fmt.Errorf("%w: profile id is required", ErrInvalidRequest) - } - if r == nil || r.profiles == nil { - return nil, fmt.Errorf("%w: profile repository is not configured", ErrProfileLoad) + lookupID, err := r.profileLookupID(profileID) + if err != nil { + return nil, err } selectedProfile, err := r.profiles.GetProfile(ctx, lookupID) if err != nil { @@ -45,6 +56,25 @@ func (r *Runner) resolveProfileSelection( }, nil } +func (r *Runner) resolveProfileIdentity(ctx context.Context, profileID string) (string, error) { + lookupID, err := r.profileLookupID(profileID) + if err != nil { + return "", err + } + if indexed, ok := r.profiles.(profileIdentityRepository); ok { + identity, err := indexed.GetProfileIdentity(ctx, lookupID) + if err != nil { + return "", fmt.Errorf("%w: %w", ErrProfileLoad, err) + } + return identity, nil + } + selection, err := r.resolveProfileSelection(ctx, lookupID) + if err != nil { + return "", err + } + return selection.id, nil +} + func normalizeResolvedExecutionTarget(target domain.ExecutionTarget) (domain.ExecutionTarget, error) { endpoint, err := domain.NormalizeOpenAICompatibleBaseEndpoint(target.Endpoint) if err != nil { diff --git a/internal/usecase/requirements_inspection.go b/internal/usecase/requirements_inspection.go index be9d9fb..fcb36e4 100644 --- a/internal/usecase/requirements_inspection.go +++ b/internal/usecase/requirements_inspection.go @@ -54,7 +54,7 @@ func (r *Runner) InspectRequirements( if strings.TrimSpace(selectedProfileID) == "" { return nil, fmt.Errorf("%w: %w: profile id is required either in request or prompt default_profile", ErrInvalidRequest, ErrProfileRequired) } - profileSelection, err := r.resolveProfileSelection(ctx, selectedProfileID) + profileIdentity, err := r.resolveProfileIdentity(ctx, selectedProfileID) if err != nil { return nil, err } @@ -74,6 +74,6 @@ func (r *Runner) InspectRequirements( if err != nil { return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err) } - inspection.ProfileID = profileSelection.id + inspection.ProfileID = profileIdentity return &inspection, nil } diff --git a/internal/usecase/requirements_inspection_test.go b/internal/usecase/requirements_inspection_test.go index 9ac438d..9f8f6cc 100644 --- a/internal/usecase/requirements_inspection_test.go +++ b/internal/usecase/requirements_inspection_test.go @@ -24,6 +24,23 @@ type requirementInspectionPromptRepository struct { snapshotVersion string } +type inspectionProfileIdentityRepository struct { + *inspectionProfileRepository + identity string + identityErr error + identityID string + identityCalls int +} + +func (repository *inspectionProfileIdentityRepository) GetProfileIdentity( + _ context.Context, + id string, +) (string, error) { + repository.identityCalls++ + repository.identityID = id + return repository.identity, repository.identityErr +} + func (repository *requirementInspectionPromptRepository) GetPromptDefinition( context.Context, string, @@ -94,6 +111,68 @@ func TestRunnerInspectRequirementsUsesCapturedSelectionWithoutExecutionWork(t *t } } +func TestRunnerInspectRequirementsUsesProfileIdentityCapabilityOrFallback(t *testing.T) { + message, err := prompt.CompileTemplate("message", `{{.name}}`) + if err != nil { + t.Fatal(err) + } + definition := &domain.PromptDefinition{ID: "selected", Version: "v1"} + makePromptRepository := func() *requirementInspectionPromptRepository { + return &requirementInspectionPromptRepository{ + definition: definition, snapshotFound: true, + snapshot: prompt.RequirementSnapshot{Templates: prompt.CompiledTemplates{Messages: []*template.Template{message}}}, + } + } + requestedID := " literal-profile " + + t.Run("optional capability", func(t *testing.T) { + profiles := &inspectionProfileIdentityRepository{ + inspectionProfileRepository: &inspectionProfileRepository{profile: &domain.ExecutionProfile{ + ID: "repository-profile", ResolvedTarget: &domain.ExecutionTarget{}, + }}, + identity: requestedID, + } + inspection, err := (&Runner{promptDefs: makePromptRepository(), profiles: profiles}).InspectRequirements( + context.Background(), RequirementInspectionRequest{ + PromptID: "selected", ProfileID: requestedID, Vars: map[string]string{"name": "Ada"}, + }, + ) + if err != nil || inspection.ProfileID != requestedID || profiles.identityCalls != 1 || + profiles.identityID != requestedID || profiles.calls != 0 { + t.Fatalf("identity capability inspection=(%+v, %v), capability calls=%d id=%q full lookups=%d", + inspection, err, profiles.identityCalls, profiles.identityID, profiles.calls) + } + }) + + t.Run("full-profile fallback", func(t *testing.T) { + profiles := &inspectionProfileRepository{profile: &domain.ExecutionProfile{ + ID: "repository-profile", ResolvedTarget: &domain.ExecutionTarget{}, + }} + inspection, err := (&Runner{promptDefs: makePromptRepository(), profiles: profiles}).InspectRequirements( + context.Background(), RequirementInspectionRequest{ + PromptID: "selected", ProfileID: requestedID, Vars: map[string]string{"name": "Ada"}, + }, + ) + if err != nil || inspection.ProfileID != requestedID || profiles.calls != 1 || profiles.id != requestedID { + t.Fatalf("fallback inspection=(%+v, %v), full lookup=(%d,%q)", inspection, err, profiles.calls, profiles.id) + } + }) + + t.Run("capability failure", func(t *testing.T) { + lookupErr := errors.New("profile identity lookup failed") + profiles := &inspectionProfileIdentityRepository{ + inspectionProfileRepository: &inspectionProfileRepository{}, + identityErr: lookupErr, + } + _, err := (&Runner{promptDefs: makePromptRepository(), profiles: profiles}).InspectRequirements( + context.Background(), RequirementInspectionRequest{PromptID: "selected", ProfileID: requestedID}, + ) + if !errors.Is(err, ErrProfileLoad) || !errors.Is(err, lookupErr) || profiles.identityCalls != 1 || profiles.calls != 0 { + t.Fatalf("capability failure=(%v), capability calls=%d full lookups=%d", err, profiles.identityCalls, profiles.calls) + } + }) +} + func TestRunnerInspectRequirementsUsesPromptDefaultAndIncludesSessionWhenNotBypassed(t *testing.T) { message, err := prompt.CompileTemplate("message", `{{input "message-input"}}`) if err != nil {