Class ScanOrchestrator
Each Command mode varies in three places — output format, per-record
sink behaviour, and whether records stream or buffer — but they all share
the same core sequence:
- Load all configured
TestDiscoveryproviders. - For each scan root, run every provider, merge their results, and group methods by class.
- For each class, optionally consult the AI engine through a layered cache + override lookup.
- Forward each method record to the supplied sink.
- Close all providers.
This class owns that sequence. Commands compose it with a
PluginLoader (passed in at construction) and configure the
per-record sink, AI runtime, and content-hash policy at call time.
API shape
Two entry points serve the two common patterns:
scan(java.util.List<java.nio.file.Path>, org.egothor.methodatlas.CliConfig, org.egothor.methodatlas.api.TestDiscoveryConfig, org.egothor.methodatlas.ai.AiSuggestionEngine, org.egothor.methodatlas.emit.TestMethodSink, org.egothor.methodatlas.emit.ClassificationOverride, org.egothor.methodatlas.AiResultCache, org.egothor.methodatlas.command.CredentialTriageContext)manages the provider lifecycle internally; it is the right call for SARIF, JSON, and GitHub-annotation modes that buffer or emit unconditionally.runDiscovery(java.nio.file.Path, java.util.List<org.egothor.methodatlas.api.TestDiscovery>, org.egothor.methodatlas.ai.AiOptions, org.egothor.methodatlas.ai.AiSuggestionEngine, org.egothor.methodatlas.emit.TestMethodSink, boolean, org.egothor.methodatlas.emit.ClassificationOverride, org.egothor.methodatlas.AiResultCache, org.egothor.methodatlas.command.CredentialTriageContext)processes a single root against pre-loaded providers; it is the right call for CSV and plain-text modes that compute per-root metadata (such as thesource_rootcolumn) before forwarding records.
The apply-tags flow has its own shape: collectMethodsByFile(java.util.List<java.nio.file.Path>, java.util.List<org.egothor.methodatlas.api.TestDiscovery>)
groups discovered methods by source file (the caller owns the provider
lifecycle so it can read each provider's
TestDiscovery.hadErrors() afterwards), and
gatherAiSuggestionsForFile(java.util.Map<java.lang.String, java.util.List<org.egothor.methodatlas.api.DiscoveredMethod>>, org.egothor.methodatlas.command.AiRuntime, org.egothor.methodatlas.AiResultCache, java.util.Map<java.lang.String, java.util.List<java.lang.String>>, java.util.Map<java.lang.String, java.lang.String>) resolves AI suggestions for one
file at a time.
Thread safety
This class is thread-safe. The injected PluginLoader is
thread-safe and ServiceLoader resolution is
idempotent; nothing else is shared between calls.
- Since:
- 1.0.0
- See Also:
-
Constructor Summary
ConstructorsConstructorDescriptionScanOrchestrator(PluginLoader pluginLoader) Creates a new orchestrator with no extra sink.ScanOrchestrator(PluginLoader pluginLoader, Optional<org.egothor.methodatlas.emit.TestMethodSink> extraSink) Creates a new orchestrator that fans out every record toextraSinkin addition to the command's primary sink. -
Method Summary
Modifier and TypeMethodDescriptioncollectMethodsByFile(List<Path> roots, List<org.egothor.methodatlas.api.TestDiscovery> providers) Collects all discovered methods from every configured root, keyed by source-file path.org.egothor.methodatlas.emit.TestMethodSinkfilterSink(org.egothor.methodatlas.emit.TestMethodSink delegate, boolean securityOnly, double minConfidence, boolean confidenceEnabled) Wraps aTestMethodSinkso that only records that pass all active filters are forwarded todelegate.voidgatherAiSuggestionsForFile(Map<String, List<org.egothor.methodatlas.api.DiscoveredMethod>> byClass, AiRuntime ai, AiResultCache aiCache, Map<String, List<String>> tagsToApply, Map<String, String> displayNames) Resolves AI security-classification suggestions for every class inbyClassand populatestagsToApplyanddisplayNameswith the results for methods that are security-relevant.booleanrunDiscovery(Path root, List<org.egothor.methodatlas.api.TestDiscovery> providers, org.egothor.methodatlas.ai.AiOptions aiOptions, org.egothor.methodatlas.ai.AiSuggestionEngine aiEngine, org.egothor.methodatlas.emit.TestMethodSink sink, boolean contentHashEnabled, org.egothor.methodatlas.emit.ClassificationOverride override, AiResultCache aiCache, org.egothor.methodatlas.command.CredentialTriageContext secretCtx) Runs all configuredTestDiscoveryproviders onroot, merges their results, orchestrates AI analysis per class, and forwards each method record tosink.intscan(List<Path> roots, CliConfig cliConfig, org.egothor.methodatlas.api.TestDiscoveryConfig discoveryConfig, org.egothor.methodatlas.ai.AiSuggestionEngine aiEngine, org.egothor.methodatlas.emit.TestMethodSink sink, org.egothor.methodatlas.emit.ClassificationOverride override, AiResultCache aiCache, org.egothor.methodatlas.command.CredentialTriageContext secretCtx) Scans every configured root, forwarding each discovered test method tosink.static org.egothor.methodatlas.ai.PromptBuilder.TargetMethodtoTargetMethod(org.egothor.methodatlas.api.DiscoveredMethod m) Converts a single discovered test method into a prompt target descriptor.
-
Constructor Details
-
ScanOrchestrator
Creates a new orchestrator with no extra sink.- Parameters:
pluginLoader- plugin loader used byscan(java.util.List<java.nio.file.Path>, org.egothor.methodatlas.CliConfig, org.egothor.methodatlas.api.TestDiscoveryConfig, org.egothor.methodatlas.ai.AiSuggestionEngine, org.egothor.methodatlas.emit.TestMethodSink, org.egothor.methodatlas.emit.ClassificationOverride, org.egothor.methodatlas.AiResultCache, org.egothor.methodatlas.command.CredentialTriageContext)andcollectMethodsByFile(java.util.List<java.nio.file.Path>, java.util.List<org.egothor.methodatlas.api.TestDiscovery>); must not benull
-
ScanOrchestrator
public ScanOrchestrator(PluginLoader pluginLoader, Optional<org.egothor.methodatlas.emit.TestMethodSink> extraSink) Creates a new orchestrator that fans out every record toextraSinkin addition to the command's primary sink.The extra sink, when present, is composed with the primary sink at the
runDiscovery(java.nio.file.Path, java.util.List<org.egothor.methodatlas.api.TestDiscovery>, org.egothor.methodatlas.ai.AiOptions, org.egothor.methodatlas.ai.AiSuggestionEngine, org.egothor.methodatlas.emit.TestMethodSink, boolean, org.egothor.methodatlas.emit.ClassificationOverride, org.egothor.methodatlas.AiResultCache, org.egothor.methodatlas.command.CredentialTriageContext)boundary so every command mode (SARIF, JSON, CSV, GitHub annotations) sees the same fan-out automatically. WhenextraSinkisOptional.empty()the orchestrator behaves identically to the legacy single-sink constructor.- Parameters:
pluginLoader- plugin loader used byscan(java.util.List<java.nio.file.Path>, org.egothor.methodatlas.CliConfig, org.egothor.methodatlas.api.TestDiscoveryConfig, org.egothor.methodatlas.ai.AiSuggestionEngine, org.egothor.methodatlas.emit.TestMethodSink, org.egothor.methodatlas.emit.ClassificationOverride, org.egothor.methodatlas.AiResultCache, org.egothor.methodatlas.command.CredentialTriageContext)andcollectMethodsByFile(java.util.List<java.nio.file.Path>, java.util.List<org.egothor.methodatlas.api.TestDiscovery>); must not benullextraSink- optional secondary sink invoked in addition to the command-supplied primary sink; must not benull(useOptional.empty())
-
-
Method Details
-
scan
public int scan(List<Path> roots, CliConfig cliConfig, org.egothor.methodatlas.api.TestDiscoveryConfig discoveryConfig, org.egothor.methodatlas.ai.AiSuggestionEngine aiEngine, org.egothor.methodatlas.emit.TestMethodSink sink, org.egothor.methodatlas.emit.ClassificationOverride override, AiResultCache aiCache, org.egothor.methodatlas.command.CredentialTriageContext secretCtx) throws IOException Scans every configured root, forwarding each discovered test method tosink. Loads and closes theTestDiscoveryproviders internally so callers do not need to manage the lifecycle.When
secretCtxis supplied, credential triage is folded into each per-class AI call so the class source is sent to the provider once for both classification and triage; passnullfor classification only.- Parameters:
roots- source roots to scan; must not benullcliConfig- full parsed CLI configurationdiscoveryConfig- discovery configuration forwarded to providersaiEngine- AI engine providing suggestions; may benullwhen AI is disabledsink- receiver of discovered test method recordsoverride- human classification overridesaiCache- AI result cachesecretCtx- credential-triage context, ornullto disable the fold (classification only)- Returns:
0if all files were processed successfully,1if any file produced a parse or processing error- Throws:
IOException- if traversing a file tree fails- Since:
- 1.0.0
-
runDiscovery
public boolean runDiscovery(Path root, List<org.egothor.methodatlas.api.TestDiscovery> providers, org.egothor.methodatlas.ai.AiOptions aiOptions, org.egothor.methodatlas.ai.AiSuggestionEngine aiEngine, org.egothor.methodatlas.emit.TestMethodSink sink, boolean contentHashEnabled, org.egothor.methodatlas.emit.ClassificationOverride override, AiResultCache aiCache, org.egothor.methodatlas.command.CredentialTriageContext secretCtx) throws IOException Runs all configuredTestDiscoveryproviders onroot, merges their results, orchestrates AI analysis per class, and forwards each method record tosink.Providers are passed in pre-loaded; callers manage the lifecycle (typically through
PluginLoader.closeAll(List)in afinallyblock) so that they can share one provider list across multiple roots while still computing per-root metadata before each call.When
secretCtxis supplied, credential triage is folded into each per-class AI call; passnullfor classification only.- Parameters:
root- directory to scanproviders- list of pre-configured discovery providersaiOptions- AI configuration for the current runaiEngine- AI engine, ornullwhen AI is disabledsink- receiver of discovered test method recordscontentHashEnabled- whether to include the class content hash in emitted recordsoverride- human classification overridesaiCache- AI result cachesecretCtx- credential-triage context, ornullto disable the fold- Returns:
trueif any provider encountered a parse or processing error- Throws:
IOException- if traversing the file tree fails- Since:
- 1.0.0
-
collectMethodsByFile
public Map<Path,List<org.egothor.methodatlas.api.DiscoveredMethod>> collectMethodsByFile(List<Path> roots, List<org.egothor.methodatlas.api.TestDiscovery> providers) throws IOException Collects all discovered methods from every configured root, keyed by source-file path. Methods whoseDiscoveredMethod.filePath()isnullare silently skipped.Providers are passed in pre-loaded so the caller can read each provider's
TestDiscovery.hadErrors()after the call and decide how to propagate the exit code; the caller also owns closing them.- Parameters:
roots- scan roots; must not benullproviders- configured and already-loaded discovery providers; must not benull- Returns:
- mutable map from source-file path to the methods found in that file; insertion order matches discovery order
- Throws:
IOException- if directory traversal fails for any root
-
gatherAiSuggestionsForFile
public void gatherAiSuggestionsForFile(Map<String, List<org.egothor.methodatlas.api.DiscoveredMethod>> byClass, AiRuntime ai, AiResultCache aiCache, Map<String, List<String>> tagsToApply, Map<String, String> displayNames) Resolves AI security-classification suggestions for every class inbyClassand populatestagsToApplyanddisplayNameswith the results for methods that are security-relevant.A display-name suggestion is only placed into
displayNameswhen the discovered method has no existing@DisplayNamein source (i.e.DiscoveredMethod.displayName()returnsnull). This prevents AI-generated names from overwriting manually authored ones.- Parameters:
byClass- discovered methods grouped by FQCN for one source fileai- AI runtime carrying the engine, override, and cacheaiCache- AI result cache used to compute the content-hash lookup keytagsToApply- output accumulator: method name to tag values to writedisplayNames- output accumulator: method name to display name to write- Since:
- 1.0.0
-
filterSink
public org.egothor.methodatlas.emit.TestMethodSink filterSink(org.egothor.methodatlas.emit.TestMethodSink delegate, boolean securityOnly, double minConfidence, boolean confidenceEnabled) Wraps aTestMethodSinkso that only records that pass all active filters are forwarded todelegate.Two independent filters are supported and composed in order:
- Security-only filter — when
securityOnlyistrue, records whoseAiMethodSuggestionisnullor hassecurityRelevant=falseare dropped. - Confidence threshold filter — when
confidenceEnabledistrueandminConfidence > 0.0, records whoseAiMethodSuggestionisnullor has aAiMethodSuggestion.confidence()belowminConfidenceare dropped. This filter is a no-op whenconfidenceEnabledisfalsebecause the confidence field is always0.0when confidence scoring was not requested.
When neither filter is active the original
delegateis returned unchanged (zero overhead).- Parameters:
delegate- the underlying sink to forward matching records tosecurityOnly- whether to enable the security-relevance filterminConfidence- minimum confidence score (inclusive) required to pass the confidence filter;0.0disables itconfidenceEnabled- whether confidence scoring was requested; must betruefor the confidence filter to activate- Returns:
- filtered sink, or
delegateunchanged when all filters are off - Since:
- 1.0.0
- Security-only filter — when
-
toTargetMethod
public static org.egothor.methodatlas.ai.PromptBuilder.TargetMethod toTargetMethod(org.egothor.methodatlas.api.DiscoveredMethod m) Converts a single discovered test method into a prompt target descriptor.Exposed publicly because
ManualPrepareCommandalso needs to build prompt-target lists from discovered methods when writing manual work files; the conversion logic must stay aligned across both call sites to keep the prompt format consistent.- Parameters:
m- discovered test method; must not benull- Returns:
- corresponding prompt target descriptor; never
null - See Also:
-