Core fact resolvers are organized by fact category, not one monolith
Core fact discovery lives in internal/engine/core.go — 6,256 lines, 369 functions, 99 runtime.GOOS comparisons, with a 195-line buildCoreFacts orchestrator that inlines ~50 resolver calls. Every fact category (networking, processors, memory, dmi, disks, ssh, selinux, identity, uptime, …) and every platform’s logic for it share that one file, so changing one category means navigating all of them. The project already split the cloud metadata resolvers (ec2.go, gce.go, az.go) and virtualization (virtual.go) into per-category files following an currentXInput (impure probe) + detectX (pure logic) seam; the rest of core never got the same treatment.
We adopt per-fact-category resolver modules as the standard organization for core facts:
- One file per fact category (
networking.go,processors.go,memory.go,os.go,dmi.go,disks.go,ssh.go,selinux.go,identity.go,uptime.go, …), each holding that category’s logic for all supported platforms. A category file keeps the pureparse*/*ForPlatformfunctions that are the cross-platform unit-test surface. - Each category exposes a dedicated assembly function —
networkingCoreFacts(s *Session) []ResolvedFact,processorsCoreFacts(s *Session) []ResolvedFact, and so on.buildCoreFactsbecomes the ordered composition of those calls, not a 195-line literal. A test can resolve and assert one category in isolation against a fake host. - This is behavior-preserving: the resolved fact names, values, post-collection ordering, and platform behavior are identical. It is a locality and navigability change, not an output-contract change.
Two constraints that must not be re-crossed:
- Platform splits within a category must not use Go’s reserved GOOS suffixes. A file named
networking_windows.go,*_linux.go,*_darwin.go, or*_freebsd.gois given an implicit build constraint by the Go toolchain and compiles only on that OS. The Windows networking parsing logic is deliberately tested on Linux/macOS CI through thegoos-string parameter seam (AGENTS.md: “platform logic should be tested through fixtures or injected probes”); a GOOS-suffixed file would exclude that logic from other platforms’ builds and tests, silently breaking the seam. When a category file grows unwieldy and a hybrid by-platform split is warranted, use a non-reserved name (e.g.networking_msft.go), never a GOOS suffix. Genuine syscall-bound code that should be GOOS-constrained already lives in its own tagged files (statfs_linux.go); this convention is about the cross-platform resolver/parse logic. - This split does not change function signatures. Collapsing the
commandRunner/fileReaderparameters intoSessionmethods is a separate, already-deferred follow-on (recorded in the 2026-06-17 deepen-engine-internals design). The category split moves functions and adds per-category assembly funcs; it does not rewrite their parameters, so the two changes stay independently reviewable.
Considered Options
- Leave core.go as one file — rejected: locality is zero. 369 functions and every platform’s logic in one 6,256-line file means a change to one category is read and reviewed against all of them, and AI/code navigation has no per-concept anchor. The deletion test confirms the file earns its keep (the complexity is real), but it has no internal seam.
- Split by platform (
linux.go,darwin.go,windows.go,freebsd.go) — rejected: scatters a single category across four files, so a “networking” change touches all of them, and it fights thecurrentXInput+detectXpattern that keeps a category’s platforms side by side. Thegoos-string parameter seam already lets one file test all platforms, so a by-platform file split buys nothing and costs cohesion. - Split by category, leaving
buildCoreFactsintact — rejected as the end state: relocating helpers improves locality of the helpers but leaves the 195-line orchestrator as a second place every category still touches. Per-category assembly functions give each category file end-to-end ownership of its slice. - Split by category with per-category assembly functions (chosen) — each category owns its resolvers and its assembly;
buildCoreFactsis their composition; behavior is preserved; the GOOS-suffix and signature-stability constraints above keep it safe and independently reviewable.