curate

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package curate is the Phase-2 grooming agent: it dedups the KB backlog, gates the decision-ready queue on incident resolution, surfaces recurring blind spots as knowledge-gap issues, and drives lifecycle/decay. It writes to the forge only — it never merges and never touches a human-labelled artifact.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Agent

type Agent struct {
	Passes []Pass
	Log    *slog.Logger
}

Agent runs the grooming passes in order. Forge-only writes; never merges.

func (Agent) Run

func (a Agent) Run(ctx context.Context)

Run executes every pass, logging (not propagating) per-pass errors.

type ClosedPRLister added in v0.4.0

type ClosedPRLister interface {
	ListClosedUnmergedPRsByLabel(ctx context.Context, label string) ([]providers.CuratedIssue, error)
}

ClosedPRLister lists closed-but-unmerged KB PRs carrying a label. Merged PRs are accepted entries (never suppressed) and are filtered out by the implementation.

type ClosedPRSuppression added in v0.4.0

type ClosedPRSuppression struct {
	Forge ClosedPRLister
}

ClosedPRSuppression derives the suppression set on every run from the forge's closed-unmerged KB PRs — no mutable store or watermark, mirroring Recurrence's idempotent, ledger-driven design. The forge retains closed PRs, and each carries the same hidden DupFingerprint marker the drafter stamped, so the set is reconstructable each pass. A markerless (legacy/hand-filed) close is skipped: there is no stable key to suppress or count it on.

func (ClosedPRSuppression) Suppressed added in v0.4.0

Suppressed implements SuppressionSource.

type Dedup

type Dedup struct {
	Forge     Forge
	Threshold float64 // title-Jaccard fallback threshold for markerless PRs; default 0.6 when 0
	Log       *slog.Logger
}

Dedup collapses near-identical open KB PRs: the lowest-numbered PR in a cluster is canonical; the rest are closed with a back-ref comment. Conservative by design (high similarity threshold) — a missed merge is cheaper than a wrong close.

func (Dedup) Run

func (d Dedup) Run(ctx context.Context) error

Run clusters open PRs and closes duplicates — fingerprint-first (deterministic DupFingerprint marker), falling back to title-Jaccard for markerless PRs.

type Forge

type Forge interface {
	ListPRsByLabel(ctx context.Context, label string) ([]providers.CuratedIssue, error)
	ListIssuesByLabel(ctx context.Context, label string) ([]providers.CuratedIssue, error)
	Comment(ctx context.Context, number int, body string) error
	ReplaceLabel(ctx context.Context, number int, remove, add string) error
	Close(ctx context.Context, number int) error
	OpenIssue(ctx context.Context, inv providers.Investigation) (providers.Ref, error)
}

Forge is the forge surface the groomer needs (all read/label/close — never merge).

type LedgerResolutionChecker

type LedgerResolutionChecker struct {
	Ledger interface {
		Episodes() ([]outcome.Episode, error)
	}
}

LedgerResolutionChecker reports a curated PR's incident has resolved when the outcome ledger holds a matching resolved episode. The join is keyed on the deterministic dedup fingerprint (resource+cause) carried in the PR body and stamped on every ledger open — stable across the LLM's prose, so a reworded re-investigation of one incident still resolves the matching PR. A PR filed before the fingerprint was wired carries no marker; it falls back to a whitespace-normalized title join. Source-agnostic: it reads the ledger's episodes, never a trigger-specific API.

func (LedgerResolutionChecker) IsResolved

IsResolved implements ResolutionChecker.

type Lifecycle

type Lifecycle struct {
	Forge      Forge
	StaleAfter time.Duration    // 0 disables the sweep
	Now        func() time.Time // injectable clock; nil ⇒ time.Now
	Log        *slog.Logger
}

Lifecycle closes stale, unprotected KB artifacts — those with no forge activity within StaleAfter. A PR whose age is unknown (zero UpdatedAt) is never closed.

func (Lifecycle) Run

func (l Lifecycle) Run(ctx context.Context) error

Run closes stale, unprotected artifacts with a comment.

type Pass

type Pass interface {
	Run(ctx context.Context) error
}

Pass is one grooming pass (dedup, queue, lifecycle…). Each is independent and resilient: a pass error is logged, not fatal — the agent runs the rest.

type Queue

type Queue struct {
	Forge   Forge
	Checker ResolutionChecker
	Log     *slog.Logger
}

Queue applies the merge condition: a quality-passing (solved) PR becomes ready-to-merge when the incident is resolved, OR when a human has labelled it accepted. Unresolved + unaccepted PRs wait (surfaced, never auto-queued).

func (Queue) Run

func (q Queue) Run(ctx context.Context) error

Run gates each solved PR onto the decision-ready queue.

type Recurrence

type Recurrence struct {
	Forge  Forge
	Ledger interface {
		Episodes() ([]outcome.Episode, error)
	}
	Threshold int // default 3 when 0
	// Suppressed, when set, is the set of KB entries a human closed WITHOUT merging
	// (keyed by DupFingerprint). Their episodes are counted SEPARATELY — silently,
	// keyed on the fingerprint — and, past the threshold, escalate via a knowledge-gap
	// issue that LINKS the closed PR ("closed unmerged but recurred N times —
	// reconsider?") instead of the plain gap message. A close-without-merge is a
	// deliberate "no": we respect it and never reopen the PR. Nil disables the branch
	// (behaviour is then identical to the pre-suppression pass).
	Suppressed SuppressionSource
	Log        *slog.Logger
}

Recurrence opens ONE knowledge-gap issue when an unresolved incident pattern recurs at least Threshold times. It is ledger-driven and idempotent: it recomputes counts from the outcome ledger every run and opens an issue only when no knowledge-gap issue for that pattern already exists — so re-running never double-opens (no mutable store or watermark).

func (Recurrence) Run

func (r Recurrence) Run(ctx context.Context) error

Run opens a knowledge-gap issue for each pattern that crosses the threshold and has none open yet. Episodes belonging to a suppressed (closed-unmerged) entry are routed to a separate escalation that links the closed PR rather than the generic gap issue.

type ResolutionChecker

type ResolutionChecker interface {
	IsResolved(ctx context.Context, pr providers.CuratedIssue) (bool, error)
}

ResolutionChecker reports whether the incident behind a curated PR has cleared (alert resolved / resource healthy). Implemented read-only over the cluster.

type SuppressedEntry added in v0.4.0

type SuppressedEntry struct {
	Fingerprint string // curator DupFingerprint (resource+cause); the join key against ledger episodes
	PRNumber    int    // the closed-unmerged KB PR to reference in the escalation
	Reason      string // the close-reason label, when one distinguishes it (e.g. "wontfix"); "" otherwise
}

SuppressedEntry is a KB entry a human closed WITHOUT merging — a deliberate "not knowledge-base-worthy". It is keyed on the entry's DupFingerprint so the recurrence pass can keep counting the incident silently and, past the threshold, escalate via a knowledge-gap issue that LINKS the closed PR — never reopening it.

type SuppressionSource added in v0.4.0

type SuppressionSource interface {
	Suppressed(ctx context.Context) (map[string]SuppressedEntry, error)
}

SuppressionSource yields the set of suppressed fingerprints (closed-unmerged KB entries a human rejected), keyed by DupFingerprint. Recurrence consults it to escalate-via-issue instead of re-litigating a human "no".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL