Module Checklist — Ship + Audit#
Run each item and report pass / fail / not applicable. The module ships when every applicable item passes.
Layout#
-
modules/<name>/matches the template:go.mod,module.go,domain/,api/,store/(when persistent),infrastructure/(when calling external providers — never indomain/),admin/,testutil/,README.md. -
go.modrequires kernel (+ siblings) at the checkout’s release tag and carries noreplacedirectives. -
embed.gocarries the migration FS with//go:embedon the spec inside thevar (...)block. - File layout contract holds (const → var → private helpers →
public declarations; methods with their struct; constructor last).
make stylecheckgreen. - Registered in
go.work(use) and rootMakefileSUBMODULES. Private modules stay out of the release EDGE map anddocs/built-in-modules.md.
Contracts#
-
Name()stable registry key;Requires()is the intended disable cascade;Optional()degrades (never fails) when absent. - Handlers write through
kernel/httpxonly (envelope parity). - Principal read from context; decimal ID parsed fail-closed on
kernel.ErrInvalidPrincipalID; no second identity shape. - Correct route surface:
RouteProvider(protected),PublicRouteProvider(anonymous), orDualAuthProvider(both, full/api/v1/...patterns, no collisions). No multi-group surface forced through a single call. - Public-router bodies capped with
http.MaxBytesReaderbefore decode. - Capabilities declared (
RoleProvider) and enforced (resolver seam + in-handler checks, fail closed on unknown). -
Manifest()validates viaAdminExtensionManifest.Validate()(no per-module copy); conformance adopted (CheckAdminExtension/ValidateAdminExtension;CheckAuthorizerfor resolvers); manifest deterministic across calls. - Migrations per driver (
{sqlite,postgresql,mysql}/), idempotent (CREATE TABLE IF NOT EXISTS), tracked inschema_migrations_<module>; no cross-module FKs (indexed reference columns instead);nosqlitebuild considered. - Webhook signatures compare with
crypto/subtle.ConstantTimeCompare, never==. - Sentinels translate at the seam (
errors.Is); mirrored messages pinned byte-equal by test. -
Initvalidates presence only (no tables — migrations run afterInitAll);Shutdownreleases whatInitacquired. - Logging via injected
kernel.Logger(nil degrades to discard); nopanic, nofmt.Println/log.*outsideinternal/app.
Gating#
- Domain coverage at 100% statements (empty bodies do not count; unreachable branches are documented per-branch exceptions).
- Table-driven suites (
namefirst,wantErrwhere errors return,ttloop variable,requirefor setup /assertfor the rest, factory constructors over struct literals). -
make depguardclean (nolesstruct/internalimports, tests included). Mocks viamake mock(mockery) or narrow hand-written stubs. -
make lint(modernize + stylecheck + golangci-lint) clean. -
make vulncheckclean.
Wiring (custom edition)#
- Private module wired via
internal/editions/<custom>/+ thincmd/<custom>/main.go— neverinternal/appby default. -
Wireconstructs services/stores,News the module with options, appends tocore.Candidates, fills optional handler surfaces;Mountassembles post-InitAllstate. Adapters one-seam-per-file;wire<Module>steps beside siblings, never in adapter files. - Disabled cascade verified (
[modules] disabled = ["<name>"]prunes + dependents with warnings; routes unmount where flag-gated). - Second-theme goals routed to
EditionWithTheme, never a secondThemeProviderregistration.
Verification#
-
go test ./...in-workspace green. -
GOWORK=off go test ./...(orgo build ./...) in the module green. -
go build ./...whole workspace green;go build -tags nosqlite ./...considered; custom binary links and serves the surface. -
README.mdnames the seams;docs/project-context.mdgains the entry when the change is architecture-level (same-change doc rule).
Upgrade (when bumping Lesstruct/kernel)#
- Versions re-derived from the checkout;
go.modrequires moved together (lockstep); noreplacereintroduced. - Capability matrix re-checked against the new
kernel/tree. - Conformance fixtures re-run; envelope compat still pinned.
- Both workspaces re-verified (in-workspace +
GOWORK=off).