0071 csrmatrix moves to an abstractions package - CyrilB1531/lodestar GitHub Wiki
0071 β CsrMatrix moves to a Lodestar.Abstractions package
Status: accepted Β· Amends: 0069 Β· Date: 2026-09-01
Context
Decision 0069 recorded
Lodestar.Abstractions as decided against, and the reasoning was sound at the time:
#427 argued for it conditionally β the shared
primitives must live in one dependency-free package "or splitting produces duplicated types and
circular dependencies" β and neither happened. A package built against a failure that did not occur
is a package nobody needs.
0069 also left one thing open, by name: "Whether a second, third and fourth edge into
Lodestar.Text stays acceptable⦠is for whoever opens the first of those lots."
#440's lot 3 is that lot β TruncatedSvd reads
a CsrMatrix and nothing else of Lodestar.Text β and this record is that answer.
Decision
CsrMatrix and SparseNorm move to a new Lodestar.Abstractions package, in a
Lodestar.Abstractions namespace. Lodestar.Decomposition will depend on Abstractions alone, and
Lodestar.Text becomes the type's second consumer rather than its owner.
Why not the edge into Lodestar.Text
Because of what it makes every later consumer carry. A package that wants a sparse matrix and two
products would take the distances, the phonetics, the stemmers, the tokenizers, the vectorizers and
the persistence layer with it β and System.Text.Json on netstandard2.0, which is the one
dependency Lodestar.Text deliberately has and Abstractions deliberately does not.
0069's own first rule reads the other way here: split only for a distinct dependency profile, audience or release cadence. The dependency profile genuinely differs, and that is the rule's first clause rather than a reading of its last.
What it costs, stated rather than softened
- A breaking source change.
Lodestar.Text0.5.0 no longer declares the type; everyusing Lodestar.Text.Vectorization;that touchesCsrMatrixchanges. About two dozen source files, the sample, the executed snippets, seven reference pages, three decision records and the README. - Three pull requests separated by two releases nobody can automate.
src/references published packages and never projects (0069 rule 3), soLodestar.Abstractions0.1.0 must be on nuget.org beforeLodestar.Textcan reference it, andLodestar.Text0.5.0 beforeLodestar.Decompositioncan be built against the pair.LodestarUseProjectRefsis a local loop, not a merge strategy. - An
InternalsVisibleTofromAbstractionstoText.CreateUncheckedskips the structural validation and is documented "never call it with data that came from outside";CountVectorizer,TfidfTransformerandHashingVectorizercall it four times between them. The grant names a package in the opposite direction to the dependency, which reads wrong and is inert at run time. It ships in 0.1.0 rather than later, because adding it afterwards would cost another release of the package whose whole purpose is being published first.
Options refused
A type-forward keeping Lodestar.Text.Vectorization as the namespace. It buys full source and
binary compatibility: no using changes, no version bump beyond a patch. Refused because it would
leave Lodestar.Abstractions declaring, for the life of the package, a type in a namespace that
names a different package β a permanent lie about where the type lives, bought to spare a
find-and-replace in a pre-1.0 library where semver already allows the break.
A namespace inside Lodestar.Text. By far the cheapest: no package, no edge, no release
sequence, and this record would not exist. Refused for the reason above β and because deferring
0069's open question a second time does not make it easier, it makes the answer more expensive.
Making CreateUnchecked public. It would remove the InternalsVisibleTo, at the price of
exporting a factory whose documentation says not to call it. A footgun is worse than an attribute
that reads backwards.
Consequences
- 0069's "
Lodestar.Abstractionswas never built" paragraph and its "What this does not settle" section are what this amends. Its rules 1, 2 and 3 stand unchanged and now cover six packages. - The layout gains one edge and loses none:
Text β Abstractions,Decomposition β Abstractions, andFuzzy β Textas before. There is still no cycle and no duplicated public type once step B lands. - Between steps A and B the type is duplicated, and the quality gate measures it. Two copies
differing only in their first line are 33.3 % duplication on step A's new code against a 3 %
gate β the duplication is what the step is, not a defect in it.
sonar.cpd.exclusionsnames that one file, and the comment beside it says step B deletes both the copy and the exclusion. 1.0.0is no longer un-gated by this question, which 0069 had settled by deciding against the package. It is settled the other way instead, and the release sequence above is what it now waits on.