Documentation
¶
Overview ¶
Package invariants is the typed home for sorcerer's design invariants — the CLAUDE.md prose rules ("Persist-before-apply", "Session termination is centralized through terminateSession", "State mutation is single- writer" and the rest) promoted to executable Invariant values that the daemon's periodic sweep, the property-test wrapper, and the recognizer subscription can all consume off one canonical registry.
The package layout (described in docs/structural-coverage/phase-1-invariant-manifest.md):
- invariant.go (this file): the Invariant interface, the Violation struct, and the Severity enum — the read-only surface every downstream consumer talks to.
- state.go: the goroutine-exclusive State snapshot and its constructor; the snapshot is what Check methods operate over.
- registry.go: the RW-mutex-protected static registry; invariants opt in via init() blocks in their declaring files.
- <one-file-per-invariant>.go: each concrete Invariant implementation. The first instance lands in persist_before_apply.go; the remaining named invariants ship as follow-on issues.
The package compiles with no external dependencies beyond the standard library and the existing internal/store + internal/sm types — the goroutine-exclusive deep-copy projections live in internal/store/ so the property-check substrate and the boot-time issuestore rehydration path share one definition of "what an issue snapshot looks like".
Index ¶
- Constants
- Variables
- func InvariantSubjectIsCompatible(name, issueState, issueKind string) (compatible bool, checked bool)
- func IsGoTestFile(path string) bool
- func IsManagedProjectDomain(name string) bool
- func IsMigrationFile(path string) bool
- func IsTransientTolerant(name string) bool
- func LintRegistry(repoRoot string) []error
- func MatchedSensitivePackage(path string) (string, bool)
- func RebuildLiveSubjects(s *State)
- func Register(inv Invariant)
- func RegisterStatic(inv Invariant)
- func Scan(rootDir string) (map[string][]string, []error)
- func Unregister(name string)
- func UnregisterStatic(name string)
- type ActivationVerdictProbeFunc
- type BufferedVerdictReplayActivityProbeFunc
- type DeliveredReviewerVerdictProbeFunc
- type Invariant
- type LastPlanTransitionAtProbeFunc
- type LockedEphemeralWorktree
- type ManagedProjectDomainOverride
- type PerIssueWorktreeEntry
- type PerIssueWorktreeListProbeFunc
- type PersistedIssueFreeze
- type PersistedReader
- type PlanBranchProbeFunc
- type RecordedPlanReviewerOutcomeProbeFunc
- type Severity
- type SnapshotInput
- type State
- type SubjectCompatibilityChecker
- type TransientTolerant
- type Violation
- type WorktreeListProbeFunc
Constants ¶
const PerIssueWorktreeLeakGraceDuration = 30 * time.Minute
PerIssueWorktreeLeakGraceDuration is the bounded grace window (beyond_grace) after an owner becomes reapable within which the deterministic teardown or the next GC sweep is expected to remove the per-issue worktree. A terminal-owner wrapper that persists on disk past this window is a detected leak. Exported so the unit / property / vacuity tests reference the constant rather than a magic number, staying robust to future tuning.
const PlanReviewRecordedVerdictThreshold = 5 * time.Minute
PlanReviewRecordedVerdictThreshold is the maximum age a recorded-but- unharvested plan_reviewer verdict may carry on a plan_review issue before the invariant flags it as a non-progressing wedge. The in-session harvest seam applies a recorded outcome synchronously on the terminating session-result event and boot recovery applies it on the next restart, so a healthy recorded verdict clears far inside this window; any survival past it signals the harvest never ran. Mirrors the per-PR reviewer thresholds so the complementary verdict-progress invariants share one tuning convention. Exported so future tuning is a single constant edit.
const ReviewerJudgingBufferedVerdictThreshold = 5 * time.Minute
ReviewerJudgingBufferedVerdictThreshold is the maximum age a buffered reviewer verdict may carry on a review_judging issue before the invariant flags it as a non-progressing wedge. A healthy buffer clears within ~one github-prs poll interval (well under this), so any survival past it signals a stuck issue. Exported so future tuning is a single constant edit.
const ReviewerJudgingDeliveredVerdictThreshold = 5 * time.Minute
ReviewerJudgingDeliveredVerdictThreshold is the maximum age a delivered- but-unharvested on-disk reviewer verdict may carry on a review_judging issue before the invariant flags it as a non-progressing wedge. The in-session harvest seam applies a delivered verdict synchronously on the teardown event and boot recovery applies it on the next restart, so a healthy delivered verdict clears far inside this window; any survival past it signals the harvest never ran. Mirrors the sibling threshold so the two complementary invariants share one tuning convention. Exported so future tuning is a single constant edit.
Variables ¶
var SensitivePackages = []string{
"internal/daemon/*.go",
"internal/sm/*.go",
"internal/issuestore/*.go",
"internal/agentcli/*.go",
"internal/role/*.go",
"internal/recognizer/*.go",
"internal/triager/*.go",
"internal/plan/*.go",
"internal/github/*.go",
"internal/schedule/*.go",
}
SensitivePackages is the canonical list of package globs where the downstream coverage pipeline fires a warning when a touched file is not covered by any registered invariant. The body analyzer (on issue create) and the post-merge coverage-gap sweep both reference THIS slice so they share one source of truth for "which packages we expect to be invariant-covered".
The entries are filepath.Match globs (one nesting level each — `*` does not cross `/`) rather than trailing-slash package paths so they compose directly with filepath.Match alongside each invariant's CoveredSurfaces. Because every entry is a `.go` glob and filepath.Match has no negation — it cannot express ".go but not _test.go" — the sensitive-surface membership matcher (MatchedSensitivePackage / IsGoTestFile) excludes `_test.go` paths BEFORE applying these globs: a test asserts behavior rather than defining a guarded production surface, so it is never a member, and the exclusion lives in the matcher rather than these strings (which stay byte-identical `.go` globs). The set mirrors the core daemon-side packages whose correctness the invariant framework guards — and deliberately excludes the internal/invariants/ package itself, because the framework is not one of the packages it guards. That package's top-level files are invariant declarations whose CoveredSurfaces() structurally point at the daemon code they guard and never at themselves (so they can never self-cover), its *_test.go files are test code rather than guarded production surface, and the registry's own integrity is already enforced by LintRegistry — so the package needs no separate invariant coverage, and including it only ever produced self-referential false positives on each newly-added invariant file.
Independently of which package a path falls in, Go test files (final path element ending in "_test.go") are excluded from sensitive-package matching by MatchedSensitivePackage — the single matcher both daemon coverage consumers route through. Test code asserts behavior and is never a guarded production surface an invariant's CoveredSurfaces() can name (every implementation names only production files), so a test file can never be "covered" and would otherwise be a permanent, unresolvable coverage gap. This mirrors the rationale above for excluding the framework's own package.
Likewise excluded — by the same MatchedSensitivePackage chokepoint (see IsMigrationFile) — are sequential one-shot schema-migration files (final path element of the shape "migration_<digits>_*.go"). A migration is a one-shot DDL step, not a guarded production surface: the schema and behavior it establishes are guarded, if at all, by the behavioral issuestore files (the specs storage and CRUD files), so an additive migration that introduces no design invariant of its own would otherwise be a permanent, unresolvable coverage gap. The rare migration that DOES encode a structural invariant directly (e.g. migration_051's immutability trigger) opts back into coverage explicitly via its invariant's CoveredSurfaces() listing — that relationship lives in InvariantsForPath, which does not consult IsMigrationFile, so this exclusion changes only the gap-detection signal and never an explicit coverage relationship.
Functions ¶
func InvariantSubjectIsCompatible ¶
func InvariantSubjectIsCompatible(name, issueState, issueKind string) (compatible bool, checked bool)
InvariantSubjectIsCompatible reports whether issueState+issueKind is a compatible subject for the named runtime invariant — the lookup the recognizer's audit-filing premise-check consults (SOR-2438). It mirrors IsTransientTolerant's resolution path (Lookup against the runtime registry, type-assert the optional interface):
- checked=false means the named invariant does not implement SubjectCompatibilityChecker (or no runtime invariant is registered under name) — the invariant has "no opinion", and the caller MUST treat the subject as compatible (the premise-check passes it).
- checked=true returns the invariant's verdict in compatible: true when the (state, kind) pair is a valid subject for the invariant's cohort, false when it is definitionally outside it (e.g. a spec-phase invariant subject that is an implementation issue or a terminal planning issue).
Resolution runs through the runtime registry only; static invariants never emit runtime violations, so an audit premise can only cite a runtime invariant's subject.
func IsGoTestFile ¶
IsGoTestFile reports whether path names a Go test file (a `_test.go` suffix). It is the single definition of the test-file exclusion the sensitive-surface membership matchers share: a test asserts behavior rather than defining a guarded production surface, and filepath.Match cannot express ".go but not _test.go" in one glob, so the exclusion must live in the matcher rather than the SensitivePackages strings.
func IsManagedProjectDomain ¶
IsManagedProjectDomain reports whether the runtime invariant registered under name declares its violations as managed-project-domain via the optional ManagedProjectDomainOverride interface (see invariant.go). It returns false — the `product` default — when no runtime invariant is registered under name, when the registered invariant does not implement ManagedProjectDomainOverride, or when it implements the method returning false. So a registry-invariant violation defaults to product-domain and only an explicit opt-in flips it to managed_project. Resolution mirrors IsTransientTolerant: it runs through Lookup against the runtime registry, the same path the recognizer filing chokepoint consults — static invariants never emit runtime violations, so runtime-only resolution is correct.
func IsMigrationFile ¶
IsMigrationFile reports whether path names a sequential one-shot schema migration Go source file — a final path element of the shape "migration_<digits>_<name>.go" (the migration-number-prefixed file the issuestore migration runner applies in order). Like IsGoTestFile it is the single definition of the migration-file exclusion the sensitive- surface membership matchers share: a one-shot migration establishes schema rather than defining a guarded production surface, and filepath.Match cannot express the "migration_<digits>_*" basename shape in one `.go` glob, so the exclusion lives in the matcher rather than the SensitivePackages strings.
The predicate is purely structural (basename shape only), so it returns true for ANY migration file regardless of whether some invariant happens to name it explicitly — e.g. migration_051, whose immutability trigger is listed by spec-approved-content-immutability's CoveredSurfaces(). That is deliberate and harmless: the exclusion only suppresses the gap-detection "is this an uncovered guarded surface?" signal, which never fired for an already-covered file anyway, while the explicit coverage relationship lives in InvariantsForPath (which does NOT consult this predicate) and is untouched.
func IsTransientTolerant ¶
IsTransientTolerant reports whether the runtime invariant registered under name declares itself transient-tolerant via the optional TransientTolerant interface (see invariant.go). It returns false when no runtime invariant is registered under name, when the registered invariant does not implement TransientTolerant, or when it implements the method returning false — so a defect invariant (the default) and an unknown name both classify as "not transient-tolerant". Resolution runs through Lookup, the same runtime-registry path the recognizer's reactive rate-gate consults; static invariants never emit runtime violations, so runtime-only resolution is correct.
func LintRegistry ¶
LintRegistry validates every registered invariant's CoveredSurfaces against repoRoot and returns one error per coverage defect. It walks BOTH registries (runtime and static) so the gate holds analyzer shims to the same standard as runtime invariants. An empty slice means every registered invariant declares at least one surface and every glob both parses cleanly and resolves to at least one existing file under repoRoot.
repoRoot is the filesystem directory the repo-relative globs resolve against (e.g. "../.." from the internal/invariants package, or the coverage CLI's --root). The resolution check is intentionally NOT run at Register()/RegisterStatic() time — the daemon binary's cwd is not the repo root, so glob resolution would be non-portable at init; the lint takes the root explicitly, mirroring annotation.Scan.
func MatchedSensitivePackage ¶
MatchedSensitivePackage returns the first SensitivePackages glob that path matches and whether any matched — the canonical sensitive-surface membership predicate the downstream coverage pipeline (create-time body warning, reviewer-side check, post-merge coverage-gap sweep) shares so all three stages agree on "is this added file a guarded production surface?".
A `_test.go` path (see IsGoTestFile) and a sequential one-shot migration file (see IsMigrationFile) are never members: both exclusions are applied BEFORE the glob loop because every SensitivePackages entry is a `.go` glob and filepath.Match has no negation, so a `.go` glob unavoidably matches such a basename. The returned glob is the stable, human-readable "package" value the consumers carry. A malformed glob is skipped (the registration lint is the surface that rejects malformed entries loudly), mirroring pathMatchesAnySurface.
func RebuildLiveSubjects ¶
func RebuildLiveSubjects(s *State)
RebuildLiveSubjects re-derives s.LiveSubjects from s.Issues by walking the latter and filtering to non-terminal entries. The framework's canonical state-construction path is Snapshot — which populates both facets in one pass from the non-terminal projection. This helper exists for tests that hand-construct a *State{Issues: ...} or hand-mutate s.Issues after Snapshot returns and want to keep LiveSubjects in sync without re-running Snapshot's deep-copy pass. No-op on a nil receiver.
func Register ¶
func Register(inv Invariant)
Register inserts inv into the package-level registry under inv.Name(). Panics when an invariant with the same name has already registered — the package-level registry is intolerant of duplicates because the test-file annotation scanner and the periodic-sweep dispatcher key off the name string, and a silent overwrite would either drop a load- bearing check or wedge the sweep on an unexpected behavior change. init()-time registrations are the only callers, so the panic surfaces during package init and a duplicate is caught before the daemon comes up.
func RegisterStatic ¶
func RegisterStatic(inv Invariant)
RegisterStatic inserts inv into the parallel STATIC registry under inv.Name(). Static invariants are the build-time, analyzer-enforced members of the framework; they carry a no-op Check (the analyzer, not the runtime sweep, is the enforcement surface) and live OUT of the runtime registry so All() — the slice the periodic sweep walks — never includes them. See the staticRegistry comment for the full rationale.
Panics when the name is already registered in EITHER registry: the coverage gate and the annotation scanner treat the runtime and static names as one flat namespace, so a collision between a runtime invariant and a static one would make a "// invariants: <name>" annotation ambiguous. As with Register, init()-time registrations are the only callers, so the panic surfaces during package init.
func Scan ¶
Scan walks rootDir looking for Go test files (*_test.go) whose top-of-file comment block declares one or more invariant defenders.
The leading comment block is every comment line preceding the `package` clause; any "// invariants: <name1>, <name2>" line found there contributes its names to the declarers map. Lines after the `package` clause are ignored (per the architecture contract: the scanner is intentionally narrow so misplaced annotations are caught by the coverage gate's under-defended check rather than parsed silently from the file body).
Returns:
- declarers: map keyed by invariant name; each value is a sorted, deduplicated list of test file paths relative to rootDir. The map only contains names that resolved against the package-level registry (i.e. against the set returned by invariants.All); any annotation referencing an unknown name is rejected via the errors slice instead.
- errs: one error per annotation referencing an unknown invariant ("<file>: unknown invariant <name>") and one per malformed annotation line ("<file>: malformed annotation line ..."). Empty when every annotation parses cleanly against a known invariant.
A nil error slice means every file's annotations resolved cleanly; a non-empty slice still returns a (possibly partial) declarers map — the CLI's coverage gate inspects both surfaces.
func Unregister ¶
func Unregister(name string)
Unregister removes the invariant registered under name. No-op when no such invariant exists. Used by test cleanup paths that install a synthetic Invariant for the duration of one test and need to undo the registration so concurrent tests in the same binary see a clean registry. Production code never calls Unregister — the registry is init-time-populated and static for the daemon's lifetime.
func UnregisterStatic ¶
func UnregisterStatic(name string)
UnregisterStatic removes the static invariant registered under name. No-op when no such invariant exists. Test-cleanup companion to Unregister; production code never calls it.
Types ¶
type ActivationVerdictProbeFunc ¶
type ActivationVerdictProbeFunc func(planKey string) (ridEvidencePresent map[string]bool, err error)
ActivationVerdictProbeFunc resolves per-requirement evidence status for one plan_activating plan: given a plan key it returns a map from requirement R-ID to whether that requirement's latest recorded activation verdict carries a non-nil evidence pointer. A nil map return means no verdict row has been recorded yet — the activation-probe-evidence-cited invariant's skip sentinel. A non-nil error means the probe failed; the invariant skips that plan. The production implementation reads the latest per-requirement activation verdict (Daemon.activationVerdictEvidencePresent → latestActivationVerdicts) so the invariant sees exactly the evidence pointers the recordActivationVerdict path persisted. The probe does the DB read at snapshot-construction time so the consuming invariant's Check stays I/O-free. Implementations must be safe for concurrent use. Mirrors RecordedPlanReviewerOutcomeProbeFunc for the activation-verdict path.
type BufferedVerdictReplayActivityProbeFunc ¶
BufferedVerdictReplayActivityProbeFunc resolves the unix-seconds timestamp of the most recent reviewer_verdict_replayed event for a review_judging issue carrying a buffered reviewer verdict — the progress signal replayPendingReviewerVerdict emits on every github-prs poll that replays the buffer against terminal CI. It returns (ts, true) when at least one replay event exists for the key and (0, false) when none does (the SOR-2628 poller-omission wedge, where the poller never replays the state). The production implementation reads issuestore.ListEvents with EventFilter{Kind: reviewer_verdict_replayed, Newest: true, Limit: 1} and takes the single most-recent row's ts; the probe does the DB read at snapshot-construction time so the consuming reviewer-judging-with-buffered-verdict-progresses Check stays I/O-free. Implementations must be safe for concurrent use. It is the replay-activity staleness reference that re-bases the buffered-verdict progress backstop off the bare mirror timestamp (issues.updated_at), which the idempotent no-op re-buffer freezes exactly while replays occur. Mirrors LastPlanTransitionAtProbeFunc for the buffered-verdict-replay path.
type DeliveredReviewerVerdictProbeFunc ¶
type DeliveredReviewerVerdictProbeFunc func(sessID string, repos []string) (decision string, delivered bool)
DeliveredReviewerVerdictProbeFunc resolves whether a reviewer session has already delivered a complete, schema-valid verdict to disk: given the daemon-side session id and the issue's repos allowlist it returns the verdict's decision string and whether a complete review.json is present. The production implementation reads <projectRoot>/.sorcerer/sessions/<sessID>/review.json and runs it through ParseReviewerVerdict — the SAME reader replayReviewerVerdictFromDisk (boot recovery) and the in-session harvest path use, so the invariant detects exactly the verdicts those paths would harvest. A (decision=="", delivered==false) result means no complete verdict is on disk yet (the reviewer is still in flight, or wrote nothing parseable). The probe does the disk I/O at snapshot-construction time so the consuming invariant's Check stays I/O-free. Implementations must be safe for concurrent use.
type Invariant ¶
type Invariant interface {
Name() string
Description() string
Check(ctx context.Context, s *State) []Violation
CoveredSurfaces() []string
}
Invariant is the property-check contract every entry in the registry satisfies. Implementations are stateless (the registry holds singletons instantiated at init time) and pure with respect to the supplied State snapshot — no I/O, no goroutine launches, no daemon-state mutation.
Name returns the canonical, kebab-case identifier (e.g. "persist-before-apply"); the registry enforces uniqueness and the test- file annotation scanner keys off this string. Description returns a one-paragraph human-readable summary of what the invariant promises — surfaced in the recognizer's auto-filed planning-issue body and in the coverage-report CLI.
Check walks the supplied State snapshot and returns one Violation per reachable inconsistency it observes. The returned slice may be nil or empty — both mean "the invariant holds". Implementations must NOT mutate the State snapshot; the snapshot is shared across every invariant on a single sweep tick and racing writes would corrupt downstream invariants' reads.
CoveredSurfaces returns the repo-relative file globs the invariant constrains — the production files whose correctness this invariant guards. The downstream coverage pipeline (the issue-create body analyzer, the reviewer-side coverage check, the post-merge maintenance sweep) asks "what invariants cover file X?" via InvariantsForPath, which matches a path against every registered invariant's CoveredSurfaces. Globs use Go's filepath.Match syntax (`*`, `?`; no `**`): `*` does not cross a `/`, so a glob covers exactly one nesting level — list one glob per level explicitly for files in nested directories. The returned slice MUST be non-empty (the registration lint rejects a zero-surface invariant) and every glob must parse cleanly and resolve to at least one existing file. Over-cover is the safe failure mode: downstream consumers de-duplicate, but an unlisted surface is invisible to the pipeline.
The build-time static-analyzer invariants (registered via RegisterStatic) implement the same Invariant interface, so they implement CoveredSurfaces too — there is no separate Go interface for static invariants. A static invariant's covered surfaces are the production files its analyzer scans.
func All ¶
func All() []Invariant
All returns every registered Invariant sorted by Name. The deterministic order lets the periodic-sweep dispatcher, the property-test driver, and the coverage-report CLI all walk the registry in a consistent sequence — diff-friendly test output, predictable event-log ordering, and stable golden files.
Returns a fresh slice on every call so the caller may mutate it (sort by a different key, filter) without affecting the registry's internal view. The Invariant pointers themselves are still the registry's singletons; callers MUST NOT mutate them.
func AllStatic ¶
func AllStatic() []Invariant
AllStatic returns every registered STATIC invariant sorted by Name. The coverage-report CLI walks All() and AllStatic() together so a static invariant is held to the same two-declarer quorum as a runtime one; the periodic sweep walks only All() so it never calls a static invariant's no-op Check. Returns a fresh slice on every call.
func InvariantsForPath ¶
InvariantsForPath returns the de-duplicated set of registered invariants whose CoveredSurfaces glob-matches path — the canonical "what invariants cover file X?" lookup the downstream coverage pipeline consumes. It enumerates BOTH registries (runtime via All and build-time analyzer shims via AllStatic) because a static invariant constrains real files just as a runtime one does.
path is a repo-relative slash path (e.g. "internal/daemon/mirror.go"). Matching uses filepath.Match per glob; an invariant is included at most once regardless of how many of its globs match (de-dup by invariant). The result is a fresh slice sorted by Name, consistent with All() / AllStatic(); the empty slice means no registered invariant covers path.
Complexity is O(n × g) — n invariants, g globs each — both small.
func Lookup ¶
Lookup returns the Invariant registered under name, or (nil, false) when no such invariant exists. Used by the property-test wrapper to pick a single invariant by name and by the coverage-report CLI when resolving annotation-cited invariants against the registry.
func LookupStatic ¶
LookupStatic returns the static invariant registered under name, or (nil, false) when no such static invariant exists. Used by the staticcheck package's registration test to assert the analyzer's registry shim self-registered.
type LastPlanTransitionAtProbeFunc ¶
LastPlanTransitionAtProbeFunc resolves the unix-seconds timestamp of the most recent plan-state transition in the history table for a planning issue key — the last history row's ts, written ONLY by persistTransitionHistory on a genuine SM move. It returns (ts, true) when a history row exists and (0, false) when no history has been written (a brand-new issue or a migration/restart gap). The production implementation reads issuestore.ListHistory and takes the final (most-recent) entry; the probe does the DB read at snapshot-construction time so the consuming spec-phase-eventually-advances Check stays I/O-free. Implementations must be safe for concurrent use. It is the real-advance staleness reference that re-bases the spec-phase backstop off the bare mirror timestamp (issues.updated_at), which judge re-dispatch / park thrash bumps without making real progress.
type LockedEphemeralWorktree ¶
type LockedEphemeralWorktree struct {
BareClone string
Path string
LockReason string
Age time.Duration
}
LockedEphemeralWorktree describes one locked ephemeral worktree found in a daemon-managed bare clone. Path is the worktree's absolute path; LockReason is the string passed to `git worktree lock --reason` at the owning operation's creation site (one of the four cpb-squash- / cpb-main-merge- / plan-assemble- / verifier- prefixed reasons, each embedding the owning plan-branch / child / issue key); Age is derived from the temp-parent directory mtime, mirroring the prune sweep's age convention. BareClone is the bare-clone path the worktree was found in, carried so a violation can name where the leak lives. Only ephemeral (prefix-matched) locked entries appear here — the production probe filters the non-ephemeral entries (the bare clone's own checkout, real per-issue worktrees) out before returning.
type ManagedProjectDomainOverride ¶
type ManagedProjectDomainOverride interface {
ManagedProjectDomain() bool
}
ManagedProjectDomainOverride is an OPTIONAL interface a runtime Invariant MAY implement to declare that ITS violations arise from a managed project's own code / config rather than from the product itself — flipping the recognizer filing's defect-domain classification from the default `product` to `managed_project` (SPEC-SOR-3065-v1 R4/R5). A registry-invariant violation is, by construction, a defect in the product's own self-model (invariants.All()) and so is `product`-domain by default; only an invariant that opts in here and returns true overrides that to `managed_project`.
Like TransientTolerant, the interface is deliberately NOT part of the Invariant contract: it is resolved by a type assertion against the registered invariant (see IsManagedProjectDomain). An invariant that does not implement it — every invariant registered today — is treated as `product`-domain (the default), so the change is purely additive and no existing invariant needs updating. A future managed-project-local invariant opts in with one method.
type PerIssueWorktreeEntry ¶
PerIssueWorktreeEntry describes one top-level per-issue worktree wrapper the per-issue-worktree-leaked invariant's probe reports. Key is the directory basename (an issue key or a plan key); Path is the absolute wrapper path; Age is the wrapper mtime age at list time. It mirrors gh.PerIssueWorktreeEntry but is defined here so the invariants package stays self-contained — the probe type carries no internal/github dependency (the daemon-side wiring adapts the gh shape into this one).
type PerIssueWorktreeListProbeFunc ¶
type PerIssueWorktreeListProbeFunc func() ([]PerIssueWorktreeEntry, error)
PerIssueWorktreeListProbeFunc enumerates every top-level per-issue worktree wrapper under <projectRoot>/.sorcerer/worktrees/ for the per-issue-worktree-leaked invariant, or returns an error if the filesystem I/O fails. Unlike WorktreeListProbeFunc it takes no bare-clone argument — the per-issue worktrees live in one project-level directory, not per bare clone — so it is a single-call probe with no per-key memoization. Nil disables the invariant's check. A non-nil err means the probe itself failed; the consuming invariant skips rather than treating the result as leak-free, the same transient-failure tolerance WorktreeListProbeFunc carries. Implementations must be safe for concurrent use.
type PersistedIssueFreeze ¶
type PersistedIssueFreeze struct {
// Snapshot is the persisted projection GetIssueSnapshot returned at t0; a
// nil Snapshot with a nil Err means the row was not persisted (the
// pre-persistence window — Check skips it).
Snapshot *store.IssueSnapshot
// Err is the GetIssueSnapshot error captured at t0; non-nil drives Check's
// warning violation (the persisted lookup failed), frozen here so the
// off-main walk reproduces the inline decision without a live read.
Err error
}
PersistedIssueFreeze is one LiveSubject's persisted projection captured at snapshot-construction time (t0) on the main loop, paired with any read error from that capture. persist-before-apply.Check reproduces its (Snapshot, Err) → {warning | skip | compare} decision off this frozen value rather than a live PersistedReader read taken during the seconds-long, git-probe-paced off-main inv.Check walk — a live read would race the freed main loop's concurrent persist+apply transitions and, for any LiveSubject that transitions inside the walk window, compare the frozen in-memory projection against an advanced persisted row and emit a false-positive critical divergence (the SOR-2448 off-main regression the reviewer caught).
type PersistedReader ¶
type PersistedReader interface {
// GetIssueSnapshot returns the persisted projection of the issue at
// key, or (nil, err) on any I/O failure. A row that does not exist
// is returned as (nil, nil) — the caller distinguishes "not
// persisted" from "look-up failed" via the error.
GetIssueSnapshot(ctx context.Context, key string) (*store.IssueSnapshot, error)
// GetSessionSnapshot returns the persisted projection of the session
// at id. Same not-found / error contract as GetIssueSnapshot.
GetSessionSnapshot(ctx context.Context, id string) (*store.SessionSnapshot, error)
// Specs returns the persisted projection of every specs-table row,
// keyed by specs.id. The spec-storage invariants need the WHOLE table
// (lineage walks resolve previous_version_id pointers across rows;
// referential-integrity resolves issues.spec_id against the set), so —
// unlike the single-row Get* accessors — this returns the full map in
// one read; Snapshot calls it once at snapshot-construction time and
// stores the result on State.Specs. A successful read MUST return a
// non-nil (possibly empty) map so the referential-integrity invariant
// can distinguish "loaded, empty table" from "reader unavailable"; a
// non-nil error returns a nil map and Snapshot leaves State.Specs nil.
Specs(ctx context.Context) (map[string]*store.SpecSnapshot, error)
// SpecFindings returns the persisted projection of every spec_findings
// row as a flat slice. The spec-approved-no-open-error-findings
// invariant needs the WHOLE table (it filters by the spec_approved
// issue's spec_id across the set), so — like Specs — this returns the
// full slice in one read; Snapshot calls it once at snapshot-construction
// time and stores the result on State.SpecFindings. A non-nil error
// returns a nil slice and Snapshot leaves State.SpecFindings nil; on
// success a nil or empty slice both mean "no findings" (the invariant
// treats them identically).
SpecFindings(ctx context.Context) ([]*store.SpecFindingSnapshot, error)
// MaterializedChildrenJunction returns the persisted projection of every
// issue_materialized_children row joined against the child issue's
// proposals.outcome, ordered by (planning_key, child_key) for
// determinism. The materialized-children-proposal-currency invariant
// needs the WHOLE junction (it flags any row whose child's proposal is
// superseded), so — like Specs / SpecFindings — this returns the full
// slice in one read; Snapshot calls it once at snapshot-construction time
// and stores the result on State.MaterializedChildrenJunction. A non-nil
// error returns a nil slice and Snapshot leaves the field nil — the
// "reader unavailable" sentinel the invariant skips on (NOT "empty
// table"); a non-nil empty slice means the junction is genuinely empty,
// so no violation is possible.
MaterializedChildrenJunction(ctx context.Context) ([]*store.MaterializedChildJunctionEntry, error)
// NonTerminalIssueKeys returns the key of every non-terminal issuestore
// row (planning + implementation) at snapshot-construction time — the
// substrate the db-nonterminal-issue-present-in-memory invariant compares
// against State.Issues to detect a DB↔in-memory divergence (a non-terminal
// row absent from d.state). Like Specs / SpecFindings /
// MaterializedChildrenJunction this returns the whole set in one read;
// Snapshot calls it once and stores the result on
// State.PersistedNonTerminalIssueKeys. A non-nil error returns a nil slice
// and Snapshot leaves the field nil — the "reader unavailable" sentinel the
// invariant skips on rather than asserting against an undefined set; a
// non-nil empty slice means "loaded; no non-terminal rows", which yields no
// violations.
NonTerminalIssueKeys(ctx context.Context) ([]string, error)
// CorrectiveChildrenPerPlan returns a map from a CIPB planning issue key to
// the slice of non-terminal corrective-child keys the
// plan_add_children_minted events record for it — the same
// event-derived set the daemon's correctiveChildInFlight deferral guard
// reads. Like Specs / SpecFindings / MaterializedChildrenJunction /
// NonTerminalIssueKeys this returns the whole set in one read; Snapshot
// calls it once and stores the result on State.CorrectiveChildrenPerPlan. A
// non-nil error returns a nil map and Snapshot leaves the field nil — the
// "reader unavailable" sentinel the consuming predicate skips on rather than
// asserting against an undefined set; a non-nil empty map means "loaded; no
// corrective children in flight", which yields no violations. Semantics
// mirror NonTerminalIssueKeys.
CorrectiveChildrenPerPlan(ctx context.Context) (map[string][]string, error)
// ListHeldCapabilityGapBlocks returns the persisted projection of every
// held capability_gap_blocks row — one per plan parked in
// plan_capability_gap_parked, linking it to the gap issue that must DEPLOY
// before the plan can proceed — for the deadlock-liveness capability-gap
// extension (SPEC-SOR-2923-v1 R7/R8). Like Specs / SpecFindings /
// MaterializedChildrenJunction / NonTerminalIssueKeys /
// CorrectiveChildrenPerPlan this returns the whole set in one read; Snapshot
// calls it once at snapshot-construction time and stores the result on
// State.HeldCapabilityGapBlocks. A non-nil error returns a nil slice and
// Snapshot leaves the field nil — the "reader unavailable" sentinel the
// extension skips on rather than asserting against an undefined block set; a
// non-nil empty slice means "loaded; no held blocks", which yields no
// capability-gap edges or flags. Semantics mirror MaterializedChildrenJunction.
ListHeldCapabilityGapBlocks(ctx context.Context) ([]store.CapabilityGapBlock, error)
}
PersistedReader is the small read interface invariants use to fetch the persisted projection of an issue or session row. Implementations adapt the issuestore.Store's existing read API into the canonical *store.IssueSnapshot / *store.SessionSnapshot view; the daemon's wiring is the production implementation, test fakes are the in-package substitute.
All methods MUST be safe for concurrent use — the periodic sweep + the property-test wrapper may both hit the reader at once. ErrNotFound classification is left to the implementation; invariants treat a nil snapshot return paired with a non-nil error as "could not compare" and emit a warning violation (or skip) rather than asserting the daemon row is missing.
type PlanBranchProbeFunc ¶
PlanBranchProbeFunc resolves the live state of a plan branch on origin: given a repo slug ("owner/repo") and a branch name it returns the branch's current origin tip SHA, whether the ref exists on origin, and any probe error. The production implementation runs `git ls-remote origin refs/heads/<branch>` against the daemon-managed bare clone; a (sha=="", exists==false, err==nil) result means the ref is genuinely absent on origin, while a non-nil err means the probe itself failed (network / git error) and the caller MUST skip rather than treat the branch as missing. Implementations must be safe for concurrent use.
type RecordedPlanReviewerOutcomeProbeFunc ¶
RecordedPlanReviewerOutcomeProbeFunc resolves whether a plan_reviewer has already recorded an authoritative verdict in the issuestore proposal row: given the proposals.id of a plan_review planning issue it returns the recorded outcome ('approved' or 'rejected') and whether such a recorded outcome exists. The production implementation reads Store.GetProposal(ctx, proposalID) — the SAME reader tryHarvestRecordedPlanReviewerOutcome uses — so the invariant detects exactly the outcomes that harvest path would advance the plan on. A 'pending' or 'abandoned' proposal (no authoritative verdict to harvest) returns ("", false). The probe does the DB read at snapshot-construction time so the consuming invariant's Check stays I/O-free. Implementations must be safe for concurrent use. Mirrors DeliveredReviewerVerdictProbeFunc for the proposal-review (plan_reviewer) path.
type Severity ¶
type Severity int
Severity classifies how loud a violation should be on the events surface.
SeverityCritical fires when the in-memory daemon state has diverged from its persisted projection (the persist-before-apply class) or when a load-bearing chokepoint has been bypassed (the session-termination class). Any critical violation is a class-1 defect — the daemon's invariant promise has been broken and the next dispatch could act on the lie.
SeverityWarning fires when an invariant detects a recoverable shape (an in-flight transition window that resolves on the next tick, a liveness observation that the next sweep would converge) but the daemon hasn't yet been observed to act on the inconsistency. Warning violations log to the events surface but don't fire the recognizer's planning-issue auto-file path.
const ( // SeverityWarning is the lower-loudness severity for invariants that // detect a recoverable shape — observable but not yet acted upon. SeverityWarning Severity = iota // SeverityCritical is the highest severity — the daemon's invariant // promise has been broken. The recognizer subscribes to these and // auto-files a planning issue on first observation of a (name, // subjects) tuple within a sliding dedupe window. SeverityCritical )
type SnapshotInput ¶
type SnapshotInput struct {
Issues map[string]*sm.Issue
Sessions map[string]*sm.Session
Plan *store.PlanSnapshot
GitRefs map[string]string
// Persisted is the persisted-source reader Snapshot reads EAGERLY on the
// caller's (main) goroutine — both the whole-table eager reads (Specs /
// SpecFindings / MaterializedChildrenJunction / NonTerminalIssueKeys /
// CorrectiveChildrenPerPlan) and the per-LiveSubject persist-before-apply
// freeze (State.PersistedLiveIssueSnapshots). It is consumed entirely within
// the Snapshot call and is NOT retained on the returned *State, so the
// off-main inv.Check walk holds no live issuestore handle (R2/R5). Nil
// disables persistence comparisons (persist-before-apply skips its check in
// that case).
Persisted PersistedReader
// Ctx is the snapshot-construction context. Snapshot uses it for the
// one eager persisted read it performs — PersistedReader.Specs(ctx) to
// populate State.Specs. It is consumed entirely within the Snapshot
// call and never retained on the returned *State (the per-invariant
// Check calls receive their own context). Nil — together with a nil
// Persisted — disables the eager Specs read, leaving State.Specs nil;
// callers that don't exercise the spec-storage invariants (most tests,
// the property harness) leave it unset. Mirrors the existing
// PlanBranchProbe pattern of carrying a context-bearing input on
// SnapshotInput rather than threading it through the constructor
// signature.
Ctx context.Context
// BranchPrefix is forwarded verbatim onto State.BranchPrefix — the
// daemon's normalized config branch prefix used by the plan-branch
// invariants to derive a planning issue's plan branch name.
BranchPrefix string
// PlanBranchProbe is the raw plan-branch origin-state resolver. When
// non-nil, Snapshot wraps it in a per-snapshot memoizing cache and
// forwards the wrapped func onto State.PlanBranchProbe; nil disables
// the plan-branch invariants' check.
PlanBranchProbe PlanBranchProbeFunc
// BareClones is forwarded verbatim (copied) onto State.BareClones — the
// daemon-managed bare-clone paths the worktree-no-leaked-locked-ephemeral
// invariant iterates. Empty / nil disables that invariant's loop.
BareClones []string
// WorktreeListProbe is the raw locked-ephemeral-worktree lister. When
// non-nil, Snapshot wraps it in a per-snapshot, per-bare-clone memoizing
// cache and forwards the wrapped func onto State.WorktreeListProbe; nil
// disables the worktree-no-leaked-locked-ephemeral invariant's check.
WorktreeListProbe WorktreeListProbeFunc
// PerIssueWorktreeListProbe is the raw per-issue worktree lister. Snapshot
// forwards it VERBATIM onto State.PerIssueWorktreeListProbe (no memoization —
// it is a single-call, argument-free probe the invariant invokes once per
// Check); nil disables the per-issue-worktree-leaked invariant's check. Like
// WorktreeListProbe it is invoked lazily during the off-main inv.Check walk,
// so the daemon-side wiring captures the off-main walk context, not the
// recover-sweep ctx that is canceled before the walk runs.
PerIssueWorktreeListProbe PerIssueWorktreeListProbeFunc
// DeliveredReviewerVerdictProbe is the raw on-disk reviewer-verdict
// reader. When non-nil, Snapshot invokes it once per non-terminal
// review_judging issue carrying a live SessionID and records any delivered
// verdict's decision on State.DeliveredReviewerVerdicts; nil disables the
// reviewer-judging-with-delivered-verdict-progresses invariant's check. The
// probe does the disk I/O at snapshot-construction time so the consuming
// Check stays I/O-free — mirroring how Specs / SpecFindings are read
// eagerly rather than lazily from within a Check.
DeliveredReviewerVerdictProbe DeliveredReviewerVerdictProbeFunc
// RecordedPlanReviewerOutcomeProbe is the raw DB-backed plan_reviewer
// recorded-outcome reader. When non-nil, Snapshot invokes it once per
// non-terminal plan_review planning issue carrying a non-zero ProposalID
// and records any recorded outcome ('approved'/'rejected') on
// State.RecordedPlanReviewerOutcomes; nil disables the
// plan-review-recorded-verdict-progresses invariant's check. The probe
// does the DB I/O at snapshot-construction time so the consuming Check
// stays I/O-free — mirroring DeliveredReviewerVerdictProbe for the
// proposal-review (plan_reviewer) path.
RecordedPlanReviewerOutcomeProbe RecordedPlanReviewerOutcomeProbeFunc
// ActivationVerdictProbe is the raw per-plan activation-verdict evidence
// reader. When non-nil, Snapshot invokes it once per non-terminal
// plan_activating planning issue carrying a non-empty SpecID and records the
// per-RID evidence-present map on State.ActivationVerdictEvidences; nil
// disables the activation-probe-evidence-cited invariant's check. The probe
// does the DB I/O at snapshot-construction time so the consuming Check stays
// I/O-free — mirroring RecordedPlanReviewerOutcomeProbe for the plan_review
// path.
ActivationVerdictProbe ActivationVerdictProbeFunc
// LastPlanTransitionAtProbe is the per-issue plan-transition-history reader
// Snapshot invokes once per IN-PROGRESS spec-phase planning issue to populate
// IssueSnapshot.LastPlanTransitionAt — the re-based staleness reference for
// spec-phase-eventually-advances. When non-nil it is called EAGERLY at
// snapshot-construction time (the one place I/O is allowed, mirroring the
// recorded-/delivered-verdict probes) so the off-main Check stays I/O-free;
// it is scoped to spec-phase planning issues so the DB read cost is
// O(in-flight spec-phase plans), not O(issues). nil disables the re-basis —
// LastPlanTransitionAt stays 0 and the invariant falls back to UpdatedAt alone
// (the existing violation corpus / migration-safety path).
LastPlanTransitionAtProbe LastPlanTransitionAtProbeFunc
// BufferedVerdictReplayActivityProbe is the per-issue reviewer_verdict_replayed
// event reader Snapshot invokes once per review_judging issue carrying a
// non-empty PendingReviewerVerdict to populate
// State.BufferedVerdictReplayActivity — the replay-activity staleness reference
// for reviewer-judging-with-buffered-verdict-progresses. When non-nil it is
// called EAGERLY at snapshot-construction time (the one place I/O is allowed,
// mirroring the recorded-/delivered-verdict probes) so the off-main Check stays
// I/O-free; it is scoped to buffered review_judging issues so the DB read cost
// is O(buffered-verdict holders), not O(issues). nil disables the re-basis —
// BufferedVerdictReplayActivity stays nil and the invariant flags every aged
// buffer (the existing violation corpus / migration-safety path).
BufferedVerdictReplayActivityProbe BufferedVerdictReplayActivityProbeFunc
// Now is the sweep moment forwarded verbatim onto State.Now — the
// reference timestamp the in-flight-dedup-bounded invariant compares
// against each issue's persisted in-flight marker. Zero disables that
// invariant's staleness check.
Now time.Time
// InFlightDedupCapSeconds is forwarded verbatim onto
// State.InFlightDedupCapSeconds — the staleness cap (seconds) the
// in-flight-dedup-bounded invariant applies. Non-positive disables the
// check.
InFlightDedupCapSeconds int64
// SameStateCycleThreshold is forwarded verbatim onto
// State.SameStateCycleThreshold — the sustained-failure backstop threshold
// the human-block-reason-denylist-or-backstop-exhausted invariant compares a
// blocked issue's SameStateCycleCount against. Non-positive leaves the
// invariant to fall back to its own default rather than disabling the check.
SameStateCycleThreshold int
// RequireOperatorApproval is forwarded verbatim onto
// State.RequireOperatorApproval — the daemon's require_operator_approval
// config signal the spec-review-open-finding-parked invariant gates on (a
// settled spec phase under operator-approval is the sanctioned rest, not a
// park). Populated at the daemon sweep site from d.cfg.RequireOperatorApproval;
// false (the default) leaves the invariant active. Mirrors the
// SubmitResultContracts injection precedent — a forwarded field with a
// real-config-backed population site.
RequireOperatorApproval bool
// SubmitResultContracts overrides the registry the
// success-class-yields-typed-result invariant reads. Nil (the default) causes
// Snapshot to fall back to agentcli.SubmitResultContracts (the real
// global). Set in tests to inject a violation corpus (a contract with a nil
// Validate / empty RequiredMarker) or to drive the healthy real registry
// through the Snapshot population path. Mirrors the BareClones forwarding
// shape — a slice field copied verbatim onto State.
SubmitResultContracts []agentcli.SubmitResultContract
// ArtifactVerifyConfigured + ArtifactVerifiedSHAs are the comparable facts the
// generated-artifact-regenerated-at-seam drift invariant needs, precomputed at
// snapshot-construction time so Check reads only the per-issue
// IssueSnapshot.ArtifactDrifted boolean (the IsNonCanonical precedent — no
// generator run in Check). ArtifactVerifyConfigured is true when the project
// declares ≥1 generated artifact with a VerifyArgv (daemon Config
// ArtifactVerifyConfigured); false leaves ArtifactDrifted false on every issue
// (the invariant stays silent for projects with no declared verify).
// ArtifactVerifiedSHAs maps planning-issue key → repo slug → the last
// verified plan-branch tip SHA (daemon State.ArtifactVerifiedSHAs), read only
// during Snapshot to compute ArtifactDrifted; it is NOT retained on State.
ArtifactVerifyConfigured bool
ArtifactVerifiedSHAs map[string]map[string]string
// HeldDiscoveringIssueKeys is the key set of the daemon's in-memory
// discoveringFootprintHeld registry, snapshotted on the main loop before
// Snapshot is called (single-writer-safe, alongside the d.state.Issues read).
// Snapshot defensively deep-copies it onto State.HeldDiscoveringIssueKeys for
// the held-discovering-record-consistent keystone invariant; the off-main
// inv.Check walk reads only the baked-in immutable copy, never the live
// registry. Nil disables the keystone's check (the "registry not wired"
// sentinel). Mirrors the BareClones forwarding shape — a map field copied
// verbatim onto State.
HeldDiscoveringIssueKeys map[string]bool
// GateGovernorStat is the daemon-global gate-concurrency governor's live
// occupancy probe — a nil-safe closure returning (running, capacity). When
// non-nil, Snapshot invokes it ONCE on the main goroutine (the one place
// I/O is allowed — the governor's Stat() is an atomic read) and records the
// two ints on State.GateRunning / State.GateCapacity so the off-main
// gate-concurrency-within-capacity Check stays I/O-free. nil (the default,
// every test substrate and the not-yet-wired window) leaves both ints zero
// — the "no governor wired" sentinel the invariant skips on. Forwarded from
// store.State.GateGovernorStat at the daemon sweep site.
GateGovernorStat func() (running, capacity int)
// GateHarnessTimeout is the daemon-authoritative effective harness timeout
// parsed from the project's config override (config.ProjectGateHarnessTimeout),
// forwarded verbatim onto State for the gate-harness-timeout-below-budget
// invariant. Zero means "no explicit config override; the gate script's own
// fallback applies" — the invariant skips on zero.
GateHarnessTimeout time.Duration
// GateSubprocessBudget is the daemon's pre-push gate subprocess SIGKILL budget
// (DefaultPrePushGateTimeout), forwarded verbatim onto State for the
// gate-harness-timeout-below-budget invariant. Zero means "not wired" — the
// invariant skips on zero.
GateSubprocessBudget time.Duration
}
SnapshotInput is the read-side handle the daemon passes to Snapshot — the live *sm.Issue / *sm.Session maps under daemon.State, plus any already-resolved git refs and the persisted-source reader. The daemon's caller acquires d.stateMu.RLock() before constructing this struct and releases the lock as soon as Snapshot returns; the deep copy inside Snapshot is what makes the returned *State safe to outlive the lock.
Issues / Sessions / GitRefs are read-only references — Snapshot walks the maps but does not mutate them. Plan is the optional per-planning aggregate (nil when no planning issue is in scope on this tick).
type State ¶
type State struct {
Issues map[string]*store.IssueSnapshot
LiveSubjects map[string]*store.IssueSnapshot
Sessions map[string]*store.SessionSnapshot
Plan *store.PlanSnapshot
GitRefs map[string]string
BranchPrefix string
PlanBranchProbe PlanBranchProbeFunc
// BareClones is the list of daemon-managed bare-clone paths the
// worktree-no-leaked-locked-ephemeral invariant probes
// (<projectRoot>/.sorcerer/repos/*.git). Populated by the daemon's
// invariants-check sweep; empty / nil in test substrates that don't run
// git (the invariant's nil-probe guard skips them anyway).
BareClones []string
// WorktreeListProbe enumerates the locked ephemeral worktrees in one
// bare clone for the worktree-no-leaked-locked-ephemeral invariant. Nil
// when the caller did not wire a probe (test substrates that don't run
// git) — the consuming invariant skips rather than assert. The snapshot
// constructor wraps the caller-supplied probe in a per-snapshot,
// per-bare-clone memoizing cache so the `git worktree list` cost is paid
// at most once per clone across the whole sweep, mirroring
// PlanBranchProbe.
WorktreeListProbe WorktreeListProbeFunc
// PerIssueWorktreeListProbe enumerates every top-level per-issue worktree
// wrapper under <projectRoot>/.sorcerer/worktrees/ for the
// per-issue-worktree-leaked invariant. Nil when the caller did not wire a
// probe (test substrates that don't run filesystem I/O) — the consuming
// invariant skips rather than assert. Unlike WorktreeListProbe it is NOT
// memoized: it is a single-call, argument-free lister invoked once per Check,
// so there is no per-key cost to amortize.
PerIssueWorktreeListProbe PerIssueWorktreeListProbeFunc
// TerminalIssueKeys is the set of issue/plan keys that were terminal
// (reapable) at snapshot-construction time — collected by Snapshot from the
// terminal issues it drops from Issues/LiveSubjects. The
// per-issue-worktree-leaked invariant consults it to tell a terminal-owner
// worktree (flaggable) apart from an unrecognized basename (preserved by the
// conservative default): a probed wrapper whose basename is absent from BOTH
// LiveSubjects and TerminalIssueKeys is unrecognized and never flagged. Nil /
// empty in test substrates that don't wire terminal issues — every wrapper is
// then treated as unrecognized (skipped), so the invariant stays silent.
TerminalIssueKeys map[string]bool
// TerminalFailIssueKeys is the FAILURE subset of TerminalIssueKeys — the
// keys that were terminal-FAIL (abandoned/rejected for an impl issue,
// plan_abandoned for a planning issue, per sm.IsTerminalFailureState) at
// snapshot-construction time. The deadlock-liveness capability-gap extension
// (SPEC-SOR-2923-v1 R8) consults it to flag a parked plan whose held gap
// issue is terminal-fail: such a gap can never deploy, so it permanently
// strands the plan, yet — being terminal — it is absent from State.Issues
// and the waits-for graph, so the cycle walk cannot catch it. Populated in
// the same Snapshot terminal-issue drop loop as TerminalIssueKeys; nil (lazy
// init) means "no terminal-fail issues seen", which the extension treats as
// "no abandoned gap to flag".
TerminalFailIssueKeys map[string]bool
// DeliveredReviewerVerdicts maps a review_judging issue key to the
// decision string of a complete, schema-valid reviewer verdict found on
// disk in that issue's live reviewer-session state dir — a verdict that
// has been DELIVERED but is not yet reflected in the in-memory buffer
// (PendingReviewerVerdict). Populated eagerly by Snapshot via
// DeliveredReviewerVerdictProbe for every review_judging issue carrying a
// non-empty SessionID; the
// reviewer-judging-with-delivered-verdict-progresses invariant consumes it
// with NO further I/O in Check (the probe did the disk read at snapshot
// time). A key is present only when the probe reported a delivered verdict;
// an in-flight reviewer that has not yet written review.json contributes no
// entry. Nil when no probe is wired (test substrates that don't run disk
// I/O) — the invariant then finds nothing and skips, the same nil-as-skip
// sentinel the other probe-backed invariants use.
DeliveredReviewerVerdicts map[string]string
// BufferedVerdictReplayActivity maps a review_judging issue key to the
// unix-seconds timestamp of the most-recent reviewer_verdict_replayed event
// for that issue — the progress signal replayPendingReviewerVerdict emits on
// every github-prs poll that replays the buffered verdict against terminal
// CI. Populated eagerly by Snapshot via BufferedVerdictReplayActivityProbe
// for every review_judging issue carrying a non-empty PendingReviewerVerdict;
// the reviewer-judging-with-buffered-verdict-progresses invariant consumes it
// with NO further I/O in Check (the probe did the DB read at snapshot time).
// A key is present only when the probe reported a replay event; a buffer the
// poller has never replayed (the SOR-2628 poller-omission wedge) contributes
// no entry, so the invariant still flags it. Nil when no probe is wired (test
// substrates that don't run DB I/O, the property harness) — the invariant
// then sees no replay activity and flags every aged buffer, the same
// nil-as-no-replay sentinel the vacuity corpus relies on.
BufferedVerdictReplayActivity map[string]int64
// RecordedPlanReviewerOutcomes maps a plan_review planning issue key to
// the recorded plan_reviewer outcome ('approved' or 'rejected') found in
// that issue's issuestore proposals row at snapshot-construction time — a
// verdict the plan_reviewer already recorded via `sorcererd plan review`
// but which the daemon has not yet harvested to advance the plan out of
// plan_review. Populated eagerly by Snapshot via
// RecordedPlanReviewerOutcomeProbe for every plan_review planning issue
// carrying a non-zero ProposalID; the
// plan-review-recorded-verdict-progresses invariant consumes it with NO
// further I/O in Check (the probe did the DB read at snapshot time). A key
// is present only when the probe reported a recorded outcome; a
// plan_reviewer that has not yet recorded a verdict (proposal outcome
// 'pending') contributes no entry and the invariant skips the issue. Nil
// when no probe is wired (test substrates that don't run DB I/O) — the
// invariant then finds nothing and skips, the same nil-as-skip sentinel
// the other probe-backed invariants use. Mirrors DeliveredReviewerVerdicts
// for the proposal-review (plan_reviewer) path.
RecordedPlanReviewerOutcomes map[string]string
// ActivationVerdictEvidences maps a plan_activating planning issue key to the
// per-RID evidence-present boolean from its latest activation verdict row. A
// key is present when the probe returned a non-nil map (at least one verdict
// exists); absent means "no verdict row yet" — the
// activation-probe-evidence-cited invariant skips the plan. Within a present
// entry, an RID maps to whether that requirement's recorded activation verdict
// carries a non-nil evidence pointer (false ⇒ recorded but un-evidenced, the
// wedge the invariant flags). Nil when no ActivationVerdictProbe is wired or no
// plan_activating plans with a SpecID are in scope. Populated eagerly by
// Snapshot via ActivationVerdictProbe so Check stays I/O-free — mirroring
// RecordedPlanReviewerOutcomes for the activation-verdict path.
ActivationVerdictEvidences map[string]map[string]bool
// Specs is the persisted projection of the specs table keyed by
// specs.id — the substrate the two spec-storage invariants walk
// (spec-version-lineage-integrity over the previous_version_id chains,
// issues-spec-id-referential-integrity over the issues.spec_id links).
// Populated eagerly by Snapshot from PersistedReader.Specs(ctx) when a
// reader and a context are wired; left nil otherwise. The nil-vs-empty
// distinction is load-bearing for the referential-integrity invariant:
// a nil map means "the persisted spec source was unavailable — skip
// rather than report every issues.spec_id as dangling", while a non-nil
// empty map means "loaded; the table is genuinely empty, so any
// issues.spec_id reference is a real dangling reference".
Specs map[string]*store.SpecSnapshot
// SpecFindings is the persisted projection of the spec_findings table —
// a flat slice of every finding row, the substrate the
// spec-approved-no-open-error-findings invariant walks (it resolves a
// spec_approved planning issue's spec_id against the finding set). Each
// snapshot copies the small fields (id, spec_id, kind, severity, status,
// requirement_ids) and skips the heavy witness_json / resolution_note
// blobs. Populated eagerly by Snapshot from
// PersistedReader.SpecFindings(ctx) when a reader and a context are
// wired; left nil otherwise. Unlike State.Specs, the nil-vs-empty
// distinction is NOT load-bearing here: nil (reader unavailable) and a
// non-nil empty slice (table genuinely empty) both mean "no findings to
// flag", so the invariant produces zero violations either way.
SpecFindings []*store.SpecFindingSnapshot
// MaterializedChildrenJunction is the persisted projection of the
// issue_materialized_children table joined with the child issue's
// proposals.outcome. The materialized-children-proposal-currency
// invariant walks this slice to flag any junction row whose child's
// proposal is superseded — the row the SupersedeProposalTx chokepoint
// should have cascade-deleted. Nil means the read failed / reader
// unavailable; the invariant skips (same nil-vs-empty distinction as
// State.Specs — a non-nil empty slice means "loaded, junction empty",
// so no violation is possible).
MaterializedChildrenJunction []*store.MaterializedChildJunctionEntry
// HeldCapabilityGapBlocks is the persisted projection of the
// capability_gap_blocks table — one block record per currently-parked plan
// (a plan resting in plan_capability_gap_parked), linking it to the
// daemon-self-improvement gap issue that must DEPLOY before the plan can
// proceed (SPEC-SOR-2923-v1 R7/R8). The deadlock-liveness capability-gap
// extension walks this slice to inject a waits-for edge from each parked plan
// to its non-deployed gap issue (R7 cycle detection) and to flag a plan whose
// gap issue is abandoned (R8). Populated eagerly by Snapshot from
// PersistedReader.ListHeldCapabilityGapBlocks(ctx) when a reader and a context
// are wired; left nil otherwise. Like State.MaterializedChildrenJunction the
// nil-vs-empty distinction is load-bearing: nil means "the persisted source
// was unavailable — skip" (the extension's len(s.HeldCapabilityGapBlocks) > 0
// fast-path), while a non-nil empty slice means "loaded; no held blocks",
// which also yields no capability-gap edges or flags.
HeldCapabilityGapBlocks []store.CapabilityGapBlock
// PersistedNonTerminalIssueKeys is the key of every non-terminal
// issuestore row at snapshot-construction time — the substrate the
// db-nonterminal-issue-present-in-memory invariant walks to flag a
// DB↔in-memory divergence (a non-terminal issuestore row absent from
// State.Issues, i.e. d.state). Populated eagerly by Snapshot from
// PersistedReader.NonTerminalIssueKeys(ctx) when a reader and a context
// are wired; left nil otherwise. Like State.MaterializedChildrenJunction
// the nil-vs-empty distinction is load-bearing: nil means "the persisted
// source was unavailable — skip" (the invariant returns no violations),
// while a non-nil empty slice means "loaded; no non-terminal rows", which
// also yields no violations but for the opposite reason.
PersistedNonTerminalIssueKeys []string
// PersistedLiveIssueSnapshots is the persisted projection of every
// LiveSubject — keyed by issue key, paired with any read error — captured
// EAGERLY at snapshot-construction time (t0) on the main loop. It is the
// FROZEN substrate the persist-before-apply invariant compares each
// in-memory projection against, so the off-main inv.Check walk computes its
// decision solely from a snapshot captured on the main loop (R5) and the
// returned State holds NO live issuestore handle (R2). Capturing it here —
// alongside the in-memory LiveSubjects copy, while the single writer is
// blocked in Snapshot — is what keeps the off-main decision identical to the
// equivalent inline computation: a live per-key read from within Check would
// instead race the freed main loop's concurrent persist+apply transitions
// and, for any LiveSubject that transitions during the seconds-long,
// git-probe-paced off-main walk window, emit a false-positive critical
// divergence. Mirrors the eager Specs / SpecFindings / NonTerminalIssueKeys
// reads. Nil when no PersistedReader was wired — the "no persisted source"
// sentinel persist-before-apply skips on (replacing its prior
// State.Persisted == nil guard).
PersistedLiveIssueSnapshots map[string]PersistedIssueFreeze
// CorrectiveChildrenPerPlan maps a CIPB planning issue key to the slice of
// non-terminal corrective-child keys recorded by plan_add_children_minted
// events — the same event-derived set the daemon's correctiveChildInFlight
// deferral guard reads. Populated eagerly by Snapshot from
// PersistedReader.CorrectiveChildrenPerPlan(ctx) when a reader and context
// are wired; nil means "reader unavailable — skip". Mirrors the
// PersistedNonTerminalIssueKeys / MaterializedChildrenJunction projections;
// the fix-pending-quiescence property predicate is its sole consumer.
CorrectiveChildrenPerPlan map[string][]string
// Now is the sweep moment, forwarded from SnapshotInput.Now. The
// in-flight-dedup-bounded invariant compares it against each issue's
// persisted in-flight marker to decide staleness; a zero value (the
// caller did not wire a clock — most test substrates and the property
// harness) disables that invariant's check rather than asserting against
// an undefined reference.
Now time.Time
// InFlightDedupCapSeconds is the staleness cap (in seconds) the
// in-flight-dedup-bounded invariant applies: a non-zero in-flight marker
// older than this is a violation. Forwarded from SnapshotInput; a
// non-positive value disables that invariant's check (no cap wired). The
// daemon's runInvariantsCheck supplies 2 × planMainMergeRepoTimeout by
// default (configurable via plan_branch.in_flight_dedup_cap_seconds).
InFlightDedupCapSeconds int64
// SameStateCycleThreshold is the configured sustained-failure backstop
// threshold — the per-issue cap on consecutive same-state/same-reason
// returns before the deterministic backstop force-routes the issue to a
// human-block state (daemon.sameStateCycleThreshold(), default 3). The
// human-block-reason-denylist-or-backstop-exhausted invariant compares each
// blocked issue's SameStateCycleCount against it to tell a legitimate
// backstop-exhausted block apart from a routable-cause regression. Forwarded
// verbatim from SnapshotInput; a non-positive value (most test substrates
// that don't wire the knob) makes the invariant fall back to its own default
// rather than disabling the check — a within-budget routable-cause block is
// a regression regardless of whether the daemon's exact threshold was wired.
SameStateCycleThreshold int
// RequireOperatorApproval mirrors the daemon's config
// require_operator_approval knob (d.cfg.RequireOperatorApproval). When true,
// every settled pre-approval spec phase is the SANCTIONED
// awaiting-operator-selection rest rather than a park, so the
// spec-review-open-finding-parked invariant emits no violation. Forwarded
// verbatim from SnapshotInput; false (the autonomous-approval default, and
// most test substrates) leaves the invariant active. Additive — no existing
// State consumer is exhaustive over the field set, mirroring the
// SubmitResultContracts addition.
RequireOperatorApproval bool
// SubmitResultContracts is the registry slice the
// success-class-yields-typed-result invariant walks to verify each submitting
// contract has a wired typed-result extraction path — a non-nil Validate
// boundary AND a non-empty RequiredMarker — so a success-class submitting
// dispatch can never fail to yield a non-nil typed result of its dispatched
// phase's type (spec SPEC-SOR-2785 R4). Typed results are ephemeral, but the
// wiring that makes one extractable is verifiable structurally over this
// registry. Populated by Snapshot from SnapshotInput.SubmitResultContracts
// (test override) or agentcli.SubmitResultContracts (the real global). A
// nil / empty value disables the check — the "no registry wired" sentinel.
SubmitResultContracts []agentcli.SubmitResultContract
// HeldDiscoveringIssueKeys is the key set of the daemon's in-memory
// discoveringFootprintHeld registry — every issue whose discovering →
// implementing write transition is held at the cross-impl footprint gate —
// snapshotted at sweep time. The held-discovering-record-consistent keystone
// invariant walks it to assert every held issue carries a complete durable
// hold record, is present in d.state.Issues, and still anchor-conflicts an
// in-flight-cohort peer. Populated by Snapshot from
// SnapshotInput.HeldDiscoveringIssueKeys (a defensive deep copy, the
// BareClones forwarding shape). NIL is the load-bearing "registry not wired"
// sentinel the invariant skips on (every property-test substrate and the
// unit-test constructors that don't populate it); a non-nil empty map means
// "wired; no holds", which also yields zero violations.
HeldDiscoveringIssueKeys map[string]bool
// GateRunning and GateCapacity are the daemon-global gate-concurrency
// governor's live occupancy at snapshot-construction time: the count of
// running gate executions and the configured slot budget K. Populated in
// Snapshot from SnapshotInput.GateGovernorStat (the governor's atomic
// Stat() read, done on the main goroutine where I/O is allowed) so the
// off-main gate-concurrency-within-capacity Check reads only the two plain
// ints, never the live governor. Both zero is the "no governor wired"
// sentinel (a nil GateGovernorStat leaves them at their zero default) — the
// invariant skips when GateCapacity == 0.
GateRunning int
GateCapacity int
// GateHarnessTimeout and GateSubprocessBudget are the two daemon-authoritative
// durations the gate-harness-timeout-below-budget invariant orders (spec
// SPEC-SOR-3074-v1 R5): the effective harness timeout (the deadline passed
// INTO the test harness, parsed from config.ProjectGateHarnessTimeout) and the
// gate-subprocess SIGKILL budget (the daemon binary constant
// DefaultPrePushGateTimeout). Both are forwarded verbatim from SnapshotInput
// (static config values — no probe). Either zero is the "not configured / not
// wired" sentinel the invariant skips on.
GateHarnessTimeout time.Duration
GateSubprocessBudget time.Duration
}
State is a goroutine-exclusive deep copy of the daemon's in-memory issue + session map plus the resolved git refs and the planning-issue aggregate at the moment the invariants sweep ran. The snapshot is the substrate every Invariant.Check call walks.
Field shape mirrors docs/structural-coverage/phase-1-invariant-manifest.md § 4.2:
- Issues / Sessions hold deep-copy *store.IssueSnapshot / *store.SessionSnapshot values projected from the daemon's *sm.Issue / *sm.Session maps. Both maps are non-nil after Snapshot returns; an empty map means "no issues / no sessions to check". Issues is scoped to the non-terminal issues — terminal issues (frozen history with no outgoing SM edges) are NOT fed to the sweep, so the sweep stays O(non-terminal) as the issuestore grows.
- LiveSubjects is the candidate subject set every invariant iterates. From the production Snapshot path it holds the same entries as Issues. The two facets stay distinct fields because callers that hand-construct a *State{Issues: ...} or hand-mutate s.Issues may place terminal entries in Issues; RebuildLiveSubjects then derives the non-terminal LiveSubjects from them. Invariants that resolve a parent/peer key by lookup read Issues, and the frozen-subject backstop (SuppressFrozenSubjectViolations) reads Issues to drop any violation reported against a terminal entry.
- Plan carries the per-planning-issue aggregate (PlanState + identity) the planning-touching invariants compare; nil when no planning issue is in scope.
- GitRefs maps worktree paths to resolved HEAD commit SHAs at the moment the snapshot was taken; populated by daemon-side wiring that calls Snapshot after probing git. Empty when the caller did not supply git refs.
- PersistedLiveIssueSnapshots is the persisted projection of every LiveSubject FROZEN at snapshot-construction time (t0) on the main loop — the substrate the persist-before-apply invariant compares each in-memory projection against. Snapshot reads it eagerly from the supplied PersistedReader and the returned State carries NO live reader, so the off-main inv.Check walk holds no live issuestore handle (R2/R5). Nil disables persistence comparisons — persist-before-apply treats a nil map as "no persisted source available" and skips.
- BranchPrefix is the daemon's normalized config branch prefix (e.g. "sorcerer/"). The plan-branch invariants derive a planning issue's canonical plan branch name from it; empty disables the derivation (the plan-branch invariants skip when they cannot name the branch).
- PlanBranchProbe resolves a plan branch's live origin state for the plan-branch existence + tip-consistency invariants. Nil when the caller did not wire a probe (test substrates that don't run git) — the consuming invariants skip rather than assert. The snapshot constructor wraps the caller-supplied probe in a per-snapshot memoizing cache so the O(plans × repos) ls-remote cost is paid at most once per (repo, branch) across the whole sweep.
func Snapshot ¶
func Snapshot(in SnapshotInput) *State
Snapshot deep-copies the supplied input into a goroutine-exclusive *State suitable for handing to every registered Invariant.Check on a single sweep tick.
Scoping: the resulting State.Issues is the non-terminal subset of the supplied input, not a 1:1 projection. Snapshot walks in.Issues once, copying every non-terminal issue into both Issues and LiveSubjects; terminal issues (frozen history with no outgoing SM edges) are dropped so the sweep stays O(non-terminal) as the issuestore grows. Issues and LiveSubjects therefore carry the same entries from this path; they remain distinct fields so a caller that hand-builds a *State or mutates s.Issues directly can re-derive LiveSubjects via RebuildLiveSubjects, and so the frozen-subject backstop has an Issues map to consult.
Goroutine-safety contract: the caller MUST hold whatever read lock guards the supplied Issues / Sessions maps for the duration of the Snapshot call; Snapshot itself walks both maps and does not take any additional lock. The returned *State no longer aliases the caller's maps — every issue / session / slice has been re-allocated into the snapshot, so the caller may release its read lock as soon as Snapshot returns.
Plan and GitRefs are reference-forwarded: PlanSnapshot is an immutable value (no slice fields) and GitRefs is copied into a fresh map so the snapshot owns its keys/values.
Safe to call from any goroutine — the snapshot makes no daemon-state mutation and does not assume single-writer ownership of the supplied maps; the lock the caller holds is the cross-goroutine boundary.
type SubjectCompatibilityChecker ¶
type SubjectCompatibilityChecker interface {
SubjectIsCompatible(issueState, issueKind string) bool
}
SubjectCompatibilityChecker is an OPTIONAL interface a runtime Invariant MAY implement to declare which issue state/kind combinations are valid subjects for its cohort — "what shape of issue could this invariant legitimately fire against?". It exists so the recognizer's audit-filing premise-check (the file_new_issue apply arm) can reject a doomed audit planning issue whose cited subject resolves locally but is in a state the invariant could never have flagged — the SOR-2438 cross-daemon-citation failure class, where a per-daemon SOR key from another daemon resolved to an unrelated local issue.
The single argument the invariant inspects is the issue's persisted state string. For a planning issue that string holds the PlanState (the issuestore stores a planning row's plan-state in its State column, discriminated by StateKind == "plan"; see internal/daemon/mirror.go's smIssueToStoreRow); for an implementation issue it holds the top-level IssueState. issueKind ("planning" | "implementation") lets the invariant gate on the kind before interpreting the state, so a spec-phase invariant can require kind == "planning" AND state ∈ its in-progress spec phases.
Like TransientTolerant, the interface is deliberately NOT part of the Invariant contract: it is resolved by a type assertion against the registered invariant (see InvariantSubjectIsCompatible). An invariant that does not implement it is treated as "no opinion" — the premise-check passes the subject (compatibility is checked=false), so the change is purely additive and no existing invariant needs updating.
type TransientTolerant ¶
type TransientTolerant interface {
TransientTolerant() bool
}
TransientTolerant is an OPTIONAL interface a runtime Invariant MAY implement to declare that its violations include benign, by-design transient windows — states that the next scheduler tick resolves and that are NOT, on their own, defects. An invariant implementing it and returning true is asserting "a single mid-window violation is expected in steady state; only an anomalous RATE of distinct subjects signals a real regression."
The interface is deliberately NOT part of the Invariant contract. It is resolved by a type assertion against the registered invariant (see IsTransientTolerant), so the other registered runtime invariants and the static-analyzer shims need no change — they simply do not implement it and are treated as defect invariants (every violation is a true defect). Five invariants implement it today: `implementer-attached-state-has-live-session` (flags three benign next-tick-resolves windows for implementer session attachment), `plan-branch-tip-consistency` (flags the benign push-landed-on-origin / result-event-not-yet-applied window), `prop-test-stub-present` (flags the benign post-transition dispatch window where a generated stub is transiently absent), `spec-requirement-matrix-complete` (flags the benign mid-cycle window where the implementer's incrementally-populated requirement trace does not yet cover every declared R-ID), and `write-state-footprint-mutual-exclusion` (flags the benign post-IMPLEMENT_OK verify-chain window, Force=true write-state re-entries, and unpredicted mid-implementation footprint growth). A future transient-tolerant invariant opts in with one method.
The single consumer is the recognizer's reactive invariant-violation rate-gate: for a transient-tolerant invariant it files a planning issue only when the distinct-subject count within a rate window crosses a threshold, instead of on first observation. The classification lives on the invariant — the single source of truth, co-located with the declaring file that documents the transient windows — rather than in a recognizer-side name set that would drift from the invariant's actual semantics.
type Violation ¶
Violation is one finding from an Invariant.Check call. Invariants emit zero or more violations against a State snapshot; the empty slice means "the invariant holds on this snapshot".
Fields:
- Invariant: the canonical name of the emitting invariant; matches Invariant.Name() so a consumer reading off the events surface can route the violation back to its declaring file.
- Subjects: the load-bearing identity tuple the violation touches. Typically one or more issue keys; for session-touching invariants the slice includes both the issue key and the session id; for repo-touching invariants the slice includes the repo slug. The first entry is the primary subject — the events-surface row's `subject` column reads from index 0 so violations dedupe cleanly in the events table.
- Detail: human-readable explanation of what the invariant observed. Surfaced verbatim in the events row's `message` column and in any recognizer-filed planning issue's body, so the prose must be safe to publish (no secrets, no internal identifiers other than issue keys / session ids / repo slugs).
- Severity: the loudness classification (see Severity comment).
func SuppressFrozenSubjectViolations ¶
SuppressFrozenSubjectViolations drops every Violation whose primary subject (Subjects[0]) resolves to a terminal/frozen issue in the snapshot. The periodic invariants sweep applies this as a structural backstop: a terminal issue (merged / abandoned / rejected / canceled / plan_completed / plan_abandoned) is immutable, so it cannot drift — any "violation" reported against one is a false positive (typically an invariant whose own terminal-state handling is imperfect, applied over the entire issuestore history).
This is belt-and-suspenders over each invariant's own terminal-skip: one place that makes the whole sweep flood-proof against history, independent of any individual invariant's correctness. It is the fix for the failure mode where running the checks over the full issuestore history produces a flood of false positives on long-dead issues.
Violations whose primary subject is NOT a known issue key in the snapshot (e.g. a session id, a repo slug, a file:line from a static analyzer) are KEPT — only confirmed-terminal ISSUES are suppressed, so the filter never hides a violation it cannot positively classify as frozen. A nil snapshot or empty input is returned unchanged.
type WorktreeListProbeFunc ¶
type WorktreeListProbeFunc func(bareClone string) ([]LockedEphemeralWorktree, error)
WorktreeListProbeFunc, given a daemon-managed bare-clone path, returns the locked ephemeral worktrees registered in that clone, or an error if the git I/O fails. The production implementation runs `git worktree list --porcelain` and stats each ephemeral temp-parent for the age; a non-nil err means the probe itself failed (the consuming invariant skips that bare clone rather than treating it as leak-free). Implementations must be safe for concurrent use — the snapshot constructor wraps a non-nil probe in a per-bare-clone memoizing cache so the `git worktree list` cost is paid at most once per clone across the sweep, mirroring PlanBranchProbeFunc's memoization.
Source Files
¶
- activation_probe_evidence_cited.go
- annotation.go
- at_most_one_nonterminal_session_per_issue_family.go
- cipb_child_feedback_has_substantive_concern.go
- cipb_shipped_plan_eventually_completes.go
- cipb_stranded_finalization_merging.go
- completed_plan_merge_latency_bounded.go
- coverage.go
- cross_plan_file_footprint_closure.go
- db_nonterminal_issue_present_in_memory.go
- deadlock_liveness.go
- dependency_graph_acyclic.go
- discovering_empty_discovery_markdown.go
- ephemeral_worktree_leaked.go
- frozen_subject_filter.go
- gate_concurrency_within_capacity.go
- gate_harness_timeout_below_budget.go
- generated_artifact_regenerated_at_seam.go
- held_discovering_record_consistent.go
- held_waiting_leaf_dispatch_starvation.go
- human_block_reason_denylist_or_backstop_exhausted.go
- impl_write_state_dwell_bounded.go
- implementer_attached_state_has_live_session.go
- in_flight_dedup_bounded.go
- in_review_clears_review_discovery_markdown.go
- invariant.go
- issues_spec_id_referential_integrity.go
- materialized_children_proposal_currency.go
- matrix_path_not_in_footprint.go
- merge_safety_combination_verified.go
- per_issue_worktree_leaked.go
- persist_before_apply.go
- plan_activating_criteria_present.go
- plan_activating_eventually_completes.go
- plan_activation_eventually_resolves.go
- plan_auto_fixing_session.go
- plan_branch_existence.go
- plan_branch_tip_consistency.go
- plan_pr_fix_dispatched_session.go
- plan_pr_ready_flip_progress.go
- plan_review_recorded_verdict_progresses.go
- planning_issue_owns_resolved_spec.go
- pre_completion_main_merge_liveness_bounded.go
- prop_test_stub_present.go
- ready_leaf_dispatch_starvation.go
- registry.go
- requirement_executable_or_justified.go
- review_judging_requires_review_discovery_markdown.go
- reviewer_judging_with_buffered_verdict_progresses.go
- reviewer_judging_with_delivered_verdict_progresses.go
- session_required_no_session_no_durable_hold.go
- session_termination.go
- spec_approved_no_open_error_findings.go
- spec_canonical_roundtrip.go
- spec_drafter_single_active_session.go
- spec_phase_eventually_advances.go
- spec_requirement_matrix_complete.go
- spec_review_judge_advance_bounded.go
- spec_review_open_finding_parked.go
- spec_state_spec_id_coupling.go
- spec_version_lineage_integrity.go
- stale_child_verifying.go
- state.go
- stub_path_not_in_footprint.go
- success_class_yields_typed_result.go
- terminal_plan_orphan_children.go
- write_state_footprint_known.go
- write_state_footprint_mutual_exclusion.go
Directories
¶
| Path | Synopsis |
|---|---|
|
activationresolveproptest
|
|
|
gen
Package gen is the operator-supplied generator-helper package backing the committed prop_test stub (the codegen-owned root proptest_stubs_test.go) for SOR-2508 R13 — the activation-permanence requirement.
|
Package gen is the operator-supplied generator-helper package backing the committed prop_test stub (the codegen-owned root proptest_stubs_test.go) for SOR-2508 R13 — the activation-permanence requirement. |
|
cmd
|
|
|
invariants
command
Command invariants is the coverage-report CLI used by the invariants-coverage CI job.
|
Command invariants is the coverage-report CLI used by the invariants-coverage CI job. |
|
deadlockproptest
|
|
|
gen
Package gen is the operator-supplied generator-helper package backing the committed prop_test stub (../proptest_stubs_test.go) for SOR-2305 R1/R2/R3/R4.
|
Package gen is the operator-supplied generator-helper package backing the committed prop_test stub (../proptest_stubs_test.go) for SOR-2305 R1/R2/R3/R4. |
|
dgaproptest
|
|
|
gen
Package gen is the operator-supplied generator-helper package backing the committed prop_test stub (../proptest_stubs_test.go) for SOR-2296 R4/R5/R6/R7.
|
Package gen is the operator-supplied generator-helper package backing the committed prop_test stub (../proptest_stubs_test.go) for SOR-2296 R4/R5/R6/R7. |
|
Package property wires the invariants framework into pgregory.net/rapid so every registered runtime invariant can be exercised against synthesized random scenario traces.
|
Package property wires the invariants framework into pgregory.net/rapid so every registered runtime invariant can be exercised against synthesized random scenario traces. |
|
corruptedgate
Package corruptedgate is the importable (non _test.go) home of the ONE corrupted-gate-field corpus the SPEC-SOR-2683-v1 meta-check verifies against the REAL invariant registry: a mid-lifecycle CIPB plan with an EMPTY branch_model (the corrupted gate field) that is otherwise in cohort via the corruption-independent signal (plan membership through the materialized-children junction) the re-derived planIsMidLifecycleContinuous reads.
|
Package corruptedgate is the importable (non _test.go) home of the ONE corrupted-gate-field corpus the SPEC-SOR-2683-v1 meta-check verifies against the REAL invariant registry: a mid-lifecycle CIPB plan with an EMPTY branch_model (the corrupted gate field) that is otherwise in cohort via the corruption-independent signal (plan membership through the materialized-children junction) the re-derived planIsMidLifecycleContinuous reads. |
|
speccanonicalroundtripproptest
|
|
|
gen
Package gen is the project-local generator-helper package backing the committed prop_test stub (../proptest_stubs_test.go) for R15 and R16 of the submit_result-contract spec (SOR-2479 umbrella; the canonical-serializer + normalization seam is SOR-2520).
|
Package gen is the project-local generator-helper package backing the committed prop_test stub (../proptest_stubs_test.go) for R15 and R16 of the submit_result-contract spec (SOR-2479 umbrella; the canonical-serializer + normalization seam is SOR-2520). |
|
Package staticcheck holds the build-time, analyzer-enforced members of the design-invariant framework.
|
Package staticcheck holds the build-time, analyzer-enforced members of the design-invariant framework. |
|
cmd/invariantanalyzers
command
Command invariantanalyzers is the standalone multichecker driver for the static analyzers landed by the invariants migration:
|
Command invariantanalyzers is the standalone multichecker driver for the static analyzers landed by the invariants migration: |
|
cmd/singlewriterstate
command
Command singlewriterstate is the standalone driver for the single-writer-state static analyzer.
|
Command singlewriterstate is the standalone driver for the single-writer-state static analyzer. |
|
Package staticmeta holds the dependency-light registry shims for the build-time static invariants.
|
Package staticmeta holds the dependency-light registry shims for the build-time static invariants. |