<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title>GUIDE — Go under the Discipline, v0.1</title>
  <p p="1">**Status.** Beta; fifth language guide. Sections isomorphic to the Rust/TS/Python/C++ guides — diff them across languages; that is the genre's contract.</p>
  <p p="2">Framing note — the fifth point of the typology. Rust *enforces*; TypeScript *permits but compiles*; Python *trusts*; C++ *demands a subset to survive*; **Go prescribes**. The language ships with its discipline pre-installed: gofmt ended formatting debates, the compiler rejects unused imports, errors are values by culture, inheritance does not exist, `internal/` is compiler-enforced encapsulation. The guide's job therefore inverts — not subset selection but **gap closure**: Go's opinions stop exactly one step short of contract grade in four places, and this guide closes them: (1) errors are values but their *sets* are open; (2) interface conformance is silent; (3) goroutines are unowned by design; (4) `init()` blesses the side-effectful import. Patterns were dissolved by the language designers before we arrived: Strategy = interface + composition root; Decorator = wrapper; Observer = channel/callback seam; Visitor degrades to enum + switch (no sum types — the deepest gap, §4); Singleton = forbidden, and `http.DefaultClient` is its stdlib disguise.</p>
  <p p="3">**Scope honesty.** Services, CLIs, and long-lived system code. Throwaway scripts are out of scope. cgo is boundary-module territory — cells are pure Go (checkable). Code generated by `go generate` follows the generated-code rule: the generator's input is the taggable unit, outputs are excluded.</p>
  <section id="baseline" title="0. Language baseline">
    <list ordered="false" p="4">
      <item>**Version floor: Go 1.24**, target latest stable. Modules with committed `go.sum`; `GOFLAGS=-mod=readonly` in CI (the lockfile is native — A2 by default). `go.work` is the workspace analog for multi-module repos.</item>
      <item>**Formatting is language-owned:** gofmt is non-negotiable and costs the Discipline zero budget — the one language where the style war was won upstream.</item>
      <item>**Gates:** `go vet` MUST; staticcheck (MIT) MUST; `govulncheck` (BSD-3) in CI (supply-chain floor). golangci-lint as the aggregation harness — **license flag: GPL-3.0**; legal under the Charter's policy only as a separate-process dev tool, never vendored or linked; if even tool-level GPL is unwanted, invoke staticcheck + vet + individual linters directly.</item>
      <item>**Suppression policy (xfail-strict by construction):** bare `//nolint` is banned; only `//nolint:&lt;linter&gt; // &lt;reason&gt;` is legal, and golangci-lint's `nolintlint` (require-specific + require-explanation + flag-unused) makes stale or vague suppressions a build failure — the suppression registry shrinks truthfully, same mechanism as `@ts-expect-error` and pyright's unnecessary-ignore.</item>
      <item>**Race detector gates tests:** `go test -race` is the MUST configuration for any package that starts a goroutine; findings are failures.</item>
      <item>**Generics:** legal and bounded — type parameters for containers/algorithms in infra packages; domain seams stay interface-based unless a measured hot path says otherwise. No type-parameter theater (R-021).</item>
      <item>**Boundary validation (parse, don't validate):** JSON decoding is loose by default — missing fields become zero values silently, unknown fields are ignored. Boundary decode uses `DisallowUnknownFields` plus explicit validation; boundary DTO structs convert explicitly into domain types; absent-vs-zero ambiguity is resolved with pointer fields or a validation layer at the boundary, never guessed in cells.</item>
    </list>
  </section>
  <section id="cells" title="1. Cells">
    <p p="5">A cell is a **package** under `internal/cells/&lt;name&gt;` — `internal/` makes non-registry imports a compile error from outside the module, and the in-module sibling ban is checked at T-syn from the import graph.</p>
    <list ordered="false" p="6">
      <item>**Import-is-execution, Go edition: `init()` and blank imports.** Go's stdlib itself blesses registration-at-import (`database/sql` drivers, image codecs, `_ "net/http/pprof"`), which is exactly why the rule must be explicit: **`init()` and blank imports are banned in cells.** The single carve-out is boundary adapters that wrap stdlib-style driver registration — registration happens there or in the composition root, never as a side effect of importing domain code. Package-level `var` with non-constant initializers is banned in cells for the same reason.</item>
      <item>**No ambient state:** cells never touch `http.DefaultClient`, the global `log`/`slog` default, `os.Getenv`, `flag.CommandLine`, `math/rand` globals, or `time.Now` directly. Capabilities are injected at construction — and Go makes this uniquely cheap: a cell declares the narrow interface it needs *privately* (`type clock interface{ Now() time.Time }`) and structural typing does the rest. No central capability package required; no mocking framework either — tests hand in literal implementations.</item>
      <item>**`context.Context`** is the cancellation capability: first parameter of every potentially-blocking seam method, never stored in a struct field.</item>
      <item>**Exports are the surface:** a cell package exports its constructor (`New(...)`) and nothing else beyond seam-required types. Exported-but-unreferenced identifiers are findings.</item>
      <item>**Promotion** to a separate module on the usual triggers (heavy optional deps, independent release cadence, ~2 kLoC).</item>
    </list>
    <p p="7">Cell manifest (directive carrier, §5) plus the conformance assertion:</p>
    <fence lang="go" p="8">//spec:implements spec://org.vibevm.core/vibevm/modules/vibe-resolver/PROP-003#solver-upgrade r=2
//spec:cell seam=DepSolver variant=sat replaces=naive flag=solver
type SatDepSolver struct{ /* ... */ }

var _ resolver.DepSolver = (*SatDepSolver)(nil) // silent conformance made loud — MUST

func New(p resolver.DepProvider, log *slog.Logger) *SatDepSolver { /* ... */ }</fence>
  </section>
  <section id="seams" title="2. Seams">
    <list ordered="false" p="9">
      <item>**Product seams are central; capability interfaces are consumer-side.** The seam (the flag-selectable, replaceable contract) lives in a neutral package; the Go idiom "define interfaces where they're consumed" is *kept* for injected capabilities inside cells. This split resolves the cultural collision instead of overruling it.</item>
      <item>"Accept interfaces, return structs": constructors return the concrete `*SatDepSolver`; only the registry hands out the seam interface.</item>
      <item>Every cell carries the compile-time conformance assertion (`var _ Seam = (*Impl)(nil)`); conform checks its presence (T-syn) — structural typing stops being silent.</item>
      <item>Seam methods that can fail return `(T, error)` where the error belongs to the seam's **closed error set** (§4); values crossing seams are plain structs with useful zero values or explicit constructors — no half-initialized exports.</item>
    </list>
  </section>
  <section id="flags" title="3. Registry and flags">
    <p p="10">R-001 binding — flag at the seam, never in the veins:</p>
    <fence lang="go" p="11">// internal/registry — the only flag reader and the only package
// permitted to import cell packages.
func DepSolver(cfg Config, p resolver.DepProvider, log *slog.Logger) resolver.DepSolver {
	switch cfg.Solver { // provenance: default | env | cli | lockfile
	case SolverSat:
		return satdepsolver.New(p, log)
	default:
		return naivedepsolver.New(p, log)
	}
}</fence>
    <list ordered="false" p="12">
      <item>**Two tiers, never confused:** build tags (`//go:build`) answer *"is the code in the binary"* — the cargo-feature analog, with per-file granularity — and are confined to registry/adapter files, never inside cell bodies (T-lex); runtime flags answer *"is the cell selected"*, read once into a config struct in `main`.</item>
      <item>**Delivery-mode honesty:** Go has no credible lazy in-process loading (the `plugin` package is platform- and version-locked); eager is the only mode, presence is the build tier's job.</item>
      <item>No self-registration (now impossible in cells — `init()` is banned), no DI frameworks, no reflection-based wiring. The registry `switch` is the system's table of contents.</item>
    </list>
  </section>
  <section id="errors" title="4. Errors as contract">
    <p p="13">Go already made errors values; the Discipline makes their *sets* part of the contract:</p>
    <list ordered="false" p="14">
      <item>**Each seam owns a closed, enumerated error set:**</item>
    </list>
    <fence lang="go" p="15">type SolveErrorCode int

const (
	ErrCycle SolveErrorCode = iota + 1
	ErrUnsatisfiable
)

type SolveError struct {
	Code SolveErrorCode
	Spec string // violated REQ URI: "spec://...#req-acyclic"
	Err  error  // wrapped cause
}

func (e *SolveError) Error() string { /* renders message + Spec */ }
func (e *SolveError) Unwrap() error { return e.Err }</fence>
    <p p="16">Consumers use `errors.As` against the published type and switch on `Code`; rendering at the boundary appends the REQ URI (PROP-014 §2.6).</p>
    <list ordered="false" p="17">
      <item>**Banned at seams:** matching on error strings; `fmt.Errorf` without `%w` (breaks the chain); returning anonymous `errors.New` for expected failures; `error` returns that are sometimes nil-with-meaning.</item>
      <item>**Exhaustiveness — the deepest gap:** Go has no sum types and no exhaustive `switch`. Closed sets are const-enums, and the `exhaustive` linter (evidence provider) supplies what the compiler won't — the fifth binding of the same Discipline rule, and the only one carried entirely by a linter.</item>
      <item>**panic = invariant violation** — the analog is native, same word. `recover` is legal only at goroutine/boundary top level (e.g. middleware), never as control flow in cells; panicking on expected failures is banned.</item>
      <item>**Structured concurrency by ownership:** every goroutine a cell starts has an owner — `errgroup.Group` (BSD-3) or `WaitGroup` + context cancellation; naked `go` with cell-outliving lifetime is banned; channels are owned and closed by their spawner. The unowned goroutine is Go's unreferenced `create_task`, with no GC to even cancel it.</item>
      <item>**Release map for free:** every Go binary carries `runtime/debug.ReadBuildInfo` — VCS revision, dirty flag, module versions — readable from the artifact itself (`go version -m`). The A1 chain *binary → build info → specmap@commit → REQ* needs zero extra machinery; the only rule is not to strip what the runtime gave you (panic stacks stay symbolized, or symbolized copies are retained).</item>
    </list>
  </section>
  <section id="specmark" title="5. specmark carrier">
    <p p="18">**Directive comments**, not doc-comment tags — a deliberate divergence from the TS/Python choice, forced by the toolchain: since Go 1.19 gofmt *reformats doc comments* and could re-wrap prose tags, but preserves `//name:value` directive lines verbatim, and godoc hides them. Go already owns the cultural slot (`//go:generate`, `//go:embed`); the Discipline takes `//spec:`:</p>
    <fence p="19">//spec:implements &lt;uri&gt; r=&lt;N&gt;                 one edge per line; lines repeat
//spec:deviates &lt;uri&gt; r=&lt;N&gt; reason="..."      reason mandatory
//spec:verifies &lt;uri&gt; r=&lt;N&gt;                   above Test/Fuzz functions
//spec:scope &lt;uri&gt; r=&lt;N&gt;                      in the package doc block (doc.go) — package-level inheritance</fence>
    <p p="20">Parsed via `go/ast` comment maps; gofmt-proof by construction. The trade-off is named honestly: provenance disappears from rendered godoc and lives in `vibe explain`/the ledger instead. ≤3 edges per item or split.</p>
  </section>
  <section id="naming" title="6. Naming (R-020/R-021 bindings)">
    <list ordered="false" p="21">
      <item>Canonical cell type name is computed: `{Variant}{Seam}` → `SatDepSolver`; the package is the lower-case variant (`satdepsolver`). Linted against the manifest.</item>
      <item>**Forbidden in cells regardless of elegance** — the Go theater list: `init()` and blank imports (§1); reflection-based wiring and struct-tag DSLs in domain code (tags are for boundary DTOs); `interface{}`/`any` in domain signatures where a type or small interface fits; method sets split across files to obscure a type; clever channel topologies as API (channels are implementation, seams are methods); `recover` as control flow; package-level mutable state; `unsafe` outside designated boundary files.</item>
    </list>
  </section>
  <section id="replacement" title="7. Replacement protocol (R-040 binding)">
    <p p="22">A cell with `replaces=` ships a differential oracle on **native fuzzing** (`go test -fuzz` corpus + deterministic seeds in CI): one fuzz target drives both cells through the seam and asserts agreement modulo a documented divergence list, `//spec:verifies`-tagged, run with `-race`. Golden files live in `testdata/` and follow the promotion protocol — the conventional `-update` flag never runs in CI, and a local update carries a debt/intent reference. **xfail honesty:** Go has no native strict-xfail; `t.Skip` on a known-failing test is banned (skip hides both regressions and healings) — known failures live only in `tests-baseline.json`, which carries full weight here, the one language in the set without an in-source twin.</p>
  </section>
  <section id="risks" title="8. Risk table (what conform must cover for Go)">
    <table p="23">
      <tr>
        <td>Footgun</td>
        <td>Rule</td>
        <td>Tier</td>
      </tr>
      <tr>
        <td>`init()` / blank import / non-const package `var` in a cell</td>
        <td>§1</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>ambient default used in a cell (`http.DefaultClient`, global log, `os.Getenv`, `time.Now`)</td>
        <td>§1</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>naked `go` with cell-outliving lifetime; channel without owner</td>
        <td>§4</td>
        <td>T-syn + review</td>
      </tr>
      <tr>
        <td>`context.Context` stored in a struct / not first param</td>
        <td>§1</td>
        <td>T-syn (vet)</td>
      </tr>
      <tr>
        <td>error-string matching; `fmt.Errorf` without `%w` at a seam</td>
        <td>§4</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>expected failure outside the seam's closed error set</td>
        <td>§4</td>
        <td>T-sem</td>
      </tr>
      <tr>
        <td>non-exhaustive switch on a closed const-enum</td>
        <td>§4</td>
        <td>T-sem (exhaustive)</td>
      </tr>
      <tr>
        <td>typed-nil stuffed into an interface</td>
        <td>§2</td>
        <td>T-sem (staticcheck)</td>
      </tr>
      <tr>
        <td>missing `var _ Seam = (*Impl)(nil)` conformance assertion</td>
        <td>§2</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>`t.Skip` on a known-failing test</td>
        <td>§7</td>
        <td>T-lex + test-gate</td>
      </tr>
      <tr>
        <td>sibling-cell import; cell imported outside the registry</td>
        <td>R-002</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>`//go:build` product tag inside a cell body</td>
        <td>§3</td>
        <td>T-lex</td>
      </tr>
      <tr>
        <td>bare `//nolint` / stale suppression</td>
        <td>§0</td>
        <td>nolintlint</td>
      </tr>
      <tr>
        <td>`unsafe` / cgo outside boundary files</td>
        <td>§0, §6</td>
        <td>T-lex</td>
      </tr>
      <tr>
        <td>flag read outside the registry</td>
        <td>R-001</td>
        <td>T-syn</td>
      </tr>
      <tr>
        <td>public export without own/inherited spec edge</td>
        <td>PROP-014 §3.2-6</td>
        <td>T-syn + index</td>
      </tr>
    </table>
  </section>
  <section id="docs" title="9. Doc layer">
    <p p="24">Godoc comments on every exported identifier (the language's own lint culture already expects this): behavior, the seam's error codes with their REQ URIs, goroutine and channel ownership, context semantics. The `//spec:` directives sit in the same comment block but stay out of the rendered page; human-facing provenance is the ledger's job. Spec stays thin; duplication between godoc and spec is a defect on the spec side.</p>
    <p p="25">**First carrier note.** No Go exists in vibevm; no carrier is designated. The guide ships genre-complete and unexercised, under the carrier-relative house clause: rules remain DRAFT until a first Go carrier exists, and at that carrier's first milestone any rule without a conform check (or explicit `WISH` mark) is removed rather than carried as aspiration.</p>
  </section>
</spec>
