Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions module/infra_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,15 @@ func (m *InfraAdmin) Init(app modular.Application) error {
m.providerMu[pm] = &sync.Mutex{}
}

// Populate providerTypeByModule from the loaded WorkflowConfig
// per spec-reviewer T6 F1 + design cycle-5/6: handler.
// ListProviders needs the YAML-config `provider:` string, NOT
// the plugin's display name from provider.Name().
if err := m.populateProviderTypes(app); err != nil {
return fmt.Errorf("infra.admin: populate provider types: %w", err)
}
// NOTE: providerTypeByModule / wfCfg / desiredSpecs are populated in
// Start(), NOT here. engine.BuildFromConfig registers the "workflow"
// config section AFTER app.Init() (engine.go: app.Init() then
// RegisterConfigSection("workflow")), so app.GetConfigSection("workflow")
// returns "not found" during Init and would silently degrade
// provider_type, supported_regions, and the mutation desiredSpecs to
// empty. Start() runs after BuildFromConfig completes, when the section
// is present. (Surfaced by scenario-92 live boot; unit tests pre-register
// the section via withConfigSectionApp so they did not catch it.)

// In-process catalogs.
m.fieldCatalog = catalog.New()
Expand Down Expand Up @@ -563,6 +565,14 @@ func (m *InfraAdmin) Start(ctx context.Context) error {
return fmt.Errorf("infra.admin: workflowEngine: %w", err)
}

// Populate providerTypeByModule + wfCfg + desiredSpecs from the loaded
// WorkflowConfig. MUST run here (Start), not Init: the engine registers
// the "workflow" config section after app.Init(), so this would silently
// degrade to empty during Init. Runs before routes serve any request.
if err := m.populateProviderTypes(m.app); err != nil {
return fmt.Errorf("infra.admin: populate provider types: %w", err)
}

if m.router == nil {
return fmt.Errorf("infra.admin: router unresolved — Init failed silently?")
}
Expand Down
7 changes: 7 additions & 0 deletions module/infra_admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ func TestInfraAdmin_Init_ResolvesAllServices(t *testing.T) {
if len(m.providers) != 1 || m.providers["do-provider"] == nil {
t.Errorf("providers = %v, want one do-provider entry", m.providers)
}
// providerTypeByModule is populated in Start() now, not Init() — the
// engine registers the "workflow" config section after app.Init(), so
// Init-time GetConfigSection would fail. Drive populateProviderTypes
// explicitly to assert the F1 contract (the function is unchanged).
if err := m.populateProviderTypes(app); err != nil {
t.Fatalf("populateProviderTypes: %v", err)
}
if m.providerTypeByModule["do-provider"] != "digitalocean" {
t.Errorf("providerTypeByModule[do-provider] = %q, want digitalocean (F1 contract)", m.providerTypeByModule["do-provider"])
}
Expand Down
Loading