lifecycle

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package lifecycle is wowapi's STATIC provider/lifecycle manifest (backlog B9). It is deliberately NOT a runtime DI container: there is no reflection, no codegen, and no service locator. It is a small, hand-maintained descriptor model — ProviderDescriptor{Provides, Requires, Scope} — that captures the REAL provider graph wired by kernel.New (kernel/kernel.go), app.Boot/moduleContext (app/context.go, app/boot.go), and module.Context (module/module.go), plus a pure-function lint over that manifest that catches the wiring-mistake classes the framework-competitive-architecture- benchmark's "DI / IoC: Static Lifecycle Graph For Go" section calls out.

The manifest is maintained by hand alongside the wiring it describes (there is no generator reading kernel.go via reflection/AST) — Manifest() below IS the source of truth the lint runs over, and CurrentManifest's own test (TestCurrentManifestLintsClean) is the regression net: if a future change to kernel.New/app.Boot/module.Context adds a wiring mistake, either that test or a forgotten manifest update will need updating, keeping the manifest honest by review.

Index

Constants

View Source
const (
	ClassScopeLeak       = "scope_leak"        // (a) process-scoped depends on request-scoped
	ClassRawPool         = "raw_pool"          // (b) module receives a raw pool instead of TxManager
	ClassTenantEscape    = "tenant_escape"     // (c) tenant-scoped service escapes its transaction
	ClassMigrateInAPI    = "migrate_in_api"    // (d) migrate-only service wired into API runtime
	ClassMissingProvider = "missing_provider"  // (e) declared Requires has no matching Provides
	ClassCycle           = "cycle"             // (e) dependency cycle
	ClassDuplicate       = "duplicate_provide" // manifest hygiene: two descriptors with the same Provides
	ClassInvalidScope    = "invalid_scope"     // manifest hygiene: Scope is not one of the five recognized values
)

Violation classes, matching backlog B9's acceptance list verbatim so a reader can cross-reference the backlog item to the check that enforces it.

Variables

This section is empty.

Functions

This section is empty.

Types

type Manifest

type Manifest struct {
	Descriptors []ProviderDescriptor
}

Manifest is an ordered set of descriptors. Order is preserved for stable printing; lint functions are order-independent (they index by Provides).

func CurrentManifest

func CurrentManifest() Manifest

CurrentManifest is the hand-maintained descriptor graph for wowapi's ACTUAL wiring, read from kernel.New (kernel/kernel.go), app.Boot/moduleContext (app/context.go, app/boot.go) and module.Context (module/module.go) as of this package's introduction (backlog B9). It is not generated: whoever adds a field to Kernel/moduleContext/moduleDeps/module.Context should add or update the matching descriptor(s) here in the same change — the CI gate (`wowapi lint lifecycle`, and TestCurrentManifestLintsClean) exists so a forgotten or wrong update fails loudly rather than silently drifting.

Naming convention: "kernel.<Field>" for a Kernel struct field built in kernel.New; "module.Context.<Method>" for an accessor module.Context exposes (all of which are APIRuntime: true, since module.Register runs in every product's api/worker boot path).

func (Manifest) ByProvides

func (m Manifest) ByProvides() map[string]ProviderDescriptor

ByProvides indexes the manifest's descriptors by their Provides name. Behavior is undefined (last-wins) if two descriptors share a Provides name — checkDuplicateProvides reports that as a violation.

func (Manifest) Print

func (m Manifest) Print() string

Print renders the manifest as a stable, human-readable table (used by `wowapi lint lifecycle` and tests).

func (Manifest) Sorted

func (m Manifest) Sorted() []ProviderDescriptor

Sorted returns a copy of the descriptors sorted by Provides, for deterministic printing/diffing.

type ProviderDescriptor

type ProviderDescriptor struct {
	// Provides is the unique name of the capability this descriptor wires,
	// e.g. "kernel.Pool" or "module.Context.Tx". Conventionally
	// "<owner>.<FieldOrMethod>" matching the real Go identifier so the
	// manifest stays traceable to kernel.go/context.go/module.go.
	Provides string
	// Requires lists the Provides names this descriptor's construction
	// depends on. Every entry must match another descriptor's Provides (lint
	// class (e): missing provider) and must not form a cycle.
	Requires []string
	// Scope is this descriptor's lifetime.
	Scope Scope
	// RawPool marks a descriptor that hands out an unwrapped *pgxpool.Pool
	// (or equivalent raw connection) rather than a TxManager/TenantDB. Lint
	// class (b) flags any module-facing descriptor with this set.
	RawPool bool
	// TenantScoped marks a descriptor whose value must not outlive the
	// tenant transaction that produced it (a database.TenantDB or something
	// derived from one). Lint class (c) flags such a descriptor with a
	// Requires edge FROM a wider-scoped (process/request) descriptor that
	// would let the value escape its transaction.
	TenantScoped bool
	// APIRuntime marks a descriptor that is wired into the api/worker
	// runtime module.Context surface (i.e. reachable from a module's
	// Register). Lint class (d) flags a migrate-scoped descriptor with
	// APIRuntime set — a migrate-only service must never be wired into a
	// process that serves API/worker traffic.
	APIRuntime bool
}

ProviderDescriptor describes one provided value/capability in the wiring graph: what it provides, what it requires (by Provides name of another descriptor), and the scope it is valid at.

type Scope

type Scope string

Scope is the lifetime a provided value is valid for. Mirrors the benchmark doc's recommended Go-native shape (framework-competitive-architecture- benchmark.md, "DI / IoC: Static Lifecycle Graph For Go").

const (
	// ScopeProcess: constructed once in kernel.New, lives for the process
	// lifetime (pools, registries, evaluators, kernel services).
	ScopeProcess Scope = "process"
	// ScopeRequest: constructed per inbound HTTP request (module.Context
	// accessors handed to Module.Register are process-scoped services, but
	// request-handling code that consumes them operates per-request).
	ScopeRequest Scope = "request"
	// ScopeTenantTx: valid only for the lifetime of one tenant-bound
	// transaction opened via database.TxManager.WithTenant/WithTenantRO
	// (database.TenantDB). Must never escape the callback that receives it.
	ScopeTenantTx Scope = "tenant_tx"
	// ScopeJob: constructed for one worker job execution.
	ScopeJob Scope = "job"
	// ScopeMigrate: valid only in the migrate process (privileged DDL
	// connection, app.SkipRLSEnforcementCheck). Must never be wired into a
	// process that serves API/worker runtime traffic.
	ScopeMigrate Scope = "migrate"
)

type Violation

type Violation struct {
	Class    string
	Provides string
	Message  string
}

Violation is one lint finding. Class identifies which failure category (a-e, matching the backlog B9 acceptance list) so callers/tests can filter or count by class; Provides names the offending descriptor; Message is the human-readable detail.

func Lint

func Lint(m Manifest) []Violation

Lint runs every check against m and returns ALL violations found, sorted for deterministic output — mirroring the errs-accumulate pattern used by kernel/config.Framework.Validate and kernel/rules.Registry.Err (collect everything, never stop at the first problem).

func (Violation) String

func (v Violation) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL