Arch-Traits Scorecard — Chunk 5#
Traits: T1 narrow interfaces · T2 no globals/init · T3 explicit composition root · T4 time/IO injected · T5 functional core · T6 concrete internal collaborators · T7 public helper/builder API · T8 package boundary discipline Scores: 0=absent · 1=weak/partial · 2=clear · 3=textbook
Score table#
| Project | T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | Σ |
|---|---|---|---|---|---|---|---|---|---|
| P42-headscale | 2 | 1 | 3 | 1 | 2 | 1 | 1 | 1 | 12 |
| P43-tailscale | 3 | 1 | 2 | 2 | 2 | 1 | 3 | 1 | 15 |
| P44-wireguard-go | 3 | 3 | 3 | 2 | 3 | 3 | 2 | 2 | 21 |
| P45-delve | 2 | 2 | 3 | 1 | 3 | 2 | 1 | 3 | 17 |
| P46-air | 3 | 3 | 3 | 1 | 1 | 3 | 1 | 1 | 16 |
| P47-pop | 3 | 1 | 1 | 1 | 1 | 1 | 2 | 1 | 11 |
| P48-pocketbase | 2 | 1 | 3 | 2 | 2 | 1 | 3 | 2 | 16 |
| P49-nats-server | 2 | 3 | 3 | 2 | 3 | 3 | 2 | 3 | 21 |
| P50-temporal | 3 | 2 | 3 | 3 | 3 | 1 | 3 | 2 | 20 |
| P51-crush | 2 | 3 | 3 | 2 | 2 | 2 | 1 | 3 | 18 |
Evidence#
P42-headscale (Σ=12)#
- T1=2:
report:P42-headscale--interfaces.md— AutoApprover(3m), AuthProvider(4m), nodeConnection(5m) are narrow; ControlServer(31m) and TailscaleClient(35m) are god interfaces - T2=1:
report:P42-headscale--patterns.md— promauto.NewCounterVec called at package level via promauto init() side effects; architecturally central - T3=3:
report:P42-headscale--architecture.md— NewHeadscale() documented as explicit composition root; manual DI; all deps passed via constructor - T4=1:
report:P42-headscale--patterns.md— 347 context.Context uses but no clock injection; time.Now() ambient - T5=2:
report:P42-headscale--architecture.md— layered monolith; some domain separation but no pure-logic packages isolated from IO - T6=1:
report:P42-headscale--interfaces.md— ControlServer(31m) and TailscaleClient(35m) suggest broad interface use beyond just external seams - T7=1:
report:P42-headscale--api-surface.md— gRPC/REST/CLI API only; no dedicated embedding API or test helper packages - T8=1:
report:P42-headscale--structure.md— no internal/; hscontrol/ is main package; layered by convention only
P43-tailscale (Σ=15)#
- T1=3:
report:P43-tailscale--interfaces.md— consumer-side interface definitions confirmed: ipnLocalBackend defined in ssh/ consuming ipnlocal; Observer(1m), StateStore(2m), Extension(3m) are narrow; pattern explicitly used - T2=1:
report:P43-tailscale--patterns.md— feature.Hook registers in init() for optional subsystem feature registration; architecturally central - T3=2:
report:P43-tailscale--architecture.md— tsd.System is explicit DI container; explicit wiring partially undermined by init()-based feature.Hook registrations - T4=2:
report:P43-tailscale--patterns.md— logger.Logf injected as func parameter (DI via function type); context pervasive; no clock injection - T5=2:
report:P43-tailscale--interfaces.md— net/dns.OSConfigurator(4m) at OS boundary; some pure packages; mixed overall at this scale - T6=1:
report:P43-tailscale--patterns.md— feature.Hook adds implicit indirection between internal subsystems; some interface-everywhere patterns - T7=3:
report:P43-tailscale--api-surface.md— tsnet library is rich public embedding API; dedicated client/local library surface; textbook integrator-facing design - T8=1:
report:P43-tailscale--structure.md— minimal internal/; flat public namespace; feature/ build flags
P44-wireguard-go (Σ=21)#
- T1=3:
report:P44-wireguard-go--interfaces.md— only 5 interfaces total; BindSocketToInterface(2m) and PeekLookAtSocketFd(2m) are capability extension interfaces; “minimum necessary abstraction” is stated philosophy - T2=3:
report:P44-wireguard-go--patterns.md— no init() side effects documented; no package-level singletons; 5 direct deps; no promauto/registry patterns - T3=3:
report:P44-wireguard-go--architecture.md— NewDevice(tun tun.Device, bind conn.Bind, logger *Logger); textbook 3-parameter composition root; nothing hidden - T4=2:
report:P44-wireguard-go--patterns.md— tun.Device and conn.Bind are injected IO interfaces; //go:linkname fastrandn is ambient randomness (not injected); no clock injection - T5=3:
report:P44-wireguard-go--structure.md— ratelimiter/, replay/, tai64n/ packages have zero intra-project deps; purely algorithmic; no IO - T6=3:
report:P44-wireguard-go--architecture.md— no interfaces between client→Sublist→Account within server package; interfaces only at tun.Device and conn.Bind IO seams - T7=2:
report:P44-wireguard-go--api-surface.md— tun/tuntest and conn/bindtest provide in-memory test doubles; clear embedding story; no dedicated examples package - T8=2:
report:P44-wireguard-go--structure.md— no internal/ (deliberate: library-first); clean package layout device/tun/conn/ratelimiter; no import cycles
P45-delve (Σ=17)#
- T1=2:
report:P45-delve--interfaces.md— MemoryReader(1m), LocationSpec(1m), service.Server(2m) are narrow; service.Client(~50m) is a god interface; Thread(10m) and Registers(8m) are wider - T2=2:
report:P45-delve--patterns.md— sync.Once for CPU feature detection is justified one-time init; no architectural init() side effects; vendor/ present but unrelated - T3=3:
report:P45-delve--architecture.md— execute() is the documented composition root; manual Config struct wiring; 4-layer system CLI→Service→Debugger→Proc all wired explicitly - T4=1:
report:P45-delve--patterns.md— ~10 context.Context uses (minimal, intentional for debugger domain); no clock or IO injection - T5=3:
report:P45-delve--structure.md— pkg/dwarf and pkg/proc have no service layer deps; service/ imports from pkg/ but not vice versa; clean directional layering - T6=2:
report:P45-delve--interfaces.md— ProcessInternal extends Process via interface; some excess interface use; mostly appropriate - T7=1:
report:P45-delve--api-surface.md— JSON-RPC 2.0 + DAP; embeddable in principle but no dedicated embedding/test API - T8=3:
report:P45-delve--structure.md— Standard Go Layout cmd/pkg; service/ layer distinct from pkg/; clean pkg/dwarf, pkg/proc, pkg/proc/amd64 decomposition
P46-air (Σ=16)#
- T1=3:
report:P46-air--interfaces.md— only 2 project-owned interfaces: exiter(1m) for test seam, Streamer(5m) for I/O; no unnecessary interfaces - T2=3:
report:P46-air--patterns.md— no init() side effects; single Engine struct owns all state; atomic.Bool for state; no package singletons - T3=3:
report:P46-air--architecture.md— Engine struct created in main.go; all deps via struct fields; clear single wiring point - T4=1:
report:P46-air--patterns.md— zero context.Context usage; custom channels for cancellation; FileWatcher interface exists but no clock injection - T5=1:
report:P46-air--structure.md— flat single runner/ package; logic and IO mixed in same package; no pure-logic isolation - T6=3:
report:P46-air--interfaces.md— only exiter(1m) and Streamer(5m); all other internal collaborators are concrete structs - T7=1:
report:P46-air--api-surface.md— application binary only; no public library or embedding API - T8=1:
report:P46-air--structure.md— no internal/; flat root-level main.go + runner/ package; minimal boundary discipline
P47-pop (Σ=11)#
- T1=3:
report:P47-pop--interfaces.md— 11 single-method lifecycle callbacks (BeforeSaveable, AfterCreateable, etc.); TableNameAble(1m), PaginationParams(1m); textbook optional extension interfaces - T2=1:
report:P47-pop--patterns.md— dialect factories registered via init() in each dialect_*.go; global Connections map; var Debug bool; var ConfigName string - T3=1:
report:P47-pop--patterns.md— NewConnection() exists but init()-based dialect auto-registration distributes composition; global Connections map populated by LoadConfigFile() - T4=1:
report:P47-pop--patterns.md— time used for created_at/updated_at (likely time.Now() internal); no clock injection; context propagation via contextStore but not injected - T5=1:
report:P47-pop--structure.md— root package is dense hub (72 .go files); sub-packages (columns/, slices/) are somewhat pure but root mixes IO and domain logic - T6=1:
report:P47-pop--interfaces.md— dialect(~21m) and store(18m) are wide internal interfaces; core collaboration is interface-based internally - T7=2:
report:P47-pop--api-surface.md— well-documented fluent Connection/Query API; public columns/, slices/, associations/ packages; genny/ for scaffolding - T8=1:
report:P47-pop--structure.md— internal/defaults and internal/randx are minimal; root package has 72 files; no strong boundary discipline
P48-pocketbase (Σ=16)#
- T1=2:
report:P48-pocketbase--interfaces.md— Mailer(1m), RecordProxy(2m), Resolver(1m), optional extensions MaxBodySizeCalculator/SetterFinder(1m each) are narrow; core.App(~150m) is the acknowledged god interface - T2=1:
report:P48-pocketbase--patterns.md— core.SystemMigrations.Register() in init(); Fields registry populated via optional init() registration; architecturally central - T3=3:
report:P48-pocketbase--architecture.md— examples/base/main.go is canonical composition root; pocketbase.New() wires all components; core.App passed explicitly to every subsystem - T4=2:
report:P48-pocketbase--interfaces.md— Mailer and blob.Driver interfaces mean IO is injectable; 197 context.Context uses; no clock injection - T5=2:
report:P48-pocketbase--structure.md— tools/ sub-packages (hook, store, types, tokenizer, search) are pure utilities; core/ mixes DB and domain logic - T6=1:
report:P48-pocketbase--patterns.md— core.App(~150m) passed as the single dependency everywhere; every package depends on the fat interface rather than concrete types - T7=3:
report:P48-pocketbase--api-surface.md— examples/base as canonical library reference; tests/ fixtures; hook-based plugin system; rich Go embedding story - T8=2:
report:P48-pocketbase--structure.md— no internal/ (deliberate for embeddability); clear core/apis/forms/plugins layering; tools/ as utility library; good discipline
P49-nats-server (Σ=21)#
- T1=2:
report:P49-nats-server--interfaces.md— Authentication(1m), DeleteBlock(2m), WAL(12m) are narrow; RaftNode(53m) and StreamStore(44m) are justified-wide system ports; bimodal distribution - T2=3:
report:P49-nats-server--patterns.md— no init() side effects; CONTRIBUTING.md explicitly discourages new deps; Server struct owns all state; no package singletons - T3=3:
report:P49-nats-server--architecture.md— NewServer(opts *Options) is the explicit composition root; ~300-line function; all state visible in Server struct fields - T4=2:
report:P49-nats-server--dependencies.md— internal/fastrand avoids global mutex; Logger is injectable interface; no clock injection - T5=3:
report:P49-nats-server--structure.md— server/stree, server/avl, server/gsl, server/ats, server/thw are pure data-structure/algorithm packages; conf/ is pure parser; no IO in these packages - T6=3:
report:P49-nats-server--architecture.md— deliberate: no interface indirection between client/Sublist/Account on hot paths; interfaces only for StreamStore/ConsumerStore/RaftNode at behavioral-substitution seams - T7=2:
report:P49-nats-server--api-surface.md— server package exported for embedding; NewServer/InProcessConn/HTTPHandler; test/ black-box package; functional but not elaborate - T8=3:
report:P49-nats-server--structure.md— internal/ for OCSP/LDAP/fastrand (security primitives); server/ sub-packages for algorithms; clear OS-portability via build tags in pse/ and sysmem/
P50-temporal (Σ=20)#
- T1=3:
report:P50-temporal--interfaces.md— dynamicconfig.Client(1m), Authorizer(1m), ClaimMapper(1m), hsm.Environment(2m), StateMachineDefinition(4m) are narrow and consumer-defined; architectural migration from MutableState(80m) to narrow HSM interfaces is ongoing - T2=2:
report:P50-temporal--architecture.md— fx DI removes need for init() globals; hsm.Registry registration via fx.Invoke not init(); 2,555-file codebase makes certainty partial - T3=3:
report:P50-temporal--architecture.md— temporal/fx.go TopLevelModule + fx.New() is explicit; nested fx.App per service; everything wired via providers;file:temporal/fx.go - T4=3:
report:P50-temporal--patterns.md— goro.AdaptivePool backed by injectable clock.TimeSource; 8,416 context.Context references; common/clock package; textbook time injection - T5=3:
report:P50-temporal--patterns.md— common/tasks, common/goro, common/backoff, common/collection are pure logic; service/history/hsm has pure state machine logic separated from IO - T6=1:
report:P50-temporal--architecture.md— fx DI means every component is declared as interface in fx.In structs; internal collaborators are interface-typed by framework convention - T7=3:
report:P50-temporal--api-surface.md— temporal/ package for embedding; temporaltest/ for test servers; WithAuthorizer/WithDynamicConfigClient functional options; textbook integrator API - T8=2:
report:P50-temporal--structure.md— no internal/ (deliberate for embeddability); 60+ packages under service/history/api/ (one per RPC handler); clear layering; good discipline
P51-crush (Σ=18)#
- T1=2:
report:P51-crush--interfaces.md— PublisherT and SubscriberT are textbook narrow generics; MessageItem hierarchy (Identifiable 1m, Animatable 2m, Expandable 1m) is narrow; Workspace(~41m) is acknowledged facade - T2=3:
report:P51-crush--architecture.md— all code under internal/; no init() side effects; App struct owns all state; no package singletons documented - T3=3:
report:P51-crush--architecture.md— setupWorkspace() + app.New() is explicit composition root; manual DI throughout; all deps wired via constructor injection - T4=2:
report:P51-crush--patterns.md— 584 context.Context uses; proper ticker/ctx.Done() patterns for cancellation; no explicit clock injection; IO is cancellable - T5=2:
report:P51-crush--structure.md— internal/csync (pure generics), internal/pubsub (pure generics), internal/stringext, internal/filepathext are pure; agent loop is inherently IO-heavy - T6=2:
report:P51-crush--interfaces.md— domain service interfaces (session.Service, message.Service) exist mainly for testability; coordinator receives them as constructor params; reasonable internal use - T7=1:
report:P51-crush--structure.md— everything under internal/; no public library surface; REST API over Unix socket exists but is not a Go library - T8=3:
report:P51-crush--structure.md— aggressive internal/ usage for all ~50 packages; no accidental public API; textbook package visibility discipline for an application binary
Pattern observations#
T3 (composition root) is strong across this chunk: 8 of 10 projects score 2 or 3. Manual constructor injection dominates; only temporal uses a DI framework (fx). Pop(1) and tailscale(2) are the outliers, both held down by init()-based extension mechanisms.
T2 (no globals/init) splits the chunk sharply. Projects built as libraries (wireguard-go, nats-server) and application binaries (air, crush) score 3 by avoiding init()-based registration entirely. Projects with plugin/dialect/metric registration patterns (headscale, tailscale, pop, pocketbase) score 1. Temporal is a clean 2 via fx.
T6 (concrete internal collaborators) correlates with architectural philosophy. Performance-first servers (wireguard-go, nats-server, air) score 3 — they explicitly avoid interface indirection on hot paths. fx-based temporal scores 1 because fx mandates interface-typed dependencies throughout. Pop and pocketbase score 1 due to wide internal interfaces (dialect(21m), core.App(150m)).
T5 (functional core) separates the protocol/systems projects from the application-layer projects. wireguard-go, nats-server, delve, and temporal all have clear pure-logic packages (algorithm sub-packages, pkg/ layers, common/* utilities). air and pop score 1: single-package or dense-root structure prevents isolation.
T1 (narrow interfaces) shows an interesting split: projects that use single-method lifecycle interfaces (pop: 11×1m callbacks; pocketbase: optional extensions; temporal: Authorizer/ClaimMapper) score 3 even when they also have god interfaces. The score reflects whether the pattern is present and intentional, not whether it is applied universally.
Standout scores: wireguard-go and nats-server both reach Σ=21 — the highest in this chunk — through complementary strengths: wireguard-go via minimal abstraction at every layer; nats-server via deliberate hot-path concreteness combined with rich pure algorithm sub-packages.
Blind scoring — no –testing.md reports read. Confidence: high for all projects except P47-pop (api-surface read only for confirmation; 4 of 7 non-testing reports provide sufficient signal).