pkg

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: BSD-2-Clause Imports: 22 Imported by: 0

Documentation

Overview

Package pkg provides the core domain types and logic for the maintainer-watcher-github-pr service: GitHub API integration, pull-request filtering, cursor persistence, and Kafka command publishing.

Index

Constants

View Source
const DefaultCursorPath = "/data/cursor.json"

DefaultCursorPath is the default path for cursor state persistence.

View Source
const DefaultMaxSlugLen = 80

DefaultMaxSlugLen is the default cap for the slugified PR-title segment alone. Bumped from 50 to 80 (2026-05-08) — 50 cut typical PR titles mid-word. Override via MAX_SLUG_LEN.

View Source
const DefaultMaxTitleLen = 200

DefaultMaxTitleLen is the default safety cap for the whole title, including segments and separators. Crosses Windows MAX_PATH=260 and ext4 NAME_MAX=255 with margin. Override via MAX_TITLE_LEN.

Variables

This section is empty.

Functions

func BuildCreateCommand

func BuildCreateCommand(
	pr PullRequest,
	details PRDetails,
	taskIDStr string,
	stage string,
	maxSlugLen int,
	maxTitleLen int,
	taskSuffix string,
	targetVault string,
	trustResult trust.Result,
) task.CreateCommand

BuildCreateCommand builds a CreateTaskCommand for a PR given its details and trust result. It is used by both the poll path (via PublishCreate) and the single-PR trigger handler.

func BuildOverrideCommand added in v0.3.0

func BuildOverrideCommand(
	pr PullRequest,
	details PRDetails,
	taskIDStr string,
	stage string,
	maxSlugLen int,
	maxTitleLen int,
	taskSuffix string,
	targetVault string,
) task.CreateCommand

BuildOverrideCommand builds a `pr-override` CreateTaskCommand for a labeled, trusted-author PR. The caller (PublishOverride) guarantees trust, so there is no untrusted branch here. The title uses the "Override" kind so it never collides with the review task's vault file for the same (PR, SHA).

func DeriveTaskID

func DeriveTaskID(owner, repo string, number int, sha string) uuid.UUID

DeriveTaskID returns a deterministic task identifier for a (PR, SHA) pair. Input: "<owner>/<repo>#<number>@<sha>", e.g. "bborbe/maintainer#42@abc123...". The full SHA is used (not truncated) to keep the dedup keyspace collision-free.

func DeriveTaskIDForce

func DeriveTaskIDForce(owner, repo string, number int, sha, nonce string) uuid.UUID

DeriveTaskIDForce returns a salted task identifier for a (PR, SHA) pair plus an extra nonce. Used when an operator explicitly requests a forced re-review (HTTP /trigger?force=true) so the executor can publish a CreateTaskCommand with a TaskIdentifier that the controller has not already seen — bypassing the dedup-skip in the agent controller.

For the same (owner, repo, number, sha) the result is always different from DeriveTaskID(...): the key includes the nonce segment "<nonce>", e.g. "bborbe/maintainer#42@abc123...!1700000000000000000".

The nonce resolution is the caller's responsibility. Callers should derive it from an injected libtime.CurrentDateTimeGetter; the helper itself is a pure function over its inputs.

func DeriveTaskIDOverride added in v0.3.0

func DeriveTaskIDOverride(owner, repo string, number int, sha string) uuid.UUID

DeriveTaskIDOverride returns a deterministic task identifier for the override action on a (PR, SHA) pair. It appends a fixed "#override" segment to the key so the result never collides with DeriveTaskID(...) for the same (owner, repo, number, sha) — the override task and the review task coexist in the cursor without deduplicating each other.

Because the segment is fixed (not a nonce), the override fires at most once per head SHA while the label stays applied; a new commit produces a new SHA and, if still labeled, a fresh override.

func NewMemDB

func NewMemDB() libkv.DB

NewMemDB returns a session-scoped, in-process libkv.DB implementation. It exists because libkv itself does not ship a public NewMemDB constructor (the upstream bborbe/kv package exposes only NewDBWithMetrics). The github-pr watcher's command consumer needs an offset store; this in-memory implementation gives it one without requiring a PVC (see spec 066 AC 9 rationale: replay-from-OffsetOldest on restart is safe because the downstream CreateTaskCommand is idempotent via derived task_id).

The implementation is intentionally minimal — it implements only the methods the offset-store / consumer wiring touches: Update, View, Sync, Close, Remove, Stats, StatsDetailed. Iterator / ListBucketNames return empty results because no caller in this package uses them.

func ParseBotAllowlist

func ParseBotAllowlist(raw string) []string

ParseBotAllowlist splits a comma-separated allowlist string into a slice of trimmed, non-empty entries.

func ParseTrustedAuthors

func ParseTrustedAuthors(raw string) []string

ParseTrustedAuthors splits a comma-separated trusted-authors string into a slice of trimmed, non-empty entries. Mirrors ParseBotAllowlist in behavior.

func SaveCursor

func SaveCursor(ctx context.Context, path string, state Cursor) error

SaveCursor persists cursor state to path atomically via a temp file + rename.

Types

type Cursor

type Cursor struct {
	LastUpdatedAt libtime.DateTime  `json:"last_updated_at"`
	HeadSHAs      map[string]string `json:"head_shas"`
}

Cursor holds the watcher's persisted poll state, including the last-seen update time and a map of task-identifier to head SHA for force-push detection.

func LoadCursor

func LoadCursor(ctx context.Context, path string, startTime libtime.DateTime) (Cursor, error)

LoadCursor reads cursor state from path. Returns cold-start state with startTime if the file is missing or corrupt.

type GitHubClient

type GitHubClient interface {
	// SearchPRs issues a GitHub Search query for open PRs updated since cursor.
	// page=1 for the first call; use SearchResult.NextPage for subsequent calls.
	// PullRequest.HeadSHA in the result is empty — call GetPRDetails to fetch it.
	SearchPRs(
		ctx context.Context,
		scope string,
		since libtime.DateTime,
		page int,
	) (SearchResult, error)

	// GetPRDetails fetches the head SHA, clone URL, and base ref for a single PR.
	// The Search API does NOT return any of these, so the poll loop must call
	// this for every PR before publishing a task command.
	GetPRDetails(ctx context.Context, owner, repo string, number int) (PRDetails, error)
}

GitHubClient abstracts the GitHub API calls.

func NewGitHubClient

func NewGitHubClient(httpClient *http.Client) GitHubClient

NewGitHubClient returns a GitHubClient backed by the real GitHub API. The httpClient must already carry authentication (App auth via lib/githubapp.NewClient).

type Metrics

type Metrics interface {
	// IncPollCycle increments the poll cycle counter with the given result label.
	// result: "success", "rate_limited", "github_error"
	IncPollCycle(result string)
	// IncPRPublished increments the PR-published counter with the given command label.
	// command: "create", "update_frontmatter", "skipped", "error", "override", "override_skipped"
	IncPRPublished(command string)
}

Metrics exposes counters for observable watcher behaviour.

func NewMetrics

func NewMetrics() Metrics

NewMetrics returns a Metrics implementation backed by Prometheus counters.

type PRDetails

type PRDetails struct {
	// HeadSHA is the commit hash of the PR's head branch. Used for force-push
	// detection and as the `ref` the agent checks out for review.
	HeadSHA string

	// CloneURL is the HTTPS clone URL of the head repo (e.g.
	// `https://github.com/owner/repo.git`). Used as the `clone_url` the
	// agent's execution phase passes to git clone.
	CloneURL string

	// BaseRef is the base branch name (e.g. `master`, `main`). Used as
	// the `base_ref` the execution phase diffs against.
	BaseRef string

	// AuthorLogin is the GitHub author login; empty for deleted accounts.
	AuthorLogin string

	// Title is the PR title.
	Title string

	// IsDraft indicates whether the PR is a draft.
	IsDraft bool

	// UpdatedAt is the PR last-updated timestamp; required for AgeFilter.
	UpdatedAt libtime.DateTime

	// Labels holds the PR's label names (e.g. `override-review`). Used to
	// route a labeled PR to the override task type.
	Labels []string
}

PRDetails holds the per-PR fields the watcher needs to materialize a task the execution phase can act on. The Search API does not expose any of these; they require a follow-up PullRequests.Get call.

type PullRequest

type PullRequest struct {
	GlobalID    int64
	Number      int
	Owner       string
	Repo        string
	Title       string
	HTMLURL     string
	HeadSHA     string
	AuthorLogin string
	IsDraft     bool
	UpdatedAt   libtime.DateTime
	// Labels holds the PR's label names (e.g. `override-review`). Populated
	// from the Search API's issue labels.
	Labels []string
}

PullRequest holds the fields the watcher needs from a GitHub PR.

type SearchResult

type SearchResult struct {
	PullRequests  []PullRequest
	HasNextPage   bool
	NextPage      int
	RateRemaining int
	RateResetAt   libtime.DateTime
}

SearchResult is the result of a single paginated search call.

type TaskConfig

type TaskConfig struct {
	Stage       string
	MaxSlugLen  int
	MaxTitleLen int
	TaskSuffix  string
	// TargetVault routes the CreateTaskCommand to a specific vault controller
	// (matched verbatim against the controller's VAULT_NAME). Empty leaves
	// TargetVault unset, so the controller's legacy default-vault fallback
	// applies — preserving pre-TARGET_VAULT behaviour.
	TargetVault string
}

TaskConfig groups the per-task publishing configuration.

type TaskPublisher

type TaskPublisher interface {
	PublishCreate(ctx context.Context, pr PullRequest, taskIDStr string, details PRDetails) bool
	// PublishOverride publishes a `pr-override` task for a labeled PR. It is
	// trusted-authors-only. Returns (handled, emitted):
	//   - handled=true: this is a trusted-author override case; the caller must
	//     NOT emit a review task for this SHA (even when the send failed —
	//     retry on the next poll). This preserves the override-only invariant
	//     that prevents an APPROVE-vs-CHANGES_REQUESTED race.
	//   - emitted=true: the override task was actually sent; the caller records
	//     it in the cursor so the override fires at most once per head SHA.
	//   - untrusted author (or trust-check error) → (false, false): the caller
	//     falls through to the normal review path (which routes untrusted →
	//     human_review).
	PublishOverride(
		ctx context.Context,
		pr PullRequest,
		taskIDStr string,
		details PRDetails,
	) (handled, emitted bool)
}

TaskPublisher publishes create-task commands for a given PR + details pair. Returns true on successful publish, false on trust check failure or send failure.

func NewTaskPublisher

func NewTaskPublisher(
	createSender task.CreateCommandSender,
	trustDecision trust.Trust,
	metrics Metrics,
	cfg TaskConfig,
) TaskPublisher

NewTaskPublisher returns a TaskPublisher that performs trust evaluation then publishes a CreateTaskCommand via the given CreateCommandSender.

type Watcher

type Watcher interface {
	Poll(ctx context.Context) error
}

Watcher polls GitHub and publishes task commands to Kafka.

func NewWatcher

func NewWatcher(
	ghClient GitHubClient,
	publisher TaskPublisher,
	metrics Metrics,
	cursorPath string,
	startTime libtime.DateTime,
	scope string,
	taskCreationFilter filter.TaskCreationFilter,
	overrideLabel string,
) Watcher

NewWatcher returns a Watcher that polls GitHub and publishes commands.

Directories

Path Synopsis
Package command defines the TriggerPRReviewCommand payload and its Kafka sender for the github-pr watcher's request topic.
Package command defines the TriggerPRReviewCommand payload and its Kafka sender for the github-pr watcher's request topic.
Package factory wires concrete dependencies for the maintainer-watcher-github-pr binary.
Package factory wires concrete dependencies for the maintainer-watcher-github-pr binary.
Package filter implements the TaskCreationFilter chain.
Package filter implements the TaskCreationFilter chain.
Package trust provides the boolean-combinator trust-decision framework for the maintainer-watcher-github-pr.
Package trust provides the boolean-combinator trust-decision framework for the maintainer-watcher-github-pr.

Jump to

Keyboard shortcuts

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