Documentation
¶
Overview ¶
Package quality runs read-only coverage and verification checks for a local repository fleet. It deliberately reports every selected repository instead of stopping at the first failure, so people and AI agents can act on one complete index.
This file backs TestNoInlineWriteSequencesOutsideFilewrite (spec/plans/ coverage-to-100 task-9): the mechanical check that a temp-file write -> sync -> chmod -> close -> publish (rename or link) sequence is implemented in exactly one place, internal/filewrite, and nowhere else.
The check is call-based, not a co-occurrence heuristic: a function is a violation if its body directly calls one of a fixed set of publish or create primitives:
- publish, always banned: os.Rename, os.Link, syscall.Rename, syscall.Renameat, syscall.Link, the fd-relative unix.Rename/ Renameat/Renameat2/RenameatxNp/Link/Linkat family, the repo-local renameNoReplace helper, or a package-level alias of os.Link/os.Rename.
- create, always banned regardless of whether the same function goes on to write content: os.CreateTemp, ioutil.TempFile, os.Create. A round-2 review of task-9 PR-1 found these three reserve or open a file unconditionally -- unlike os.OpenFile/unix.Openat, they cannot be used for a lock-only open, so gating them behind a content-write check let a real write escape through a package-level seam function (internal/nodeidentity/nodeidentity.go:writeNodeIDTempFile).
- create, banned only when the same function also writes content: os.OpenFile or unix.Openat carrying an O_CREAT-family flag. This gate stays narrow because both are legitimately used for a lock-file open (O_CREAT|O_EXCL followed only by Fchmod/Flock, never a content write) that internal/filewrite has nothing to replace.
- os.WriteFile alone is not banned; it is only a violation together with one of the publish primitives above in the same function, since a bare os.WriteFile is an ordinary overwrite, not a temp-file publish.
This replaced an earlier create-and-rename-pair heuristic that missed every os.*-based site (the dominant shape in this repository), every renameNoReplace call and every package-level rename/link alias, and that could never match os.OpenFile at all because it looked for the identifier O_CREAT while the os package spells its flag O_CREATE.
A violation's key is "relative/path.go:FuncName", or "relative/path.go:ReceiverType.FuncName" for a method -- the receiver type disambiguates two methods that share a name on different receivers in the same file (internal/streams/events.go has both (*FileEventLog).Append, a real O_APPEND write, and (DiscardEvents).Append, a no-op stub that is never flagged).
Two named allow-lists carry the sites this detector finds but task-9's PR series has not folded into internal/filewrite yet:
- PendingMigrationExemptions: real write-then-publish (or write-once, or create-only-scratch) sequences still implemented inline, each naming the PR that will migrate it. The last PR in the series empties this map, turning the guard below into a zero-exception gate. Per a round-2 review decision, this includes every ephemeral/scratch CreateTemp site (a name reserved and freed, or a file written and used once by a single subprocess, never durably read back): the plan the file's own package doc for internal/filewrite states the Verifies goal as "zero direct temp-file write/sync/chmod/close/ rename sequences outside the new package", with no carve-out for scratch files, so these are pending a future filewrite.CreateScratch helper rather than permanently exempt.
- NotAFileWritePublishExemptions: sites this detector's necessarily conservative rules flag, but that are not a temp-file write-and- publish sequence at all -- a queue-state move, an archive/quarantine move, a directory move, an append-only log write, or the renameNoReplace primitive's own OS-specific implementation. These never migrate to internal/filewrite, because there is nothing here for it to replace. This list holds only move-only renames, append-only log writes, and the renameNoReplace OS wrappers -- never a create-temp-file site, since every one of those is either a real write (pending migration) or a scratch file (also pending, per the policy above).
TestEveryFilewriteBoundaryExemptionMatchesALiveViolation asserts every entry in both maps still names a real, currently-detected site, so a stale or padded entry is caught immediately and the allow-lists can only shrink as sites genuinely migrate. TestNotAFileWritePublishExemptionsNeverAlsoCreateAndWriteContent asserts no NotAFileWritePublishExemptions entry -- exempted for a rename/move/ append reason -- also independently creates a file via a gated OpenFile/Openat-with-create-flag call, an os.WriteFile/ioutil.WriteFile call, or an unconditionally-banned create primitive, and writes content to it; a round-2 review found internal/locallink/execports.go's Link function misclassified this way (exempted as "renames ... into place", when it also does two real O_CREATE|O_EXCL content writes before that rename), and a round-3 review found the same true of internal/lifecyclehooks/queue.go's Dispatcher.quarantineFile (renames the quarantined file, then os.WriteFile's a ".reason.txt" sidecar of its own). TestNotAFileWritePublishExemptionsAreRenameOnlyOrAppendOnlyNeverBoth asserts every remaining entry is exactly one of the list's two legitimate shapes -- a pure rename/move (calls a publish primitive, writes no content of its own) or a pure append-only log (opens O_APPEND and writes, calls no publish primitive) -- never neither and never both; a function doing both at once (rename plus an independent append-write) would otherwise slip past the create-and-write check above, since that check only looks for a *gated* create primitive and O_APPEND opens are deliberately excluded from it.
Package quality's parallel-baseline logic backs two things that must never drift apart: TestParallelBaselineDoesNotRegress (the CI guard) and cmd/parallelbaseline (the tool a human runs to regenerate the committed baseline after a deliberate, reviewed decision to add a new serial test). Both call the exported functions in this file so there is exactly one implementation of "what counts as serial" and "what counts as a baseline key".
Index ¶
- Constants
- Variables
- func EvaluateRatchet(blocks []CoverageBlock, changed ChangedLines, touchedFiles map[string]bool, ...) ([]PackageRatchet, []RatchetWarning)
- func FormatDeadcodeBaseline(findings []DeadcodeFinding) string
- func FormatParallelBaseline(entries []ParallelBaselineEntry) string
- func GitLineOffsets(ctx context.Context, repoRoot, mergeBase string) (map[string]FileLineOffsets, error)
- func GitMergeBase(ctx context.Context, repoRoot, target string) (string, error)
- func GitTopLevel(ctx context.Context, dir string) (string, error)
- func GitTouchedFiles(ctx context.Context, repoRoot, mergeBase string) (map[string]bool, error)
- func LoadDeadcodeBaseline(path string) (entries map[string]bool, missing bool, err error)
- func PackageOf(file, modulePath string) string
- func PackageUncoveredCounts(blocks []CoverageBlock, modulePath string) map[string]int
- func ParallelGuardModuleRoot() (string, error)
- func ParseParallelBaseline(path string) (map[string]string, error)
- func ReadModulePath(moduleRoot string) (string, error)
- func SaveValidationCache(cacheRoot string, key ValidationCacheKey, report VerificationReport) error
- func SingleWorkerNodeEnv() []string
- func SortVerificationReports(reports []VerificationReport)
- func ValidateBaseline(baseline PackageBaseline, expectedSHA string) error
- func ValidationCacheDir(root string) string
- func WriteBaseline(path string, baseline PackageBaseline) error
- func WriteDeadcodeBaseline(path string, findings []DeadcodeFinding) error
- type ChangedLines
- type ChangedPackagesResult
- type Check
- type CoverageBlock
- type CoverageDiagnostic
- type CoverageDiagnosticFile
- type CoverageDiagnosticManifest
- type CoverageReport
- type DeadcodeFinding
- type DeadcodeOptions
- type DeadcodeReport
- type FileLineOffsets
- type InlineWriteSequenceViolation
- type ModuleCoverage
- type PackageBaseline
- type PackageRatchet
- type ParallelBaselineEntry
- type Progress
- type ProgressState
- type RatchetFinding
- type RatchetWarning
- type RepositoryCoverage
- type RunOptions
- type Status
- type UncoveredBlock
- type ValidationCacheKey
- type VerificationEntry
- type VerificationReport
- func LoadValidationCache(cacheRoot string, key ValidationCacheKey) (VerificationReport, bool, error)
- func Verify(ctx context.Context, repository, path string, checks []Check) VerificationReport
- func VerifyWithOptions(ctx context.Context, repository, path string, checks []Check, ...) VerificationReport
Constants ¶
const ( // ReasonChangedStatementUncovered is a statement the diff itself adds or // modifies, found via GitChangedLines. ReasonChangedStatementUncovered = "added or changed statement is not covered by a test" // ReasonNewlyUncoveredAtBase is a statement the diff does not touch at // all, found only because the package's uncovered count rose (review // B2): it was covered when baseline was measured and is not now. ReasonNewlyUncoveredAtBase = "newly uncovered (was covered at base)" )
Ratchet finding/warning reasons (review-696 non-blocking #4): a genuinely added or changed line and a pre-existing statement whose *coverage* changed are different situations and must read differently, since the first names something the diff shows and the second does not.
const DefaultDeadcodeBaseline = ".wb/deadcode-baseline.txt"
DefaultDeadcodeBaseline is the repository-relative baseline path. It sits beside .wb/quality.yaml because it is repository-owned policy, reviewed in the pull request that changes it, not machine state.
Variables ¶
var DefaultDeadcodeTool = []string{"go", "run", "golang.org/x/tools/cmd/deadcode@v0.50.0"}
DefaultDeadcodeTool pins the analyzer the way .wb/quality.yaml pins golangci-lint: an unpinned analyzer silently changes the gate's verdict between runs, which is the one thing a ratchet must never do.
var NotAFileWritePublishExemptions = map[string]string{
"internal/lifecyclehooks/queue.go:Dispatcher.recoverRunning": "renames a queue job's state directory back to pending on recovery; not a file write",
"internal/lifecyclehooks/queue.go:Dispatcher.claimBatch": "renames a queue job's state directory to claim it; not a file write",
"internal/streams/store.go:Store.archiveLocked": "renames a stream's directory into an archive location; not a file write",
"internal/locallink/execports.go:ExecNode.Unlink": "renames an existing backup directory back into place; not a temp-file write",
"cmd/wb/daemon_file_bridge.go:daemonFileBridgeServer.quarantine": "renames a request file into a quarantine directory; not a write publish",
"internal/worktrees/worktrees.go:moveExpectedDirectoryNoReplaceAuthorized": "moves a worktree directory after an identity check; not a file write",
"internal/worktrees/worktrees.go:moveExpectedLockNoReplace": "moves a lock file after an identity check, without writing new content; not a file write",
"internal/hooks/manager.go:moveExpectedManagedHookNoReplace": "moves a managed hook after an identity check, without writing new content; not a file write",
"internal/worktrees/rename_noreplace_darwin.go:renameNoReplace": "OS-specific renameNoReplace syscall wrapper, not a write sequence",
"internal/worktrees/rename_noreplace_linux.go:renameNoReplace": "OS-specific renameNoReplace syscall wrapper, not a write sequence",
"internal/hooks/rename_noreplace_darwin.go:renameNoReplace": "OS-specific renameNoReplace syscall wrapper, not a write sequence",
"internal/hooks/rename_noreplace_linux.go:renameNoReplace": "OS-specific renameNoReplace syscall wrapper, not a write sequence",
"internal/agentguard/gh.go:recordGhPrMergeOverride": "O_APPEND log write, not a create/publish sequence",
"internal/hooks/metrics.go:AppendEvents": "O_APPEND log write, not a create/publish sequence",
"internal/runlog/runlog.go:Append": "O_APPEND log write (flock-guarded), not a create/publish sequence",
"internal/streams/events.go:FileEventLog.Append": "O_APPEND log write (flock-guarded), not a create/publish sequence",
"internal/lifecyclehooks/queue.go:appendReceipt": "O_APPEND log write (flock-guarded), not a create/publish sequence",
}
NotAFileWritePublishExemptions lists "relative/path.go:FuncName" (or "relative/path.go:ReceiverType.FuncName" for a method) sites this detector's conservative call-based rules flag, but that are not a temp-file write-and-publish sequence: a queue-state move, an archive or quarantine move, a directory or lock move, an append-only log write, or the renameNoReplace primitive's own OS-specific implementation (which internal/filewrite does not yet own -- see LinkNoReplace's doc comment). None of these migrates to internal/filewrite, because there is no write sequence here for it to replace. Per a round-2 review decision, this list holds only move-only renames, append-only logs, and the renameNoReplace OS wrappers -- a create-temp-file site never belongs here, even a scratch one; see PendingMigrationExemptions' Category D/E. TestNotAFileWritePublishExemptionsNeverAlsoCreateAndWriteContent enforces that no entry below also independently creates and writes a file.
var ParallelGuardExcludedDirs = []string{"cmd/wb", "hub", "internal/runqueue"}
ParallelGuardExcludedDirs are the subtrees sneat-dev/wb#623's test-speed sweep did not touch, because other agent lanes were actively editing their test files at the same time (cmd/wb is #623 step 3; hub and internal/runqueue belong to other in-flight lanes). Once those get their own pass this list should shrink, and eventually disappear.
var PendingMigrationExemptions = map[string]string{
"internal/worktrees/worklog.go:writeBytesImmutableAt": "PR-3: worktrees -- rename-based immutable publish (via renameNoReplace)",
"internal/worktrees/worklog.go:writeBytesAtomicAt": "PR-3: worktrees -- fd-relative rename-based atomic write",
"internal/worktrees/worklog.go:writeBytesAtomic": "PR-3: worktrees -- path-based twin of writeBytesAtomicAt",
"internal/sessionlaunch/state.go:publishLaunchArtifact": "PR-7: session-and-lifecycle -- link-based immutable publish",
"internal/hooks/manager.go:writeExecutableAt": "PR-5: hooks -- fd-relative create+chmod+write+renameNoReplace publish",
"cmd/wb/daemon.go:daemonController.writeLifecycleOwnerPID": "PR-2: cmd/wb -- path-based CreateTemp+chmod+sync+Rename",
"cmd/wb/daemon_file_bridge.go:writeDaemonFileEnvelope": "PR-2: cmd/wb -- OpenFile+write+sync+Rename",
"cmd/wb/daemon_process_darwin.go:startDaemonProcess": "PR-2: cmd/wb -- CreateTemp plist+chmod+Rename",
"cmd/wb/fleet_default_branch.go:persistDefaultBranchReport": "PR-2: cmd/wb -- CreateTemp+chmod+sync+Rename",
"cmd/wb/hooks_agent.go:writeSettingsAtomically": "PR-2: cmd/wb -- CreateTemp+chmod+Rename",
"cmd/wb/peers.go:savePeerUpstreamState": "PR-2: cmd/wb -- CreateTemp+chmod+sync+Rename",
"cmd/wb/sync_report.go:writeSyncIssuesFile": "PR-2: cmd/wb -- CreateTemp+chmod+Rename",
"internal/agents/run.go:Store.Save": "PR-7: session-and-lifecycle -- CreateTemp+chmod+sync+Rename",
"internal/archiveprune/untracked.go:overwriteArchiveCleanReceipt": "PR-8: misc-atomic-writers -- CreateTemp+chmod+sync+Rename",
"internal/checkoutmarker/checkoutmarker.go:writeFileAtomically": "PR-8: misc-atomic-writers -- CreateTemp+chmod+Rename",
"internal/daemon/lifecycle.go:Store.Save": "PR-7: session-and-lifecycle -- CreateTemp+chmod+sync+Rename",
"internal/daemon/service.go:Service.persistRecord": "PR-7: session-and-lifecycle -- OpenFile+sync+Rename",
"internal/deps/github_actions.go:writeAtomic": "PR-8: misc-atomic-writers -- CreateTemp+chmod+Rename",
"internal/discover/local_index.go:writeLocalIndex": "PR-8: misc-atomic-writers -- CreateTemp+chmod+Rename",
"internal/fleetsync/receipt.go:overwriteRemovalReceipt": "PR-8: misc-atomic-writers -- CreateTemp+chmod+Rename",
"internal/githubobserver/observer.go:writeCacheEntry": "PR-8: misc-atomic-writers -- CreateTemp+chmod+Rename",
"internal/hooks/pushtier_prlookup.go:savePRStatusCache": "PR-5: hooks -- WriteFile-to-temp+Rename",
"internal/landinglane/landinglane.go:writeRecord": "PR-8: misc-atomic-writers -- WriteFile-to-temp+Rename",
"internal/layout/migrate.go:writeManifest": "PR-8: misc-atomic-writers -- WriteFile-to-temp+Rename",
"internal/lifecyclehooks/gc.go:rewriteReceiptRecords": "PR-8: misc-atomic-writers -- CreateTemp+chmod+sync+Rename",
"internal/lifecyclehooks/queue.go:writeJSONAtomic": "PR-8: misc-atomic-writers -- CreateTemp+chmod+sync+Rename",
"internal/mergeack/mergeack.go:Persist": "PR-7: session-and-lifecycle -- CreateTemp+chmod+sync+Rename",
"internal/migrate/engine.go:Apply": "PR-8: misc-atomic-writers -- CreateTemp+chmod+Rename",
"internal/npmrelease/release.go:writeAtomic": "PR-8: misc-atomic-writers -- CreateTemp+chmod+Rename",
"internal/orchestrate/worktree_merge.go:persistWorktreeMergeReceipt": "PR-4: orchestrate -- CreateTemp+chmod+sync+Rename",
"internal/orchestrate/worktree_merge_ack.go:persistPreparedWorktreeMergeRebatch": "PR-4: orchestrate -- CreateTemp-based publish",
"internal/orchestrate/worktree_merge_ack.go:persistLandedFailureAcknowledgement": "PR-4: orchestrate -- CreateTemp-based publish",
"internal/orchestrate/worktree_merge_ack.go:persistValidationFailureSupersession": "PR-4: orchestrate -- CreateTemp-based publish",
"internal/orchestrate/worktree_merge_retired_publication.go:persistRetiredPublicationAcknowledgement": "PR-4: orchestrate -- CreateTemp-based publish",
"internal/orchestrate/worktree_merge_stranded.go:persistStrandedLandingAcknowledgement": "PR-4: orchestrate -- CreateTemp-based publish",
"internal/orchestrate/worktree_merge_unpublished_validation_failure.go:persistUnpublishedValidationFailureAcknowledgement": "PR-4: orchestrate -- CreateTemp-based publish",
"internal/quality/go_test_shards.go:writeCoverageProfileAtomically": "PR-6: quality -- CreateTemp+chmod+sync+Rename",
"internal/quality/validation_cache.go:SaveValidationCache": "PR-6: quality -- CreateTemp+sync+Rename",
"internal/repositoryevents/queue.go:Queue.persist": "PR-8: misc-atomic-writers -- CreateTemp-based publish",
"internal/repositoryevents/receiver.go:CursorStore.saveState": "PR-8: misc-atomic-writers -- CreateTemp-based publish",
"internal/runqueue/visibility.go:atomicWriteFile": "PR-8: misc-atomic-writers -- CreateTemp-based publish",
"internal/streams/store.go:Store.writeAtomically": "PR-8: misc-atomic-writers -- CreateTemp-based publish",
"internal/waitregistry/registry.go:Register": "PR-8: misc-atomic-writers -- WriteFile-to-temp+Rename",
"internal/wbconfig/peers.go:SetPeersUpstream": "PR-8: misc-atomic-writers -- CreateTemp-based publish",
"internal/wbconfig/remote.go:SetRemoteHub": "PR-8: misc-atomic-writers -- CreateTemp-based publish",
"internal/worktrees/branches_quarantine.go:writeQuarantineReport": "PR-3: worktrees -- OpenFile+sync+Rename",
"internal/worktrees/lifecycle.go:writeCleanupReport": "PR-3: worktrees -- WriteFile-to-temp+Rename",
"internal/worktrees/rename.go:writeRenameReport": "PR-3: worktrees -- WriteFile-to-temp+Rename",
"internal/worktrees/retire.go:writeRetireReport": "PR-3: worktrees -- CreateTemp-based publish",
"internal/worktrees/stage_recovery.go:writeRetiredStageReceipt": "PR-3: worktrees -- WriteFile-to-temp+Rename",
"internal/orchestrate/worktree_merge_ack.go:persistReceiptCollisionAcknowledgement": "PR-4: orchestrate -- publishes via package-var alias linkReceiptCollisionAcknowledgement = os.Link",
"internal/orchestrate/worktree_merge_ack.go:persistConflictCandidateAdvance": "PR-4: orchestrate -- publishes via package-var alias linkConflictCandidateAdvance = os.Link",
"internal/orchestrate/worktree_merge_ack.go:persistLegacyValidationFailureIdentity": "PR-4: orchestrate -- publishes via package-var alias linkLegacyValidationFailureIdentity = os.Link",
"internal/orchestrate/worktree_merge_ack.go:persistLegacyConflictIdentity": "PR-4: orchestrate -- publishes via package-var alias linkLegacyConflictIdentity = os.Link",
"internal/orchestrate/worktree_merge_ack.go:persistMissingCleanupAcknowledgement": "PR-4: orchestrate -- publishes via package-var alias linkMissingCleanupAcknowledgement = os.Link",
"internal/orchestrate/worktree_merge_ack.go:persistSelfSupersessionCorrection": "PR-4: orchestrate -- publishes via package-var alias linkSelfSupersessionCorrection = os.Link",
"internal/orchestrate/worktree_merge_adopt_published.go:persistPublishedCandidateAdoption": "PR-4: orchestrate -- publishes via package-var alias linkPublishedCandidateAdoption = os.Link",
"internal/worktrees/branches_cleanup.go:writeBranchCleanupReport": "PR-3: worktrees -- writeDurableFile(temporary) then os.Rename",
"internal/nodeidentity/nodeidentity.go:publishNodeID": "PR-7: session-and-lifecycle -- publishes via os.Link; its temp-file half writeNodeIDTempFile migrates in the same PR",
"internal/nodeidentity/nodeidentity.go:writeNodeIDTempFile": "PR-7: session-and-lifecycle -- CreateTemp+chmod+write+sync via package-var seams fileChmod/fileWriteString/fileSync/fileClose, escapes the OpenFile content-write gate; migrates with publishNodeID",
"cmd/wb/daemon_file_bridge.go:daemonFileBridgeKey": "PR-2: cmd/wb -- OpenFile O_CREATE|O_EXCL write-once",
"cmd/wb/peers.go:writeOneTimeToken": "PR-2: cmd/wb -- OpenFile O_EXCL write-once (one-time token)",
"cmd/wb/remote_enroll.go:writePrivateCredential": "PR-2: cmd/wb -- chmod+OpenFile O_EXCL write-once",
"cmd/wb/verify_receipt.go:writeGraduationReceipt": "PR-2: cmd/wb -- OpenFile O_EXCL write-once",
"internal/retiredcandidateack/ack.go:Persist": "PR-7: session-and-lifecycle -- OpenFile O_EXCL write-once",
"internal/session/session.go:MarkParked": "PR-7: session-and-lifecycle -- OpenFile O_EXCL write-once (parked lifecycle marker)",
"internal/session/session.go:MarkResumed": "PR-7: session-and-lifecycle -- OpenFile O_EXCL write-once",
"internal/worktrees/branches_cleanup.go:writeDurableFile": "PR-3: worktrees -- OpenFile O_EXCL write-once (also used non-temp)",
"internal/worktrees/branches_cleanup.go:copyFileSHA256": "PR-3: worktrees -- OpenFile O_EXCL write-once (copy via io.Copy)",
"internal/locallink/execports.go:ExecNode.Link": "PR-8: misc-atomic-writers -- two OpenFile O_CREATE|O_EXCL write-once marker/backup writes ahead of a rename; not rename-only, unlike ExecNode.Unlink",
"internal/locallink/execports.go:copyBuiltPackageContents": "PR-8: misc-atomic-writers -- OpenFile O_EXCL write-once (copy via io.Copy)",
"internal/worktrees/retire.go:retireCaptureFile": "PR-3: worktrees -- OpenFile O_EXCL write-once (copy via io.Copy)",
"internal/orchestrate/worktree_merge.go:extractWorktreeMergeArchive": "PR-4: orchestrate -- OpenFile O_CREATE|O_TRUNC write via io.Copy for each archive entry",
"cmd/wb/coverage_ratchet.go:runChangedCoverage": "PR-9: scratch-helper (filewrite.CreateScratch) -- reserves a unique coverage-profile path, closes and reuses it, never writes",
"cmd/wb/fleet_default_branch.go:defaultBranchReportPath": "PR-9: scratch-helper (filewrite.CreateScratch) -- reserves a unique report path then frees it via os.Remove, never writes",
"internal/locallink/execports.go:ExecGit.ContentHash": "PR-9: scratch-helper (filewrite.CreateScratch) -- reserves a name for git plumbing output, closes and removes it, never writes",
"internal/pathguard/pathguard.go:OSProbe": "PR-9: scratch-helper (filewrite.CreateScratch) -- writability probe: create, close, remove, never writes",
"internal/quality/coverage.go:coverageProfilePath": "PR-9: scratch-helper (filewrite.CreateScratch) -- reserves a unique coverage-profile path, closes it, never writes",
"internal/quality/verify.go:runShardedVerification": "PR-9: scratch-helper (filewrite.CreateScratch) -- reserves a unique verify-coverage-profile path, closes it, never writes",
"cmd/wb/fleet_merge_policy.go:applyClassicProtectionWithoutLinearHistory": "PR-9: scratch-helper (filewrite.CreateScratch) -- ephemeral temp file passed as gh api --input, removed by defer",
"cmd/wb/fleet_merge_policy.go:applySharedRuleset": "PR-9: scratch-helper (filewrite.CreateScratch) -- ephemeral temp file passed as gh api --input, removed by defer",
"internal/hooks/run.go:runTemplate": "PR-9: scratch-helper (filewrite.CreateScratch) -- ephemeral temp script for one subprocess execution, removed by defer",
"internal/orchestrate/worktree_merge.go:PrepareWorktreeMergeRevert": "PR-9: scratch-helper (filewrite.CreateScratch) -- writes an ephemeral git-diff patch temp file, removed by defer",
"internal/orchestrate/worktree_merge.go:runWorktreeMergePrePushGate": "PR-9: scratch-helper (filewrite.CreateScratch) -- writes an ephemeral pre-push gate input temp file, removed by defer",
"internal/orchestrate/worktree_merge.go:writeWorktreeMergePrompt": "PR-9: scratch-helper (filewrite.CreateScratch) -- writes an ephemeral prompt temp file for one worktree-create call; caller removes it by defer",
"internal/orchestrate/worktree_merge_conflict_replacement.go:writeConflictCandidateRefreshPrompt": "PR-9: scratch-helper (filewrite.CreateScratch) -- writes an ephemeral prompt temp file for one worktree-create call; caller removes it by defer",
"internal/orchestrate/worktree_merge_published_forward_repair.go:writePublishedForwardRepairPrompt": "PR-9: scratch-helper (filewrite.CreateScratch) -- writes an ephemeral prompt temp file for one worktree-create call; caller removes it by defer",
"internal/orchestrate/worktree_merge_seal.go:writeValidationFailureSealPrompt": "PR-9: scratch-helper (filewrite.CreateScratch) -- writes an ephemeral prompt temp file for one worktree-create call; caller removes it by defer",
"cmd/wb/deps_policy.go:fetchPolicy": "PR-9: scratch-helper (filewrite.CreateScratch) -- writes one HTTP download to a temp file returned to the caller for one-shot use",
"internal/lifecyclehooks/queue.go:Dispatcher.quarantineFile": "PR-8: misc-atomic-writers -- renames the quarantined file, then os.WriteFile's a \".reason.txt\" sidecar of its own -- not rename-only",
"internal/locallink/execports.go:ExecGit.ExcludePath": "PR-8: misc-atomic-writers -- O_APPEND write to a git exclude file; not a durable log, so not permanent-list append-only",
}
PendingMigrationExemptions lists "relative/path.go:FuncName" (or "relative/path.go:ReceiverType.FuncName" for a method) sites that implement a real temp-file write-then-publish sequence, write-once sequence, or scratch/name-reservation create inline, not yet routed through internal/filewrite. Every entry names the task-9 PR that will migrate it; that PR removes the entry in the same commit it lands.
Functions ¶
func EvaluateRatchet ¶ added in v0.164.0
func EvaluateRatchet(blocks []CoverageBlock, changed ChangedLines, touchedFiles map[string]bool, lineOffsets map[string]FileLineOffsets, baseline PackageBaseline, modulePath string) ([]PackageRatchet, []RatchetWarning)
EvaluateRatchet applies the per-change coverage ratchet (spec/plans/coverage-to-100/README.md task-3, founder decisions 11 and 12): a package the PR changes fails when its uncovered-statement count rises against baseline, or when a changed, non-moved line is uncovered anywhere. A package the PR does not change only ever warns on a count rise — touchedFiles (repository-relative paths, including files the diff only deletes lines from, deletes entirely, or a rename's old AND new path) decides package ownership; a changed package with no baseline entry at all is treated as a baseline of 0 (review-696 blocking #2: the merge-base measurement covers every package that existed then, so a missing entry means the package did not exist, not that it is exempt).
Attributing a count-only rise to an exact statement (uncoveredBlockKey against baseline.UncoveredBlocks) can land on a block inside a file the PR itself touched: editing a file shifts every later line's position, so the baseline's raw stored position there does not directly compare. Set lineOffsets (from GitLineOffsets) maps each baseline block's stored line through that file's own diff hunks to its current line first: a current block landing on that mapped line is the same pre-existing statement, just shifted, not new (review-696 non-blocking #3) — pass nil to skip this mapping entirely, which means no touched-file block can be matched to a shifted baseline position, so every uncovered block in a touched file is reported as attributable (the original, pre-5a7d0df6 behavior, before the blanket touched-file skip existed — this is the LEAST conservative option, not a conservative one).
func FormatDeadcodeBaseline ¶ added in v0.137.0
func FormatDeadcodeBaseline(findings []DeadcodeFinding) string
FormatDeadcodeBaseline renders findings as a baseline file. Entries are sorted and one per line so that a diff of this file reviews as a list of mechanisms that lost or gained a caller.
func FormatParallelBaseline ¶ added in v0.164.8
func FormatParallelBaseline(entries []ParallelBaselineEntry) string
FormatParallelBaseline renders entries as the committed file format: sorted by key, tab-separated key and reason, one per line.
func GitLineOffsets ¶ added in v0.164.0
func GitLineOffsets(ctx context.Context, repoRoot, mergeBase string) (map[string]FileLineOffsets, error)
GitLineOffsets computes FileLineOffsets for every file a diff against mergeBase touches (review-696 non-blocking #3). --no-renames matches GitTouchedFiles: a rename is a plain delete-then-add pair, so its old path's hunks (an entire "delete everything") never falsely offset the new path's line numbers.
func GitMergeBase ¶ added in v0.164.7
GitMergeBase resolves the merge-base commit of HEAD and target inside repoRoot. It is the base resolution `wb coverage --changed --target` and `wb run --changed --target` both use, so the two never disagree about which commit a diff is measured against.
func GitTopLevel ¶ added in v0.164.7
GitTopLevel resolves the repository's top-level working-tree directory containing dir. Every path GitTouchedFiles/GitChangedLines/GitLineOffsets report is relative to this directory, never to whatever directory the caller happened to invoke Git from — callers that need those paths relative to a different directory (a nested Go module root, or the caller's own working directory) must rebase them explicitly; see ChangedPackages for that rebasing.
func GitTouchedFiles ¶ added in v0.164.0
GitTouchedFiles reports every repository-relative file path a diff touches against mergeBase — added, modified, deleted, or renamed — independent of whether the diff added any line at all. A pure deletion (for example removing a test function) never appears in ChangedLines (it added no line), but it must still count as "the PR changed this package" (founder decision 2026-09-23, review B1): the caller uses this, not ChangedLines, to decide per-package ratchet ownership.
func LoadDeadcodeBaseline ¶ added in v0.137.0
LoadDeadcodeBaseline reads a baseline file. A missing file is not an error: it reports every finding as new, which is what a repository adopting the gate should see before it records its starting point.
func PackageOf ¶ added in v0.164.0
PackageOf maps a coverage profile's module-qualified file path (for example "github.com/sneat-dev/wb/internal/quality/ratchet.go") to the package directory relative to the module root ("internal/quality"). modulePath is the module declaration from go.mod (for example "github.com/sneat-dev/wb"); the module root package itself maps to ".".
func PackageUncoveredCounts ¶ added in v0.164.0
func PackageUncoveredCounts(blocks []CoverageBlock, modulePath string) map[string]int
PackageUncoveredCounts sums the uncovered statement count per package.
func ParallelGuardModuleRoot ¶ added in v0.164.8
ParallelGuardModuleRoot walks up from the calling file to the nearest go.mod.
func ParseParallelBaseline ¶ added in v0.164.8
ParseParallelBaseline reads the baseline file, keyed as "<key>\t<reason>" per line. Every entry must carry a non-empty reason; ParseParallelBaseline returns an error naming every entry that does not (callers surface all of them at once rather than failing on the first).
func ReadModulePath ¶ added in v0.164.0
func SaveValidationCache ¶ added in v0.98.3
func SaveValidationCache(cacheRoot string, key ValidationCacheKey, report VerificationReport) error
SaveValidationCache writes terminal evidence atomically. Failed writes are returned to the caller; a cache failure never changes validation semantics.
func SingleWorkerNodeEnv ¶ added in v0.88.0
func SingleWorkerNodeEnv() []string
SingleWorkerNodeEnv is the environment a single-worker Node run must carry. It is exported so a caller that composes its own command still states the same environment the profile does.
func SortVerificationReports ¶
func SortVerificationReports(reports []VerificationReport)
SortVerificationReports orders reports for deterministic output.
func ValidateBaseline ¶ added in v0.164.0
func ValidateBaseline(baseline PackageBaseline, expectedSHA string) error
ValidateBaseline reports whether baseline is usable against expectedSHA. A wrong schema version, an empty package map, or (when expectedSHA is non-empty) a SHA that does not match expectedSHA each make the baseline unusable: EvaluateRatchet treats a package missing from Packages as having no baseline and passes the count rule for it by design, so a baseline that silently lost its contents (an empty `{}` artifact, a schema drift, or one published for the wrong commit) would otherwise disable the per-package count rule for every package without failing anything (spec/plans/coverage-to-100/README.md task-3(b)). Callers must treat a non-nil error as "this baseline cannot be trusted", not "no baseline available" — the caller falls back to measuring the merge base directly, or fails loudly, either way never using the untrusted baseline as-is.
func ValidationCacheDir ¶ added in v0.98.3
ValidationCacheDir is kept in one place so all merge baseline callers share the same private WB state and tests can replace it without touching a user repository.
func WriteBaseline ¶ added in v0.164.0
func WriteBaseline(path string, baseline PackageBaseline) error
WriteBaseline writes baseline as deterministic, indented JSON.
func WriteDeadcodeBaseline ¶ added in v0.137.0
func WriteDeadcodeBaseline(path string, findings []DeadcodeFinding) error
WriteDeadcodeBaseline records findings as the new tolerated set.
Types ¶
type ChangedLines ¶ added in v0.164.0
ChangedLines is the set of new-file line numbers a diff added or modified against a merge base, excluding lines git identifies as moved-but-unmodified (spec/plans/coverage-to-100/README.md task-3(a)). Keys are file paths relative to the repository root, matching `git diff`'s `b/<path>` spelling.
func GitChangedLines ¶ added in v0.164.0
func GitChangedLines(ctx context.Context, repoRoot, mergeBase string) (ChangedLines, error)
GitChangedLines computes ChangedLines for repoRoot against mergeBase, running `git diff --merge-base <mergeBase> -U0 --color-moved=plain` with explicit color assignments so moved lines are distinguishable from plain additions regardless of the caller's git config.
type ChangedPackagesResult ¶ added in v0.164.7
type ChangedPackagesResult struct {
// Target is the branch or ref the caller asked to diff against.
Target string
// MergeBase is the resolved merge-base commit of HEAD and Target.
MergeBase string
// Packages is the sorted, deduplicated list of `go test`/`go vet`-ready
// package patterns the diff touches ("." for workingDir's own module
// root, "./internal/foo" or "../sibling" otherwise, always relative to
// workingDir). A directory outside the Go module that contains
// workingDir, a directory belonging to a DIFFERENT Go module nested
// inside that module (its own go.mod below workingDir's module root —
// a tools/, examples/, or <product>/backend-style subtree), a directory
// the diff empties out entirely (fully deleted or moved away), and a
// directory the go tool itself would never build (no buildable *.go
// file left in it, a "testdata"/"vendor" directory, or a "_"/"."-prefixed
// path segment) are never included. A directory whose only *.go files
// are excluded by build constraints on the current host (an
// architecture- or OS-specific file that does not match GOOS/GOARCH
// here) is not detected as such and may still be included — that would
// need actually loading the build graph, which this function
// deliberately does not do.
Packages []string
}
ChangedPackagesResult is the outcome of computing which Go packages a local diff touched against a resolved merge base.
func ChangedPackages ¶ added in v0.164.7
func ChangedPackages(ctx context.Context, workingDir, target string) (ChangedPackagesResult, error)
ChangedPackages computes the Go packages a local diff against target touches, scoped to the Go module that contains workingDir (the nearest go.mod at or above workingDir, up to the repository root) and expressed as patterns relative to workingDir itself — exactly what a human typing `go test ./...` from that directory would need, whether workingDir is a repository's top level or a nested module root such as "<product>/backend" (the fleet's common layout).
It captures staged changes, unstaged changes, and already-committed changes since the merge base, all in one pass (spec/plans/coverage-to-100/README.md task-19, issue #570). This promotes the pre-commit hook template's shell mapping (internal/hooks/config.go, BuiltinGoPreCommit) into a first-class, tested Go function `wb run --changed` calls.
The diff itself is computed the same way GitTouchedFiles' underlying `git diff --merge-base <mergeBase>` call already does for `wb coverage --changed`: with no --cached, `git diff <ref>` compares ref to the CURRENT WORKTREE, which is the index (staged changes) and the on-disk files (unstaged changes) together — already committed changes since the merge base are naturally included too, since they are already part of that worktree state relative to the older merge-base ref. One git invocation therefore captures all three kinds of local change; no separate --cached pass is needed. Git always reports these paths relative to the repository's top level, never relative to workingDir, which is why this function resolves that top level itself (GitTopLevel) instead of treating workingDir as if it already were that root.
Untracked files (never added to Git at all) are not included: `git diff` itself never reports them, staged, unstaged, or committed.
Renames count both their old and new path (GitTouchedFiles uses --no-renames for exactly this reason: with rename detection on, git prints only the destination path and silently drops the source). Files that do not end in ".go" are ignored; a "_test.go" change counts like any other Go file. The module root package maps to ".".
type Check ¶
type Check string
Check selects a conventional verification class.
func ParseChecks ¶
ParseChecks validates the explicit --checks list. A missing list defaults to the conventional lint, test, build sequence.
type CoverageBlock ¶ added in v0.164.0
type CoverageBlock struct {
File string
StartLine int
StartCol int
EndLine int
EndCol int
Statements int
Count int
}
CoverageBlock is one statement-range entry from a Go coverage profile (`go test -coverprofile`). Count is the number of times the profiled binary executed every statement in the block; Count == 0 means the block is uncovered.
func ParseCoverageProfile ¶ added in v0.164.0
func ParseCoverageProfile(profilePath string) ([]CoverageBlock, error)
ParseCoverageProfile reads a Go coverage profile (`mode: ...` header followed by `file:startLine.startCol,endLine.endCol numStmt count` rows) into its constituent blocks, preserving line ranges that profileTotals collapses into a single aggregate.
type CoverageDiagnostic ¶ added in v0.67.9
type CoverageDiagnostic struct {
Manifest string `yaml:"manifest" json:"manifest"`
SHA256 string `yaml:"sha256" json:"sha256"`
}
CoverageDiagnostic points at the private manifest containing lossless raw output for failed coverage jobs.
type CoverageDiagnosticFile ¶ added in v0.67.9
type CoverageDiagnosticManifest ¶ added in v0.67.9
type CoverageDiagnosticManifest struct {
SchemaVersion int `yaml:"schema_version" json:"schema_version"`
Repository string `yaml:"repository" json:"repository"`
Module string `yaml:"module" json:"module"`
// Ambient names the machine-state signals present in the gate's own
// environment and in the ancestors of TMPDIR and Module when the shard
// failures below were recorded. Empty when none were observed.
Ambient envguard.AmbientInputs `yaml:"ambient,omitempty" json:"ambient,omitempty"`
Files []CoverageDiagnosticFile `yaml:"files" json:"files"`
}
CoverageDiagnosticManifest is intentionally separate from CoverageReport: it contains unbounded command output and therefore stays in the private report root rather than crossing the bounded hook/session boundary.
type CoverageReport ¶
type CoverageReport struct {
SchemaVersion int `yaml:"schema_version" json:"schema_version"`
Repositories []RepositoryCoverage `yaml:"repositories" json:"repositories"`
Statements int `yaml:"statements" json:"statements"`
Covered int `yaml:"covered" json:"covered"`
Percentage float64 `yaml:"percentage" json:"percentage"`
}
CoverageReport is a deterministic, machine-readable coverage index.
func NewCoverageReport ¶
func NewCoverageReport(repositories []RepositoryCoverage) CoverageReport
NewCoverageReport aggregates reports in deterministic repository order.
type DeadcodeFinding ¶ added in v0.137.0
type DeadcodeFinding struct {
// Identity is the baseline key: import path + "." + function name. It
// deliberately excludes the source position, so moving a function or
// editing the lines above it does not invalidate the baseline and does not
// silently re-admit a genuinely new finding.
Identity string `yaml:"identity" json:"identity"`
Package string `yaml:"package" json:"package"`
Function string `yaml:"function" json:"function"`
File string `yaml:"file,omitempty" json:"file,omitempty"`
Line int `yaml:"line,omitempty" json:"line,omitempty"`
}
DeadcodeFinding is one unreachable function.
type DeadcodeOptions ¶ added in v0.137.0
type DeadcodeOptions struct {
// Patterns are the main packages to analyze. deadcode only starts from
// executables, so a pattern matching no main package reports nothing.
Patterns []string
// BaselinePath is relative to the repository root when not absolute.
BaselinePath string
// Tool overrides the analyzer invocation; nil uses DefaultDeadcodeTool.
Tool []string
// Filter is deadcode's -filter regular expression. Empty keeps deadcode's
// own default, which reports the module of the first listed package.
Filter string
// IncludeGenerated reports dead functions in generated files too. Off by
// default: generated code is not hand-wired, so its reachability is the
// generator's contract, not this repository's.
IncludeGenerated bool
// Timeout bounds the analyzer. Zero disables the bound.
Timeout time.Duration
}
DeadcodeOptions configures one reachability run.
type DeadcodeReport ¶ added in v0.137.0
type DeadcodeReport struct {
// Findings is every unreachable function found, baselined or not.
Findings []DeadcodeFinding `yaml:"findings" json:"findings"`
// New is the gate: findings absent from the baseline. Non-empty fails.
New []DeadcodeFinding `yaml:"new,omitempty" json:"new,omitempty"`
// Fixed lists baseline entries that are now reachable or gone. They never
// fail the gate; they are what the baseline should shed.
Fixed []string `yaml:"fixed,omitempty" json:"fixed,omitempty"`
// BaselinePath is the file consulted, empty when none was configured.
BaselinePath string `yaml:"baseline_path,omitempty" json:"baseline_path,omitempty"`
// BaselineMissing distinguishes "no baseline file yet" from "empty
// baseline". The first is a repository that has not adopted the gate; the
// second is a repository that has adopted it and is clean.
BaselineMissing bool `yaml:"baseline_missing,omitempty" json:"baseline_missing,omitempty"`
}
DeadcodeReport is the verdict of one run.
func Deadcode ¶ added in v0.137.0
func Deadcode(ctx context.Context, repositoryPath string, options DeadcodeOptions) (DeadcodeReport, error)
Deadcode runs the reachability analysis in repositoryPath and compares it against the configured baseline.
type FileLineOffsets ¶ added in v0.164.0
type FileLineOffsets struct {
// contains filtered or unexported fields
}
FileLineOffsets maps one file's merge-base (old) line numbers to its current (new) line numbers, built from that file's own unified-diff hunks. EvaluateRatchet uses it (review-696 non-blocking #3) to find the current position of a baseline-recorded statement whose file was edited elsewhere, instead of treating every uncovered block in a touched file as unattributable.
func (FileLineOffsets) Map ¶ added in v0.164.0
func (offsets FileLineOffsets) Map(oldLine int) (newLine int, ok bool)
Map translates a merge-base line to its current line. ok is false when oldLine falls inside a hunk's old range: the diff itself touched that exact line, so there is no single current line to point to for it — the direct changed-line rule (GitChangedLines) already covers whatever replaced it.
type InlineWriteSequenceViolation ¶ added in v0.165.0
type InlineWriteSequenceViolation struct {
// File is the path relative to root, slash-separated.
File string
// Func is the offending function's name, or "ReceiverType.Name" for a
// method.
Func string
// Line is the function declaration's line number, for a human
// reading the failure to jump straight to it.
Line int
}
InlineWriteSequenceViolation names one function outside internal/filewrite whose body directly calls a publish or create primitive spec/plans/coverage-to-100 task-9 consolidates into internal/filewrite, and is not exempted by either allow-list passed to FindInlineWriteSequences.
func FindInlineWriteSequences ¶ added in v0.165.0
func FindInlineWriteSequences(root string, pendingMigration, notAFileWritePublish map[string]string) ([]InlineWriteSequenceViolation, error)
FindInlineWriteSequences walks root (a module root, typically ParallelGuardModuleRoot's result) and reports every non-test Go function outside internal/filewrite and internal/unixcompat whose body directly calls a publish primitive (os.Rename, os.Link, syscall.Rename, syscall.Renameat, syscall.Link, the fd-relative unix.Rename/Renameat/ Renameat2/RenameatxNp/Link/Linkat family, the repo-local renameNoReplace helper, or a package-level alias of os.Link/os.Rename), a create primitive banned unconditionally (os.CreateTemp, ioutil.TempFile, os.Create), a create primitive banned only alongside a content write (os.OpenFile or unix.Openat carrying an O_CREAT-family flag), or os.WriteFile together with one of the publish primitives above in the same function -- skipping any file:func listed in pendingMigration or notAFileWritePublish. Callers pass PendingMigrationExemptions and NotAFileWritePublishExemptions for the real guard; a test may pass its own maps (or nil) to exercise the detector without touching package state, which keeps every test in this file parallel-safe.
func (InlineWriteSequenceViolation) String ¶ added in v0.165.0
func (v InlineWriteSequenceViolation) String() string
type ModuleCoverage ¶
type ModuleCoverage struct {
Path string `yaml:"path" json:"path"`
Statements int `yaml:"statements" json:"statements"`
Covered int `yaml:"covered" json:"covered"`
Percentage float64 `yaml:"percentage" json:"percentage"`
Attempts int `yaml:"attempts,omitempty" json:"attempts,omitempty"`
}
ModuleCoverage records the statement totals from one Go module's generated coverage profile.
type PackageBaseline ¶ added in v0.164.0
type PackageBaseline struct {
SchemaVersion int `json:"schema_version"`
SHA string `json:"sha,omitempty"`
Packages map[string]int `json:"packages"`
UncoveredBlocks map[string][]UncoveredBlock `json:"uncovered_blocks,omitempty"`
}
PackageBaseline is the per-package uncovered-statement baseline the ratchet compares against. It has no committed file of its own: go-ci's coverage job publishes it as a build artifact on every push to the default branch (spec/plans/coverage-to-100/README.md task-3(b)). Packages holds each package's uncovered statement count for the rise check; UncoveredBlocks holds the exact uncovered statement ranges behind that count, so a rise can be attributed to specific newly-uncovered statements instead of only reported as a number.
func BaselineFromProfile ¶ added in v0.164.0
func BaselineFromProfile(blocks []CoverageBlock, modulePath, sha string) PackageBaseline
BaselineFromProfile builds a PackageBaseline from a measured coverage profile, for publishing as the baseline artifact.
func ComputeBaselineAtRef ¶ added in v0.164.0
func ComputeBaselineAtRef(ctx context.Context, repoRoot, ref string, timeout time.Duration, options RunOptions) (PackageBaseline, error)
ComputeBaselineAtRef checks out ref into a throwaway git worktree and measures its per-package uncovered-statement counts, for the fallback path when no published baseline artifact exists yet (spec/plans/coverage-to-100/README.md task-3(b)). It is bounded by timeout so a missing artifact cannot make every PR pay for an open-ended run. options carries the same Retry/CoverageDiagnosticsDir a normal `wb coverage` run uses; RepositoryRunOptions is applied to the checked-out worktree so the merge base is measured through the identical .wb/quality.yaml-aware sharded, retried CoverWithOptions runner the head measurement uses (review item 5/non-blocking #1), not a bare `go test`.
func LoadBaseline ¶ added in v0.164.0
func LoadBaseline(path string) (PackageBaseline, error)
LoadBaseline reads a PackageBaseline written by WriteBaseline. A missing file is reported as os.ErrNotExist so callers can distinguish "no baseline available yet" from a malformed one.
type PackageRatchet ¶ added in v0.164.0
type PackageRatchet struct {
Package string
Uncovered int
BaselineUncovered int
HasBaseline bool
Rose bool
Changed bool // true when the PR itself touches a file (including _test.go) in this package
NewlyUncoveredChanged []RatchetFinding
Pass bool
}
PackageRatchet is one package's ratchet verdict.
type ParallelBaselineEntry ¶ added in v0.164.8
ParallelBaselineEntry is one committed line: a serial test or subtest, keyed by package directory + test name + subtest path (never by file:line, which shifts on every unrelated edit), with a mandatory reason.
func ScanSerialTests ¶ added in v0.164.8
func ScanSerialTests(root string) (serial []ParallelBaselineEntry, bareNolint []string, err error)
ScanSerialTests walks every _test.go file under root (skipping ParallelGuardExcludedDirs) and returns one entry per serial Test function or t.Run subtest, at any nesting depth, with an automatically classified reason. It also returns the set of nolint-without-a-reason sites, which the guard treats as offenders in their own right (S2/B2: a bare `//nolint:paralleltest` is no longer accepted).
type Progress ¶ added in v0.50.0
type Progress struct {
Repository string
Language string
Module string
Check Check
Command string
Detail string
State ProgressState
Status Status
Attempts int
Completed int
Total int
}
Progress describes one external check or a completed repository. Repository is filled by the fleet runner, which owns cross-repository scheduling.
type ProgressState ¶ added in v0.50.0
type ProgressState string
ProgressState identifies a visible quality-work transition.
const ( ProgressStarted ProgressState = "started" ProgressRetrying ProgressState = "retrying" ProgressCompleted ProgressState = "completed" ProgressRepositoryCompleted ProgressState = "repository_completed" )
type RatchetFinding ¶ added in v0.164.0
RatchetFinding names one newly uncovered statement that fails the ratchet.
type RatchetWarning ¶ added in v0.164.0
type RatchetWarning struct {
Package string `json:"package"`
File string `json:"file"`
Line int `json:"line"`
Reason string `json:"reason"`
}
RatchetWarning names one newly uncovered statement in a package the PR did not itself change (review B1, founder decision 2026-09-23: "only packages the PR changes" are hard-gated on their uncovered count; every other package's count rise is reported, never failed on).
type RepositoryCoverage ¶
type RepositoryCoverage struct {
Repository string `yaml:"repository" json:"repository"`
Path string `yaml:"path" json:"path"`
Status Status `yaml:"status" json:"status"`
Modules []ModuleCoverage `yaml:"modules,omitempty" json:"modules,omitempty"`
Statements int `yaml:"statements" json:"statements"`
Covered int `yaml:"covered" json:"covered"`
Percentage float64 `yaml:"percentage" json:"percentage"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
Diagnostic *CoverageDiagnostic `yaml:"diagnostic,omitempty" json:"diagnostic,omitempty"`
}
RepositoryCoverage records aggregate Go coverage for one repository.
func Cover ¶
func Cover(ctx context.Context, repository, path string) RepositoryCoverage
Cover measures all Go modules below path. It creates profiles in the system temporary directory, never in the repository.
func CoverWithOptions ¶
func CoverWithOptions(ctx context.Context, repository, path string, options RunOptions) RepositoryCoverage
CoverWithOptions measures coverage with a deadline and retries for each Go module's test command.
type RunOptions ¶
type RunOptions struct {
Timeout time.Duration
Retry int
// CheckTimeout bounds one logical verification check, including all of its
// command attempts and any process-isolated Go shards. Zero leaves the
// existing per-command Timeout behavior unchanged.
CheckTimeout time.Duration
// ShardAttemptTimeout bounds one process-isolated Go test shard attempt.
// Zero retains Timeout as the shard-attempt bound when Timeout is set.
ShardAttemptTimeout time.Duration
// GoTestShards runs each explicitly named Go package in this many
// process-isolated shards. It is opt-in because TestMain and process-global
// fixtures run once per shard; callers must name packages whose contract
// permits that isolation. Discovery invokes TestMain once before each shard
// process invokes it again.
GoTestShards int
// GoShardPackages are module-relative package patterns such as
// ./internal/worktrees. Packages not named here still run exactly once.
GoShardPackages []string
// GoLintCommands replaces the default `go vet ./...` lint step with the
// repository-owned argv sequences from .wb/quality.yaml. Structured argv
// keeps exact tool pins reproducible without invoking a shell.
GoLintCommands [][]string
// CoverageProfile retains the exact merged Go profile for one module.
// Fleet and multi-module adapters reject it rather than inventing names.
CoverageProfile string
// CoverageDiagnosticsDir retains raw output from failed process-isolated
// coverage jobs beside the durable coverage report. The human-facing error
// remains bounded; this private artifact is the lossless recovery path.
CoverageDiagnosticsDir string
// CoverageDiagnosticsRepository identifies the owning repository in the
// private manifest when a fleet runner executes several repositories.
CoverageDiagnosticsRepository string
// SingleWorker constrains every check to one worker, so a verification
// run cannot exceed the workstation's concurrency cap on its own. Go tests
// gain `-p 1` and never `-race`; package-script Node runs gain
// `--parallel=1` and `--maxWorkers=1`, while mixed Nx target runs gain
// only Nx's executor-neutral `--parallel=1`. The Nx daemon and cache are
// disabled in either case.
//
// Serialization is deliberately *not* a substitute for per-file
// isolation: nothing here relaxes an isolation flag, because a serialized
// leak is worse than a flake — it is reproducible and misattributed.
SingleWorker bool
// Env is appended to each check's environment as KEY=VALUE entries. It is
// how a caller states the environment a run must carry (GOWORK=off,
// NX_DAEMON=false) rather than leaving it to the shell that invoked wb.
Env []string
// Progress receives lifecycle events for external checks. Callers may use it
// for terminal diagnostics; reports remain the authoritative output.
Progress func(Progress)
}
RunOptions bounds a single external command and retries only failed attempts. Zero Timeout disables the per-command deadline.
func RepositoryRunOptions ¶ added in v0.67.1
func RepositoryRunOptions(root string, base RunOptions) (RunOptions, error)
RepositoryRunOptions applies an explicit repository-owned quality policy to one validation run. Absence is the portable default; malformed or ambiguous policy fails closed rather than silently falling back to a slower or weaker command.
type Status ¶
type Status string
Status is the outcome of a repository or a discrete verification command.
type UncoveredBlock ¶ added in v0.164.0
type UncoveredBlock struct {
File string `json:"file"`
StartLine int `json:"start_line"`
StartCol int `json:"start_col"`
EndLine int `json:"end_line"`
EndCol int `json:"end_col"`
}
UncoveredBlock names one uncovered statement range in the baseline, keyed the same way a Go coverage profile line names it, so a later EvaluateRatchet run can tell which of a package's currently-uncovered blocks are new against the baseline and which already existed there (spec/plans/coverage-to-100/README.md task-3, review item B2: a count-only failure must still name file:line).
type ValidationCacheKey ¶ added in v0.98.3
type ValidationCacheKey struct {
Repository string `json:"repository"`
TargetRevision string `json:"target_revision"`
Checks []Check `json:"checks"`
QualityConfigSHA string `json:"quality_config_sha"`
WBRevision string `json:"wb_revision"`
GoToolchain string `json:"go_toolchain"`
ModuleFiles []string `json:"module_files"`
// ValidatorSHAs binds cached evidence to the executable bytes that produced
// it. In particular, SpecScore's rules can change between installed
// versions while the repository and WB revision remain identical.
ValidatorSHAs map[string]string `json:"validator_shas,omitempty"`
}
ValidationCacheKey identifies the exact inputs that make a verification report reusable. Checks remain ordered because the order is part of the command contract and can affect the resulting evidence.
func NewValidationCacheKey ¶ added in v0.98.3
func NewValidationCacheKey(repository, targetRevision, root, wbRevision string, checks []Check, validatorSHAs map[string]string) (ValidationCacheKey, error)
NewValidationCacheKey fingerprints repository-local policy, module manifests, and the executable digests used by external validators. The caller supplies the exact target revision and WB revision, and passes nil validatorSHAs when no external validator participates.
One constructor rather than two: the original signature was kept alongside a WithValidators variant that delegated to it, and `wb deadcode` immediately reported the original as unreachable once the only caller moved. A dead wrapper beside a live near-identical function is a reliable way to have the wrong one called later.
type VerificationEntry ¶
type VerificationEntry struct {
Language string `yaml:"language" json:"language"`
Module string `yaml:"module,omitempty" json:"module,omitempty"`
Check Check `yaml:"check" json:"check"`
Command string `yaml:"command,omitempty" json:"command,omitempty"`
Status Status `yaml:"status" json:"status"`
Detail string `yaml:"detail,omitempty" json:"detail,omitempty"`
Attempts int `yaml:"attempts,omitempty" json:"attempts,omitempty"`
}
VerificationEntry is one command WB attempted or intentionally skipped.
type VerificationReport ¶
type VerificationReport struct {
Repository string `yaml:"repository" json:"repository"`
Path string `yaml:"path" json:"path"`
// Revision and WorkspaceClean are populated by the WB command adapter
// around the complete verification run. They let a downstream receipt bind
// successful mechanisms to the exact clean Git tree they exercised.
Revision string `yaml:"revision,omitempty" json:"revision,omitempty"`
WorkspaceClean bool `yaml:"workspace_clean,omitempty" json:"workspace_clean,omitempty"`
Status Status `yaml:"status" json:"status"`
Results []VerificationEntry `yaml:"results" json:"results"`
}
VerificationReport records all conventional checks applicable to a repository. Unsupported stacks and missing optional Node scripts are skipped rather than treated as failures.
func LoadValidationCache ¶ added in v0.98.3
func LoadValidationCache(cacheRoot string, key ValidationCacheKey) (VerificationReport, bool, error)
LoadValidationCache returns only an intact terminal report with an exact key. Any malformed, stale, or otherwise incomplete record is a cache miss.
func Verify ¶
func Verify(ctx context.Context, repository, path string, checks []Check) VerificationReport
Verify runs the requested conventional Go and Node checks. The caller owns cross-repository parallelism; checks within one module run in the requested order to keep output and failures clear.
func VerifyWithOptions ¶
func VerifyWithOptions(ctx context.Context, repository, path string, checks []Check, options RunOptions) VerificationReport
VerifyWithOptions runs the requested checks with per-command reliability controls. The returned report includes every attempted, skipped, passed, or failed command.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
parallelbaseline
command
Command parallelbaseline is the single source of truth for internal/quality/testdata/paralleltest_baseline.txt.
|
Command parallelbaseline is the single source of truth for internal/quality/testdata/paralleltest_baseline.txt. |