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
- func BuildCreateCommand(pr PullRequest, details PRDetails, taskIDStr string, stage string, ...) task.CreateCommand
- func BuildOverrideCommand(pr PullRequest, details PRDetails, taskIDStr string, stage string, ...) task.CreateCommand
- func DeriveTaskID(owner, repo string, number int, sha string) uuid.UUID
- func DeriveTaskIDForce(owner, repo string, number int, sha, nonce string) uuid.UUID
- func DeriveTaskIDOverride(owner, repo string, number int, sha string) uuid.UUID
- func NewMemDB() libkv.DB
- func ParseBotAllowlist(raw string) []string
- func ParseTrustedAuthors(raw string) []string
- func SaveCursor(ctx context.Context, path string, state Cursor) error
- type Cursor
- type GitHubClient
- type Metrics
- type PRDetails
- type PullRequest
- type SearchResult
- type TaskConfig
- type TaskPublisher
- type Watcher
Constants ¶
const DefaultCursorPath = "/data/cursor.json"
DefaultCursorPath is the default path for cursor state persistence.
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.
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, forced bool, ) 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.
forced marks an operator-requested re-review (trigger with force=true). The caller has already salted taskIDStr via DeriveTaskIDForce; forced makes the TITLE differ too, which is what actually matters — the agent controller dedupes on title path, so a salted task_identifier alone lands on the existing review task's filename and is silently rejected with ErrTaskAlreadyExists (confirmed in prod, bborbe/coding#90, 2026-08-09).
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 ¶
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 ¶
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
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 ¶
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 ¶
ParseBotAllowlist splits a comma-separated allowlist string into a slice of trimmed, non-empty entries.
func ParseTrustedAuthors ¶
ParseTrustedAuthors splits a comma-separated trusted-authors string into a slice of trimmed, non-empty entries. Mirrors ParseBotAllowlist in behavior.
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.
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 ¶
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.
Source Files
¶
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. |