Documentation
¶
Overview ¶
Package commitpolicy checks a forge project's git state against forge's commit policy:
- Generated code a COMPILER needs is COMMITTED. gen/, every forge-generated source file and every buf-generated stub must be tracked, so a checkout builds (`go build`, `go install`, a frontend build) as-cloned and `forge ci verify-generated` can see drift in it.
- Generated files only forge or tooling reads are NEVER committed (IsToolDerived): forge derives each one on demand, in any checkout, so a committed copy buys nothing and costs a merge conflict between every two branches whose inputs differ. The proto descriptor, gen/forge_descriptor/, is one: committed as one aggregate file, it conflicted between any two pull requests that touched any proto.
- Machine-local materializations are NEVER committed: the legacy .forge-kcl/ module copy, and a web frontend's dev runtime document public/config.js, whose values resolve per machine.
Why this is a check and not just a template ¶
The scaffold .gitignore encodes the policy, but a project keeps the .gitignore it was born with forever — forge never rewrites a file the user owns. A project scaffolded while the template ignored gen/*, *_gen.go and the mocks carries those rules indefinitely, and nothing notices: its CI fails on `undefined:` symbols that live in ignored files, while `forge ci verify-generated`, which diffs with `git status --porcelain`, is blind to ignored paths by construction and reports green. That is the exact state a scaffolded project reached (its CI went red on every compiling job) before this check existed. It runs in `forge lint` and after verify-generated's regenerate, so every project that lints or runs CI is held to the policy whatever its .gitignore says.
It judges THIS project's files only. A nested tree that belongs to another project — its own git repository, or a Go module outside this one's module path — is skipped whole; see isForeignProject.
Index ¶
Constants ¶
const ( // RuleGeneratedIgnored: a generated file the .gitignore excludes. RuleGeneratedIgnored = "generated-code-ignored" // RuleLocalStateTracked: a machine-local materialization in git. RuleLocalStateTracked = "machine-local-state-tracked" // RuleToolDerivedTracked: a generated file only tooling reads, in git. RuleToolDerivedTracked = "tool-derived-file-tracked" )
Rule ids, stable for `forge lint --json` consumers.
const DeliveredRenderMarker = "project-specific guidance belongs in your own skills directory"
DeliveredRenderMarker is the phrase every harness-delivered agent skill carries in its banner (rendered by older forge versions, which are swept on generate).
A delivered skill is NOT project-generated code: it is a render of the skill catalog embedded in the forge binary, stamped with the rendering forge's version, with no input from the project. Like .forge-kcl/ it is binary-derived state — `forge skill load` prints the authoritative copy straight from the binary — so every forge build would rewrite a committed copy, and whether to track the directory is the project's call (forge's own repo ignores .claude/; a harness: reliant project has none). The commit policy therefore neither requires it to be committed nor forbids it.
const GoBuildCacheSentinel = "This directory holds cached build artifacts from the Go build system."
GoBuildCacheSentinel is the first line of the README the Go toolchain writes into every GOCACHE root. It is how a build cache identifies ITSELF, which is the only robust way to recognize one: GOCACHE is configurable, so the directory can be called anything. Agents in this workspace point it at $WORKTREE/.gocache to keep concurrent worktrees off a shared cache, but that name is a convention, not a contract.
Matching on the sentinel rather than a name also keeps the carve-out honest: a directory only earns the skip by actually being a Go cache.
Variables ¶
This section is empty.
Functions ¶
func Format ¶
Format renders violations as the multi-line block `forge lint` and `forge ci verify-generated` print.
func IsGoBuildCacheDir ¶
IsGoBuildCacheDir reports whether dir is a GOCACHE root, by reading the sentinel README the toolchain puts there.
Exported because two independent project-root walkers need it and must not disagree: this commit policy, and internal/removalguard's surviving- reference scan, which hit the identical false positive — a cache entry's copy of a stub for a since-removed feature read as a live reference to it.
This matters because a cache entry is a VERBATIM copy of compiler input, generated Go source included — an entry's first line is routinely "// Code generated by 'go test'. DO NOT EDIT." So isGenerated matches it, and since a build cache is gitignored by design, every entry otherwise becomes a "generated code, but excluded from git" violation. Measured: 133 in one control-plane worktree, 276 reported in the field, which buries the findings this rule exists to surface.
func IsToolDerived ¶ added in v1.0.10
IsToolDerived reports whether rel (project-relative, slash-separated) is a generated output only forge or tooling reads, which this policy keeps out of git.
Types ¶
type Violation ¶
type Violation struct {
Rule string
// Path is project-relative, slash-separated. For an ignored generated
// file it is the file; for tracked local state it is the tracked path
// (a directory for .forge-kcl/).
Path string
// Why says what the path is and why the policy applies to it.
Why string
// Fix is the literal command (or edit) that resolves it.
Fix string
}
Violation is one breach of the policy.