# p0147h: DI + Data File Splits

- [Implementation] Six oversize files (3 ServiceCollectionExtensions at 359/188/242 lines, 3 data files ContextKeys/CommandNames/PipelinePresets at 171/202/289 lines) split into per-subdomain partial files — each split file ≤80 lines target, all final files ≤120 line hard limit.
- [Architecture] Helper-method splits stay INSIDE the same `static partial class` (not separate extension classes). Reason: `AddAgentSmith()` / `AddAgentSmithInfrastructure()` / Server's per-method internals remain the single public surface; reviewers + operators see one entry point per layer. Internal organisation is for readability, not external composition.
- [Architecture] Constants stay on their original class via `partial class` (ContextKeys, CommandNames, PipelinePresets). Reason: every caller in the codebase references `ContextKeys.X` / `CommandNames.Y` / `PipelinePresets.Z` — a rename would touch hundreds of call sites and add zero value. Partial preserves the surface; grep counts pre/post-refactor are byte-identical (684 ContextKeys, 395 CommandNames, 80 PipelinePresets references — all compile unchanged).
- [Implementation] Application's `ServiceCollectionExtensions` (359 → 47 lines + 7 partials): split by subdomain — `AddSkillRuntime` (p0126b/c collaborators), `AddPipelineHandlers` (basic command handlers), `AddTriageServices` (p0111c triage + p0125b activation), `AddSecurityHandlers` (scanners + API security), `AddBootstrapHandlers` (p0125c/d concept publication + p0130a/c bootstrap chain), `AddContextBuilders` (KeyedContextBuilder registrations), `AddPipelineRuntime` (executor + DataFlows + Sandbox + ToolKit).
- [Implementation] Infrastructure's `ServiceCollectionExtensions` (188 → 52 lines + 7 partials): split into `AddTicketProviders`, `AddSourceProviders`, `AddChatClients`, `AddOutputStrategies`, `AddSecurityScanners`, `AddDialogue`, plus a `ConfigLoading` partial holding the `LoadNucleiConfig` / `LoadZapConfig` / `FindConfigFile` YAML helpers.
- [Implementation] Server's `ServiceCollectionExtensions` (242 → 72 lines + 6 partials): split into `Redis`, `Webhooks`, `LongRunning`, `Intent` (engine + handlers), `PlatformAdapters` (Teams + Slack), `JobSpawner`. Each method is independently called from Program.cs so the split is mechanical — no entry-point coordination needed.
- [Architecture] ContextKeys further split into 7 partials beyond the YAML's planned 4 (Pipeline/Security/Api/Bootstrap), adding Plan + Skill: 101 keys split as core (TicketId, Repository, SourcePath, …), Pipeline (RunId, Sandbox, FailedStepName, ActivePhaseStep, …), Plan (Plan, CodeChanges, PlanJson, OpenQuestionsAwaitingAnswer, EmptyPlanSkipped, …), Skill (ActiveSkill, TriageOutput, VerifyRoundCount, ConceptVocabulary, …), Security (StaticScanResult, SecurityTrend, SkillObservations, …), Api (SwaggerSpec, NucleiResult, ApiTarget, …), Bootstrap (SkillCandidates, InitMode, WrittenTickets, …). Driven by the ≤80-line target — a single Pipeline partial would have hit 120 lines.
- [Implementation] CommandNames split: progress-label map (the 60+ `Labels` dictionary entries) extracted to `CommandProgressLabels.cs` as a separate partial; per-subdomain constants split into `CommandNames.Pipeline.cs`, `CommandNames.Security.cs`, `CommandNames.Api.cs`. Retired-command migration table (`RetiredCommands` + `TryGetRetirementMessage`) stays in the entry `CommandNames.cs` since it's the operator-facing diagnostic surface.
- [Implementation] PipelinePresets: one file per preset (`PipelinePresets.FixBug.cs`, `…SecurityScan.cs`, etc.) — 10 partials of 20-34 lines each. Each preset's command list is a one-file change that doesn't touch any other preset. Resolver + type-map + skills-path-map stay in `PipelinePresets.cs`.
- [Architecture] PipelinePresets static dictionary initialization moved into a `static PipelinePresets()` constructor. Reason: C# spec does NOT guarantee static field initialization order across partial-class files. Inline-initializing `All = new Dictionary<…> { ["fix-bug"] = FixBug, … }` in `PipelinePresets.cs` while `FixBug` lives in `PipelinePresets.FixBug.cs` produced 52 test failures (`IsSinglePhase("fix-no-test")` returned `true` because the field was still `null` when `All` was populated). Static constructors run AFTER all field initializers regardless of file order — guaranteed-correct + zero-cost.
- [TradeOff] Three split files end up slightly over the 80-line target but under the 120 hard limit: `CommandNames.Pipeline.cs` (81), `CommandProgressLabels.cs` (87), `PipelinePresets.cs` (117). Each was at a natural domain boundary — splitting further would have produced per-section files of <15 lines and increased file-count noise more than it reduced per-file LOC. Acceptable trade.
- [Implementation] Coordination protocol with parallel-running phases (p0147b/c/d/e/f): the subdomain-helper pattern means each parallel agent's new service registration drops into one helper without colliding. The same-class `partial` structure preserves the single-entry-point surface so `AddAgentSmith()` continues to call all subdomain helpers from one place.
- [Implementation] Caller-side zero-impact verified post-refactor: full test suite green (1689 + 66 = 1755 tests, identical pass count to pre-refactor). Build is warning-clean (5 pre-existing `SandboxToolHost` obsolete warnings unchanged, no new warnings from the split).
