Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Running CI Gates Locally

Audience: contributors who want to mirror the CI surface on a local machine before pushing, or want to narrow down which gate failed in a remote run.

The full make lint && make test cycle is the default pre-push check. This page is the targeted-subset reference for when you know which contract you changed and want to verify only that contract’s gates.

All tests

go test ./...

Runs everything. Slow but exhaustive. The recommended pre-push default.

Descriptor contract gates

The descriptor surface (predict, manifest, inspect, envelope) has structural invariants that catch hand-edits and import cycles:

go test ./descriptor/ ./internal/descriptor/ -run 'TestPredictNoExecution|TestDescriptorNoFmtSprintf|TestGoldensNotHandEdited'
  • TestPredictNoExecutionImports — internal/descriptor/predict.go must not import internal/service/ or internal/processing/.
  • TestDescriptorNoFmtSprintf — no fmt.Sprintf in descriptor/envelope.go, manifest.go, predict.go, inspect.go.
  • TestGoldensNotHandEdited — every golden file under descriptor/testdata/ must end with a valid // golden-hash: <sha256> line that matches the body.

Skill-coverage gates

The skill pack under skills/ is the LLM-facing surface. Every registered component, error code, distribution, CLI leaf, field type, MCP tool must be mentioned in its target skill by name:

go test ./internal/skills/ -run 'TestSkillsCoverAll|TestSkillsManifestConsistent|TestSkillsFrontmatter'

The specific gates this batch includes are listed in Testing Conventions → Non-skippable CI gates.

Predecessor-reference scrub

Pulse has a hard rule against leaking strings from its predecessor project (legacy “Orbit” naming) into error codes or type constants:

go test . -run TestNoOrbit

Should always return zero matches before opening a PR.

Public API gates

Every package outside internal/ and cmd/ is frozen Go API at v1.0.0. Three guards keep the surface deliberate:

go test ./internal/apigolden/            # TestPublicAPIGolden (blocking)
make smoke                               # builds internal/embeddersmoke as an external module
  • TestPublicAPIGolden dumps the exported shape of every public package, with each root alias expanded into its target’s fields, tags and methods, and compares it to internal/apigolden/testdata/public_api.txt. If you meant to change the surface, regenerate with go test ./internal/apigolden/ -run TestPublicAPIGolden -update and review the diff as the API delta.
  • make smoke compiles and tests internal/embeddersmoke, a nested module with its own go.mod that may only use public spellings. CI runs it.
  • The apidiff job (.github/workflows/api-compat.yml) runs on PRs only. See Pull Request Process.

Contrib modules

contrib/otelpulse and contrib/prompulse are separately published Go modules (own go.mod + go.sum, replace => ../.. for development). The core module never depends on them, nor on any OpenTelemetry or Prometheus module (TestCoreModuleNoObservabilityDeps).

make contrib        # per module: go mod tidy -diff, vet, staticcheck, test
make contrib-tidy   # re-tidy every contrib module

make contrib runs without -mod=mod, so a stale contrib go.mod or go.sum fails locally and in CI. A root dependency bump therefore needs make contrib-tidy in the same PR. Contrib modules are released in lockstep with the root tag by scripts/release-contrib.sh (called by release.yml); run it without --push for a dry-run tag plan.

CLAUDE.md hygiene gates

CLAUDE.md is itself a tested artefact. Every PULSE_* env var named in Go source must appear there; every non-skippable gate must be listed; the current format_version must be mentioned:

go test . -run 'TestClaudeMd|TestUpdateDemandTable'

Per-change quick reference

If you changed…Run
An aggregator / attribute / filterer / grouper / window / featurego test ./internal/skills/ -run TestSkillsCoverAllComponents && go test ./internal/descriptor/ -run TestManifestOperatorsComplete
A statistical testgo test ./types/ -run TestStreamability_TestsKnown && go test ./internal/descriptor/ -run 'TestManifestTestsComplete|TestManifestPostTestsComplete'
A synth distributiongo test ./internal/skills/ -run TestSkillsCoverAllSynthDistributions && go test ./internal/descriptor/ -run TestManifestDistributionsComplete
A regression operatorgo test ./internal/skills/ -run TestSkillsCoverAllRegressions && go test ./internal/descriptor/ -run TestManifestRegressionsComplete
An error codego test ./errors/ -run 'TestCodesHaveFixups|TestErrorsLookup' && go test ./internal/descriptor/ -run 'TestManifestErrorCodesComplete|TestManifest_ErrorCodesSlim'
An MCP toolgo test ./internal/skills/ -run TestSkillsCoverAllMCPTools && go test ./internal/descriptor/ -run TestManifestMCPToolsComplete && go test ./internal/mcp/ -run TestMCPSchemaBinding
A field typego test ./internal/skills/ -run TestSkillsCoverAllFieldTypes && go test ./encoding/... ./internal/encoding/...
The Update Demand table or a contract listed in itgo test . -run TestUpdateDemandTableCovers
Any exported identifier in a public package, or a root alias’s targetgo test ./internal/apigolden/ (then -update only if the change is intended) + make smoke
A package move under internal/io/** / internal/iocorego test ./internal/iocore/ -run TestIOImportBoundary

The full set is documented in Testing Conventions → Non-skippable CI gates and enumerated by name in CLAUDE.md.