You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Layers conformance (#256)
* layers: stop three false-positive classes in pattern violations
- di/config packages become Neutral: classified, but in no dependency
direction, since every layer both uses them and is used by them.
- android-clean orders data innermost per Android's own guidance; ios-clean
keeps the opposite order on purpose — the two disagree about which way
domain and data depend.
- the Rails autoload rule no longer claims app/ children Rails does not
autoload, which is where front-end apps live.
* layers: confidence is graded/scanned, floored at 0.20 on the winner
Drops the layerCoverage term, whose denominator was the layer table rather than
the repository. Graded counts classified modules in non-neutral layers; both
counts are over distinct module names, since pattern.Modules is keyed by name.
The floor lives in thickEnough() on bestPattern()'s result, not in
detectPatterns(): applied at admission it dropped a thin framework-gated match
and let the ungated hexagonal pattern take the repo instead.
* layers: score each taxonomy over its language cohort
patternDef.languages gated on the repo's dominant language; it is now appliesTo,
the languages a taxonomy may classify, and cohort() restricts the modules it is
scored over. bestPattern becomes selectPatterns: strongest first, skipped when
its cohort overlaps one already claimed, so an ungated taxonomy yields instead of
competing everywhere. A floored winner still claims its languages.
Violation titles carry the taxonomy name — two cohorts can produce the same layer
pair. This changes finding identity, so pinned baselines churn once.
* layers: add php-layered, drop core from the hexagonal domain patterns
`core` named a container, not a layer: a platform keeping its product under
src/Core/ had 1049 of 1491 classified modules read as domain on that segment
alone, and 339 findings followed. Same argument the Angular taxonomy already
makes for the same word.
php-layered is gated on the language, not a framework — there is no single PHP
framework — and built from vocabulary that recurs across unrelated PHP trees.
Its listener/subscriber/provider/exception tier is Neutral.
countSignatureLayers stays presence-based. Requiring two modules per signature
layer drops one wrong statement and one right one, and neither an absolute nor a
relative threshold separates them; the wrong one is recorded in the corpus.
detectViolations still reads only `imports`, now with the measurement that says
why: 3065 module pairs reachable through calls/instantiates/injects/implements
across four repositories yield 5 wrong-direction edges.
* layers: add nuxt and sveltekit, record four ecosystems measured and left out
Both taxonomies come from the frameworks' prescribed directory structures, not
from what the corpus repositories share, so neither takes a signature gate — the
framework fact already settles what the repository is. Nuxt's server/ is the
Nitro backend, classified and unordered.
Python, Rust, Swift and the routers/services/models Go layout are documented
above patternDefs with what was measured: three of them have no vocabulary that
holds across unrelated repositories, and the fourth would re-rank pkg/services
everywhere on the evidence of one example.
* llmcontext: reserve the architecture sections, derive the feature guide
On two corpus repositories the repository map spent all 64,000 characters before
the architecture section was reached, so the file rendered a module list and no
statement at all. Architecture Pattern and the guide are now reserved, and the
layer mapping is capped per pattern.
layers.GuideFor exports the taxonomy order with an example module, replacing a
switch that had prose for a few taxonomies by name and generic advice for every
other repository. Layers of one level render together — rails-mvc puts a dozen
directories on its domain tier deliberately, and numbering them asserted a
sequence that does not exist. No recognised order now emits no section.
* llmcontext: bound the repository map and cap any one section's share
The map was one unbounded row per module sorted by name, so a large repository
spent the whole budget on an alphabetical census and every later section was
omitted. Above maxRepoMapRows it summarises by area with the largest modules;
entry points group by kind, listing mains in full and sampling populations.
The share cap is the real fix: bounding sections one at a time moved the problem
down the list twice. A section over its share is truncated, not dropped.
Repo labels are prefixed to area names only when the snapshot holds more than
one repository.
* llmcontext: summarise the remaining per-item sections
Routes group by path prefix, storage by kind, dependency edges by out-degree —
which no other section reports, since Critical Modules ranks fan-in. Risk Zones
is capped and ordered by confidence; it was emitted in explainer order, so a
truncation kept whichever findings came first rather than the likeliest.
With the map and entry points from the previous commit, a large monolith's digest
goes from 64,000 characters holding two sections to 28,000 holding ten, with no
truncation left to do.
* explain: report every cohort's architecture, not just the strongest
Report kept one Architecture string, so a repository with two layer orders in
force reported one and dropped the other. Architectures carries all of them; the
scalar stays as the strongest so the JSON shape does not move.
Verified against a pre-change baseline: check --fail-on=layers exits 0 across the
violation-title change, which lands as incidental shifts rather than new findings.
* layers: record .NET as measured and left out
Five .NET repositories share no order beyond the clean-architecture layout
already covered. The one that looks layered is the trap: a media server's
MediaBrowser.Controller is its domain abstractions assembly, so matching
"Controller" across dotted project names — the only way .NET matching works —
would read fifty modules of interfaces as a delivery layer.
* layers: prefer unordered layers when classifying a module
Matching is position-blind, so a wiring directory nested inside an ordered one
took the enclosing layer: core/data/…/data/di classified as data, …/service/config
as service. Neutral-first is the fail-safe direction — misclassifying into a
neutral layer silences a verdict, out of one invents a verdict about a directory
every layer references by design.
* Updating docs
* facts: add Insight.Metrics, and build the feature guide from it
Explainers state their numbers in prose and every caller parses them back out:
pkg/explain regexes ints from titles, bench-arch regexed "N of M modules
classified" until the cohort language was added to that sentence. Metrics is the
machine-readable mirror, shaped like Fact.Props and read through accessors that
tolerate the float64 a round-trip leaves.
It mirrors rather than replaces: internal/diff enumerates Title, Confidence,
Description and Evidence, so a number living only in Metrics would stop being
visible to the gate. A test pins both halves of that.
layers publishes its denominators, conformance counts and layer order for
recognised AND declared patterns, which is what lets renderFeatureGuide serve a
repository that states its own architecture — enola's own tree included.
GuideFor is removed.