1package main23import "strings"45// modulePath is the import path of this module. Every rule below reasons about6// package paths relative to it: an import that does not carry this prefix is7// either the standard library or a third-party dependency.8const modulePath = "github.com/FelineStateMachine/atlas"910// Lane names the architectural lane a package belongs to. Lanes are the unit11// the import matrix of issue #5 §3.2 is written in; they are a property of the12// package path, not of a bespoke directory taxonomy.13type Lane string1415const (16 LaneFormat Lane = "format"17 LaneGenerate Lane = "generate"18 LaneEnrich Lane = "enrich"19 LaneApp Lane = "app"20 LaneWorkbench Lane = "workbench"21 LaneCLI Lane = "cmd/atlas"2223 // LaneTests is the out-of-package test tree: suites that must read the24 // filesystem (the corpus, the shared cell vectors, the e2e registry)25 // against packages that may not. It has the run of the lanes it judges26 // and none of the producer duties, and the rules below say so by name.27 LaneTests Lane = "tests"2829 // LaneShell is the desktop shell at the module root: the Wails host of30 // issue #5 §3.4. It is a lane so that the rules which do apply to a host31 // entry — network confinement, semconv discipline — apply to it, and so32 // that the one that does not (hostenv purity, which a host entry exists33 // to violate) says so by name rather than by the shell happening to sit34 // outside the analysis.35 LaneShell Lane = "shell"3637 // LaneLogging is the one shared clean-room package: the leveled event38 // stream every lane narrates itself through (issue #5 §9,39 // docs/logging.md). It is a lane of its own so that who may import it is40 // answered by the matrix rather than by where it happens to sit —41 // everybody may, except format/, which depends on the standard library42 // alone and says so in its own rule.43 LaneLogging Lane = "logging"4445 // LaneOutside covers anything the module's rules do not own — tools/,46 // third-party paths, anything unheard of. Rules never fire on it: a rule47 // that is a total function of a package path needs an answer for a path48 // it has never heard of.49 LaneOutside Lane = ""50)5152// cleanRoomRoots are the module-relative directories the analyzers police, in53// the order a reader of §3.1 meets them. A root that does not exist yet simply54// contributes no packages, which is how rules for unwritten lanes pass55// trivially rather than erroring.56//57// shellRoot is the module root itself and means the one package there, never58// the tree beneath it — every other root is already named on its own line, and59// a recursive pattern here would analyze each of them twice.60var cleanRoomRoots = []string{61 shellRoot,62 "format",63 "internal/logging",64 "internal/generate",65 "internal/enrich",66 "internal/app",67 "internal/workbench",68 "cmd/atlas",69 "tests",70}7172// shellRoot names the module root in the vocabulary cleanRoomRoots is written73// in. The module-relative path of the root package is the empty string; "." is74// what a reader of a root list expects to see, and defaultPatterns and laneOf75// both translate.76const shellRoot = "."7778// lanePrefixes maps each lane to the module-relative path that defines it.79// Order matters only for readability; laneOf matches on exact path segments.80var lanePrefixes = []struct {81 lane Lane82 prefix string83}{84 {LaneFormat, "format"},85 {LaneLogging, "internal/logging"},86 {LaneGenerate, "internal/generate"},87 {LaneEnrich, "internal/enrich"},88 {LaneApp, "internal/app"},89 {LaneWorkbench, "internal/workbench"},90 {LaneCLI, "cmd/atlas"},91 {LaneTests, "tests"},92}9394// rel returns the module-relative path of an import path, and whether the95// import is module-local at all.96func rel(importPath string) (string, bool) {97 if importPath == modulePath {98 return "", true99 }100 if r, ok := strings.CutPrefix(importPath, modulePath+"/"); ok {101 return r, true102 }103 return "", false104}105106// laneOf classifies a module-relative package path.107func laneOf(relPath string) Lane {108 if relPath == "" || relPath == shellRoot {109 return LaneShell110 }111 for _, lp := range lanePrefixes {112 if relPath == lp.prefix || strings.HasPrefix(relPath, lp.prefix+"/") {113 return lp.lane114 }115 }116 return LaneOutside117}118119// laneOfImport classifies an import path, reporting LaneOutside for anything120// that is not a module-local clean-room package.121func laneOfImport(importPath string) (Lane, bool) {122 r, ok := rel(importPath)123 if !ok {124 return LaneOutside, false125 }126 return laneOf(r), true127}128129// isStdlib reports whether an import path names a standard-library package.130// The standard heuristic: stdlib paths have no dot in their first segment.131func isStdlib(importPath string) bool {132 first, _, _ := strings.Cut(importPath, "/")133 return !strings.Contains(first, ".")134}135136// under reports whether a module-relative path sits at or below prefix.137func under(relPath, prefix string) bool {138 return relPath == prefix || strings.HasPrefix(relPath, prefix+"/")139}