# Source tag publication Use `releasekit.Publish` to validate a committed candidate and push its exact annotated source tag: ```go result, err := releasekit.Publish(ctx, releasekit.ReleaseRequest{ RepositoryDir: "/work/project", ModuleDir: "integrations/example", Product: "Example", Version: "v1.2.3", ExpectedPushURL: "ssh://git.example/owner/example.git", Validation: releasekit.ValidationSpec{ Go: releasekit.GoValidationSpec{Race: true, FullVet: true}, }, AdditionalChecks: func(ctx context.Context, candidate releasekit.Candidate) error { return runProjectChecks(ctx, candidate.ModuleDir) }, }) if err != nil { return err } fmt.Println(result.ModulePath, result.Version, result.Tag, result.CandidateCommit, result.TagObject) ``` `ModuleDir` is a repository-relative physical module directory; omit it or use `.` for a root-module release. ReleaseKit reads the selected module's committed declaration and derives the complete Git tag from its declared module path and physical location. `Version` remains the stable semantic version. The note is `/docs/releases/.md`; an on-disk `/v2` directory is part of the note path but is removed from the tag prefix only when the module path ends in `/v2`. `PublicationResult` returns the absolute `ModuleDir`, declared `ModulePath`, complete `Tag`, and existing commit and tag-object evidence. The optional `ValidationSpec.SharedFiles` selects additional committed files for enabled file checks. Its nil and empty-slice behavior is described in the [candidate check guide](check.md#local-candidate-check). Publication captures a clean candidate on `main`, validates its committed release note, and checks the selected remote before running candidate checks. Set `ExpectedPushURL` to require the effective push destination to match an exact string. Git URL rewrites are resolved first, and the push URL is compared when fetch and push URLs differ. A mismatch fails preflight before Go validation, callbacks, or tag creation; detect it with `errors.Is(err, releasekit.ErrUnexpectedPushURL)`. `Check` ignores this publication-only field. It runs one `go test -mod=readonly -count=1 ./...` suite with the available compatible local Go toolchain. Set `Validation.Go.Race` to run that suite with `-race`. Set `Validation.Go.FullVet` to run `go vet -mod=readonly ./...` after successful tests, with test-time vet disabled for that suite. The zero values keep ordinary test-time vet behavior. It then calls `AdditionalChecks` once when supplied. The standard suite cannot be replaced; move product-specific behavior into `AdditionalChecks` and remove repeated standard-suite commands from custom checks. A failed suite or additional check prevents tag creation, and wrapped errors preserve their causes. If race prerequisites are unavailable, the selected suite fails as `go-test` instead of silently running without the race detector. Full-vet failures are reported as `go-vet` and stop publication before tag creation. Set `Validation.Go.CompileTargets` to compile packages for each complete target pair after tests and vet. This uses `CGO_ENABLED=0`, produces no artifact, and does not execute target binaries. A failure is reported as `package-compile` with the selected target and prevents tag creation. Set `Validation.GoFormat` to check committed regular Go source using the Go formatter built into ReleaseKit. It uses the selected file inputs, including vendor and explicit shared files, and ignores worktree changes and symlink blobs, and reports unformatted paths as `go-format` without changing files. The default is disabled. The check follows tests, selected vet, and package compilation, and a failure stops publication before product checks or tag creation. Set `Validation.ModuleDistribution.WorkspaceFiles` to reject committed `go.work` and `go.work.sum` blobs in the selected inputs, including symlink entries. The rule reads the captured candidate regardless of `GOWORK`; uncommitted files are ignored, and Go workspace selection and vendor behavior are unchanged. Its zero value disables the check. Findings use `workspace-files` and stop publication before product checks or tag creation. Set `Validation.ModuleDistribution.LocalReplacements` independently to parse selected module's committed `go.mod` declarations. Vendored contents remain in scope; automatic inputs stop at independently released nested modules. Explicit shared files can include another module's declaration. Filesystem `replace` directives are reported with their source path, line, and target as `local-replacements`; versioned replacements are permitted. Committed module symlinks are resolved within the candidate, and parse or inspection errors retain their causes. This check is independent of `GOWORK`; its zero value disables it, and a failure stops publication before product checks or tag creation. Set `Validation.FileSize` to check each committed blob against a byte limit. Nil disables the check; an empty object enables the default of 1,000,000 decimal bytes. `LimitBytes` uses strict greater-than, accepts zero, and rejects negative values. `Exclusions` contains literal files or directories relative to the selected module, including committed descendants of excluded directories. Symlink blob size is its link text; gitlinks are omitted. This check measures only the captured candidate and reports one oversized file with its measurement as `file-size`; it stops publication before product checks or tag creation. Set `Validation.Markdown` to check committed local Markdown destinations and supported heading fragments during publication. Nil disables the check; an empty object enables it with outside-checkout links reported as out of scope. Set `EnforceCheckoutBoundary` to fail those links. Submodule contents remain out of scope under either setting. Observations use the request's output writer; failures use `markdown-links` and stop publication before product checks or tag creation. ReleaseKit-owned selections replace duplicate generic suite, vet, formatting, distribution, and Markdown checks in configured product checks. Keep release-note rules and consumer-specific assertions, such as maintained-example tests, in `AdditionalChecks` or profile `checks`. If `Build` selects targets, ReleaseKit verifies them after the standard suite and additional checks, before creating the tag. The operation then rechecks the candidate and remote guards, creates an annotated tag for the captured commit, and pushes only that tag object. A successful result means Git confirmed the push or an exact authoritative tag query confirmed it after a push error. A failed inspection after local tag creation can leave the tag ref even when its object ID is unavailable; when the ID was established it remains in `TagObject`. The local tag is retained after a failed push. `PublicationState` distinguishes a known failure, conflict, confirmed publication, and unresolved outcome. Resolve an unknown result against the captured endpoint before retrying; see the [maintainer procedure](../release.md) for the recovery steps. The result's endpoint is the exact URL and can contain credentials; handle it accordingly. The [CLI contract](../cli.md) documents command output and exit statuses. ## Retain and reconcile publication evidence `Publish` automatically attempts to save one private record for each invocation. The default store is under the repository's Git common directory, so linked worktrees share it. `ReleaseRequest.PublicationRecordDir` can override the directory; relative overrides resolve from the source repository root. The CLI equivalent is `publish --record-dir DIR`. ReleaseKit creates its owned storage directories privately and creates record files with owner-only permissions; existing directory permissions are left as they are. The caller owns storage retention and removal. The saved endpoint is retained exactly for later observation and may include credentials. Treat records, `PublicationEvidence`, and `PublicationResult` as sensitive. Store overrides in a location with suitable access controls, avoid printing raw endpoint values, and remove records when their recovery value has expired. Diagnostic errors and CLI output apply credential redaction; the private record and typed evidence preserve the raw endpoint. Recording failure is reported in `PublicationResult.RecordError`, separately from `Publish`'s return error and publication state. A successful push remains successful if storage fails, but the saved handoff is unavailable. Callers can still pass `result.Evidence` directly to `ReconcilePublication` when it contains the required identity. For example: ```go func observe(ctx context.Context, result releasekit.PublicationResult) (releasekit.PublicationReconciliationResult, error) { return releasekit.ReconcilePublication(ctx, result.Evidence) } ``` The evidence is populated as publication identity becomes available, including partial evidence on some earlier failures. An abrupt process termination before the save point can leave no record; removed storage also removes the handoff. Records from older attempts may lack the tag object, endpoint, or other identity needed for a conclusive observation. Use `LoadPublicationEvidence(path)` to read one explicit record, `ListPublicationAttempts(ctx, request)` to discover readable records and per-record diagnostics in a local directory, or `ReconcilePublication(ctx, evidence)` to observe a selected endpoint. Listing does not query remotes, and neither listing nor reconciliation changes saved records. Recovery does not infer the attempt from current HEAD, local tags, module files, or profiles. `ReconcilePublication` returns a nil error for a completed `published`, `not_published`, or `conflict` observation; unresolved `unknown` results include a typed error that retains the underlying cause. See the [GoDoc](../../recovery.go) for the exported fields and error kinds, the [CLI guide](../cli.md) for command statuses, and the [record format guide](../integrations/publication-records.md) for supported stored and structured formats.