0111 the generalized linear model does not earn its own package - CyrilB1531/lodestar GitHub Wiki
0111 โ The generalized linear model does not earn its own package
Status: accepted ยท Date: 2026-09-12
Context
Decision 0104 named what would be built and left where deliberately open:
Whether it earns a package at all is the question 0096 answered on audience, and it is answered the same way โ with a measurement โ when #616's spec is written, not before.
The spec (docs/superpowers/specs/2026-09-11_0616_generalized-linear-models.md, "Placement,
deliberately deferred") named the test: decision 0076
allows a split only for a distinct dependency profile, audience or cadence. It also named one input
that might have decided the question on its own, before any of the three: whether Poisson's
log-likelihood forced Lodestar.Stats' internal Gamma.LogGamma public, which the spec called
"one argument for being its own thing, and one argument against."
That input resolved during implementation, before this record was due. Lodestar.Stats.Regression
reaches Lodestar.Stats through a PackageReference on a published floor
(CONTRIBUTING.md's Working across two packages), so a member opened there could not ship on this
branch regardless of which package the GLM lived in โ publication was refused on that ground
alone, not on placement. LogLikelihood.Of
(src/Lodestar.Stats.Regression/Internal/LogLikelihood.cs) computes log(y!) from the response
itself, with a Kahan-compensated cumulative table, and src/Lodestar.Stats/ carries no diff on
this branch. So the measurement that remains is the plain one #616 asked for: how much this added,
and whether it meets 0076's test.
The measurement
Taken on this branch, git diff --stat main...HEAD -- src/Lodestar.Stats.Regression/:
GeneralizedLinearModel.cs | 166 ++++++++
GlmFamily.cs | 17 ++
GlmOptions.cs | 25 ++
GlmSummary.cs | 58 ++
Internal/Families.cs | 48 ++
Internal/Irls.cs | 154 ++++++++
Internal/LeastSquares.cs | 116 ++++++
Internal/LogLikelihood.cs | 72 ++++
OrdinaryLeastSquares.cs | 114 +----------
9 files changed, 661 insertions(+), 109 deletions(-)
661 lines added, 109 removed โ and the 109 are OrdinaryLeastSquares.cs losing three private
methods (Design, Solve, StandardErrors) to the new Internal/LeastSquares.cs, which the
GLM's IRLS loop calls too. The extraction is itself evidence: OLS and the GLM share the same
design-matrix and covariance arithmetic closely enough that separating them into two packages
would have to either duplicate LeastSquares.cs or draw a PackageReference between two packages
for code neither one exposes publicly.
Four new public types, twenty-four new public members: GeneralizedLinearModel (one method,
Fit), GlmFamily (two values, Binomial and Poisson), GlmOptions (five init-only
properties), GlmSummary (sixteen init-only properties). Everything else the diff adds โ
Families, Irls, LeastSquares, LogLikelihood โ is internal static, reachable only inside
the assembly and its two InternalsVisibleTo test projects
(Lodestar.Stats.Regression.Tests and its NetStandard mirror). None of it is a public member
because none of it is declared public on a public type. The twenty-four count is the members a
reader can find in source code: source-declared, with accessors excluded, and constructors counted
only when explicitly written (not compiler-synthesized parameterless constructors or backing fields).
No neighbour's surface opened. Lodestar.Stats/Internal/Gamma.cs's LogGamma (line 37) is still
internal, unchanged by this branch. src/Lodestar.Stats.Regression.csproj still declares exactly
the two edges 0096 recorded โ Lodestar.Stats for the Student and Fisher tails, and
Lodestar.Decomposition for the Householder QR โ and no PackageReference beyond them.
Version.props still reads 0.1.0: the decision is taken before the bump, which is what makes it
a decision rather than a retrofit of one already made.
Against 0076's three criteria
Dependency profile. Identical, not merely similar. Before this branch the package carried zero
external dependencies and two internal edges; after it, the same zero and the same two. The GLM
took no dependency the OLS beside it did not already have, and the one input that could have
forced a new edge open โ LogGamma โ resolved to no publication at all, on a constraint that
would have applied wherever the GLM lived.
Audience. Indistinguishable from the audience already in the package. 0096 gave
Lodestar.Stats.Regression its own package on this exact criterion: "a caller who wants a
regression table wants none of [the ten hypothesis-test families in Lodestar.Stats], and a
caller running a Kruskal-Wallis wants no QR." A caller who wants a GLM's inference table wants
exactly what an OLS caller wants โ coefficients, standard errors, a test statistic, p-values,
intervals โ the same shape, fitted through a link function instead of an identity one.
GlmSummary and OlsSummary sit next to each other in the namespace, and GlmOracleTests and
OlsOracleTests sit next to each other in the same test project, both replaying a frozen
statsmodels corpus at the tolerance regime decision 0081
set โ GlmOracleTests reuses StatsOracleAsserts for it directly, where OlsOracleTests
predates that helper and asserts the same rule inline. Nothing here is the "wants none of it"
split 0096 measured; it reads closer to "wants more of the same."
Release cadence. No evidence of a distinct one. The GLM shipped inside the same unreleased
0.1.0, in the same commit range, sharing LeastSquares.cs with the OLS it sits beside โ code that
would need to move, be duplicated, or cross a package boundary if the two ever separated. A
distinct cadence is something a package earns by needing to ship on its own schedule; nothing
about this work has asked to.
None of the three reads distinct. 0076 requires one to license a split, and this measurement supports none of them.
Decision
The generalized linear model ships inside Lodestar.Stats.Regression, beside the ordinary least
squares it was built next to. No new package, no Version.props, no EXPECTED entry, no
FLOORS row, no *.NetStandard.Tests mirror, no wiki-map.json entry โ none of the fixed cost
#566 measured for a seventeenth package on
#616 is paid, because nothing here asked for
one. The repository stays at sixteen packages.
This settles 0104's open question the way 0104 said it would be settled: by measurement, against 0076, before the version bump.
Consequences
- The public surface
Lodestar.Stats.Regressionships past 0.1.0 now carriesGeneralizedLinearModel,GlmFamily,GlmOptionsandGlmSummarynext toOrdinaryLeastSquares,OlsOptionsandOlsSummaryโ one package, seven public types doing regression. - Nothing here is a package move, so none of #616's remaining documentation debt (a
bench/README.mdsection againstAccord.Statistics, adocs/wiki-map.jsonentry, reference pages, a sample, equivalence rows, a guide) is a second package's cost. It isLodestar.Stats.Regression's, exactly as it was for OLS. - Should a later family (negative binomial, Gamma, inverse Gaussian โ the ones 0104 named out of scope) change any of these three readings โ a dependency the OLS half never needed, a caller who wants the GLM table and nothing OLS-shaped, a release the OLS half is not on โ this record is what a future split cites as no longer true, rather than reopening 0076 from nothing.
- If a later branch judges differently, the move is owed before the next version bump, not
after:
GeneralizedLinearModel,GlmFamily,GlmOptionsandGlmSummaryare about to ship public in 0.1.0, and a published type cannot move packages without a deprecation cycle. That branch is its own issue and its own branch; this one ships insideLodestar.Stats.Regressioneither way.