Tracking issue for the v4 scoping audit: multi-agent review of engine internals, core API surface, source generators/analyzers, options generator, docs, and CI job output across ~25 workflow runs. 100 issues raised; breaking API-reshaping issues carry the breaking label and the v4.0.0 milestone.
Related pre-existing open issues: #3228 , #3226 , #2996 , #2623 .
Priorities
Every issue carries a priority label — filter with label:P0 etc.:
P0 — critical: silent corruption, hangs, or regressions landing unnoticed. Work first.
P1 — high: real user impact, or foundation the v4 branch needs early. Work second.
P2 — medium: improvements and polish. After P1.
P3 — low: hygiene. Whenever.
Recommended working order
Phase 0 — stop the bleeding (P0, releasable on 3.x):
#3337 (main has had no CI since April — fix first so everything else is guarded) → #3244 (silently skipped modules) → #3253 (AlwaysRun cleanup can no-op) → #3240 (TCS/lock recursion) → #3251 (startup hang on diamond graphs) → #3267 (orphaned process trees) → #3279 (distributed value-type crash; breaking fix, may need a 3.x mitigation + full fix in v4).
Phase 1 — 3.x hardening (P1, non-breaking):
Phase 2 — v4 branch foundation (breaking, in dependency order):
#3272 (remove obsolete API — clears the ground) → #3273 (context hierarchy) → #3274 (registry → source-gen) → #3290 (single builder entry point) → #3292 + #3279 (result model) → #3283 (hooks) → then the remaining milestone items in any order; #3320 (generated renames) last so tool packages regenerate once.
Phase 3 — P2 improvements , opportunistically alongside phases 1-2. Phase 4 — P3 whenever.
Engine correctness bugs
Engine performance & robustness
SecretObfuscator rebuilds and re-sorts the full secret set on every log line #3254 SecretObfuscator rebuilds secret set per log line
CoordinatedTextWriter: per-character global locking and O(n^2) buffer scans on the redirected Console.Out #3255 CoordinatedTextWriter per-char locking, O(n²) scans
Scheduler loop: make event-driven; O(n^2) allocations per 100ms cycle under the exclusive write lock #3256 Scheduler loop: event-driven + allocation fixes
ModuleScheduler.AddModule rebuilds the entire dependency state for every dynamically added module #3257 AddModule rebuilds entire dependency state per dynamic module
ProgressSession: per-module ticker tasks, uncancellable delays, fire-and-forget render loop #3258 ProgressSession ticker/pause/loop lifecycle
ModuleLogger allocates per log call and builds a new AnsiConsole per renderable #3259 ModuleLogger per-call allocations, per-renderable AnsiConsole
Cache module attribute reflection across the engine (repeated per event, per call, with instance-identity bugs) #3260 Cache module attribute reflection (incl. instance-identity bug)
Startup: estimated-time lookups are rate-limited to 100 modules/second #3261 Startup: estimated-time lookups rate-limited to 100/s
EstimatedTimes cache: per-module full-directory enumeration and unbounded growth #3262 EstimatedTimes cache: per-module directory enumeration, unbounded growth
Startup: whole-AppDomain type scans and eager assembly loading (UnusedModuleDetector, DependencyChainProvider O(n^2)) #3263 Startup: whole-AppDomain type scans, eager assembly loading
Run conditions can be evaluated twice per run and are re-reflected per module #3264 Run conditions evaluated twice per run
PipelineBuilder.Environment builds and disposes a full generic host on every read #3265 PipelineBuilder.Environment builds a host per read
Stream command output live to the module logger and cap in-memory buffering #3266 Stream command output live + cap buffering
Kill the whole child process tree on cancellation (job objects / process groups) #3267 Kill whole child process tree on cancellation
Disposer.RegisterOnShutdown roots one ProcessExit handler per module logger #3268 Disposer roots one ProcessExit handler per module logger
Deferred progress-mode output is unbounded in RAM and lost entirely on crash #3269 Deferred progress-mode output unbounded in RAM, lost on crash
ModuleOutputBuffer spins on a global lock with 50ms Monitor.TryEnter and drops output on timeout #3270 ModuleOutputBuffer lock spin drops output on timeout
BuildSystemDetector: cache detection, explicit precedence, and full BuildSystem coverage in IsRunningIn #3271 BuildSystemDetector caching, precedence, full enum coverage
v4 API reshaping (breaking, milestone v4.0.0)
v4: remove obsolete APIs and back-compat aliases #3272 Remove obsolete APIs and back-compat aliases
v4: collapse the duplicated context interface hierarchy (19 empty markers + IPipelineHookContext) #3273 Collapse duplicated context interface hierarchy
v4: replace the ModuleInitializer/static-registry integration wiring with compile-time source generation #3274 Replace ModuleInitializer/static-registry wiring with source generation
v4: tighten the public surface — internalize engine plumbing #3275 Tighten public surface — internalize engine plumbing
Matrix support: wire up MatrixModuleExpander or mark the public API experimental #3276 Matrix: wire up expander or mark experimental
TryGetService<T> throws instead of returning null #3277 TryGetService throws instead of returning null
v4: CommandResult — required members instead of CS8618 suppression #3278 CommandResult: required members, no CS8618 suppression
v4: unify ModuleResult<T?> vs ModuleResult<T> — InvalidCastException for value-type results in distributed mode #3279 Unify ModuleResult<T?>/ModuleResult<T> (distributed InvalidCastException)
v4: Git package — lazy GitInformation, honest nullability, Commits on the interface #3280 Git package: lazy GitInformation, honest nullability
Module.Configuration race and Tags allocating per access #3281 Module.Configuration race, Tags allocation
GetModule<T> is an O(n) DI scan with SingleOrDefault #3282 GetModule O(n) DI scan
v4: consolidate the four overlapping module hook mechanisms #3283 Consolidate four overlapping hook mechanisms
v4: remove no-op DependsOnLazy and eagerly-evaluated DependsOnIf(Func<bool>) #3284 Remove no-op DependsOnLazy and eager DependsOnIf(Func)
v4: single dependency-declaration surface #3285 Single dependency-declaration surface
v4: composable skip/before/after configuration (8 last-wins WithSkipWhen overloads) #3286 Composable skip/before/after configuration
v4: consistent execute-method naming across Module, Module.NonGeneric and SyncModule #3287 Consistent execute-method naming
v4: retry API — WithRetry(count, baseDelay, shouldRetry); stop leaking Polly #3288 Retry API redesign; stop leaking Polly
v4: SkipDecision — drop surprising implicit conversions, init-only, document #3289 SkipDecision: drop surprising implicit conversions
v4: one registration/builder entry point; delete sync-over-async Build() #3290 One registration/builder entry point; delete sync-over-async Build()
v4: PipelineOptions — init-only record, options pattern, no Spectre global mutation in setters #3291 PipelineOptions: init-only, options pattern, no Spectre globals
v4: single result model — drop ModuleResultType and Is* triplication; PipelineSummary cleanup #3292 Single result model; PipelineSummary cleanup
v4: CommandException should carry the CommandResult, not inline stdout/stderr into Message #3293 CommandException carries CommandResult
v4: generate interfaces for tool sub-domain services (concrete unmockable types on IDotNet/IDocker facades) #3294 Generate interfaces for tool sub-domain services
v4: one command-builder abstraction (delete non-generic ICommandBuilder; tool builders implement the CRTP interface) #3295 One command-builder abstraction (CRTP)
v4: discoverable tool entry points — context.Tools.* instead of extension methods requiring usings #3296 Discoverable tool entry points (context.Tools.*)
v4: Async suffix on generated command methods; split the 78-member IGitCommands #3297 Async suffix on generated methods; split IGitCommands
v4: readonly collections in public API surface #3298 Readonly collections in public API
v4: remove global-namespace extension pollution (T.AsTask(), bool.AsSkipDecisionIfTrue) #3299 Remove global-namespace extension pollution
v4: IEnvironmentDomainContext.WorkingDirectory setter mutates process-wide CWD under parallel modules #3300 WorkingDirectory setter mutates process-wide CWD
v4: IHttp should not hand out raw HttpClients (bypasses secret masking, resilience and logging) #3301 IHttp: no raw HttpClients
Docs/XML sweep: v2 APIs still taught (PipelineHostBuilder in README and docs, non-compiling examples, broken crefs) #3302 Docs/XML sweep: v2 APIs still taught
Source generator & analyzers
SourceGenerator: incremental caching broken (Location and ImmutableArray in model records) #3303 Incremental caching broken (Location, ImmutableArray equality)
SourceGenerator: predicates run semantic analysis on every type in every project #3304 Predicates run semantic analysis on every type
SourceGenerator: duplicate module class names silently collapse into one extension method #3305 Duplicate module names silently collapse
SourceGenerator: add MPG#### diagnostics instead of silent skips #3306 MPG#### diagnostics instead of silent skips
Add source-generator snapshot and incremental-cache tests #3307 Source-generator snapshot + incremental-cache tests
Packaging: generator/analyzer DLLs packed from hardcoded bin paths without build ordering #3308 Generator/analyzer DLLs packed from hardcoded bin paths
Analyzers: AsyncModule and AwaitThis dead since ModuleBase removal; StatefulModule has a dead branch #3309 AsyncModule/AwaitThis dead since ModuleBase removal
Consumer analyzer test suite never runs in CI; test ref assemblies mislabeled #3310 Consumer analyzer tests never run in CI; ref assemblies mislabeled
MissingDependsOn analyzer never fires on context.GetModule<T>() and ignores generated accessors #3311 MissingDependsOn never fires on real usage
Analyzers: renumber to a single MP#### ID family, add HelpLinkUri, generate per-rule docs #3312 MP#### IDs, HelpLinkUri, generated per-rule docs
MPDEP002 claims circular-dependency detection but only finds direct 2-cycles #3313 MPDEP002 only finds direct 2-cycles
New analyzer batch: unregistered module, async void, blocking calls, unflowed CancellationToken, duplicate DependsOn #3314 New analyzer batch (unregistered module, async void, blocking, CT flow)
Add code fixes for the six analyzer rules without one #3315 Code fixes for the six rules without one
Analyzers and StyleCop are disabled in CI - add an analyzer lane #3316 Analyzers/StyleCop disabled in CI — add analyzer lane
Options generator & tool packages
Docs & first-run experience
CI
Tracking issue for the v4 scoping audit: multi-agent review of engine internals, core API surface, source generators/analyzers, options generator, docs, and CI job output across ~25 workflow runs. 100 issues raised; breaking API-reshaping issues carry the
breakinglabel and the v4.0.0 milestone.Related pre-existing open issues: #3228, #3226, #2996, #2623.
Priorities
Every issue carries a priority label — filter with
label:P0etc.:Recommended working order
Phase 0 — stop the bleeding (P0, releasable on 3.x):
#3337 (main has had no CI since April — fix first so everything else is guarded) → #3244 (silently skipped modules) → #3253 (AlwaysRun cleanup can no-op) → #3240 (TCS/lock recursion) → #3251 (startup hang on diamond graphs) → #3267 (orphaned process trees) → #3279 (distributed value-type crash; breaking fix, may need a 3.x mitigation + full fix in v4).
Phase 1 — 3.x hardening (P1, non-breaking):
Phase 2 — v4 branch foundation (breaking, in dependency order):
#3272 (remove obsolete API — clears the ground) → #3273 (context hierarchy) → #3274 (registry → source-gen) → #3290 (single builder entry point) → #3292 + #3279 (result model) → #3283 (hooks) → then the remaining milestone items in any order; #3320 (generated renames) last so tool packages regenerate once.
Phase 3 — P2 improvements, opportunistically alongside phases 1-2. Phase 4 — P3 whenever.
Engine correctness bugs
Engine performance & robustness
v4 API reshaping (breaking, milestone v4.0.0)
Source generator & analyzers
Options generator & tool packages
newon shadowed generated properties (CS0108)Docs & first-run experience
CI