Help us shape the future of Go! Take the 2026 Go Developer Survey

pkg

package
v0.4.3 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: BSD-2-Clause Imports: 21 Imported by: 0

Documentation

Overview

Package pkg provides the core domain types and logic for the github-dark-factory-watcher 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. Override via MAX_SLUG_LEN.

View Source
const DefaultMaxTitleLen = 200

DefaultMaxTitleLen is the default safety cap for the whole vault filename, 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,
	cfg TaskConfig,
	matchedSpecs []string,
) task.CreateCommand

BuildCreateCommand builds the dark-factory-implement CreateTaskCommand for a candidate PR. Exposed for direct testing of the emitted contract.

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. Same inputs → same UUID, so a re-emit of the same (PR, SHA) is a controller no-op.

func DeriveTaskIDForce added in v0.2.0

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 force-triggers a re-run via the /trigger command with force=true: the nonce segment "!<nonce>" makes the UUID differ from DeriveTaskID(...) for the same (PR, SHA), so the agent controller's task-file dedup does NOT skip it and a fresh task is created. The nonce is the caller's responsibility (typically a microsecond-resolution time value).

The nonce is expected to carry microsecond resolution (e.g. UnixMicro): two force-triggers landing in the same microsecond could theoretically derive the same identifier and collide. Future readers should not swap this for a coarser clock (second/millisecond resolution widens that collision window).

func NewMemDB added in v0.2.0

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 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.
	SearchPRs(
		ctx context.Context,
		scope string,
		since libtime.DateTime,
		page int,
	) (SearchResult, error)

	// GetPRDetails fetches head SHA, clone URL, branch, state and draft flag for
	// a single PR via pulls/N (re-confirming draft/state against search-index lag).
	GetPRDetails(ctx context.Context, owner, repo string, number int) (PRDetails, error)

	// ListPRFiles returns the paths of every file touched by the PR diff
	// (paginated pulls/N/files).
	ListPRFiles(ctx context.Context, owner, repo string, number int) ([]string, error)

	// GetContent returns the decoded bytes of a repo file at the given ref.
	// A missing file surfaces as an error for which IsNotFound reports true.
	GetContent(ctx context.Context, owner, repo, filePath, ref string) ([]byte, error)

	// ListDir returns the entry paths (files and subdirs) directly under the
	// given directory path at ref. A missing directory surfaces as an error for
	// which IsNotFound reports true.
	ListDir(ctx context.Context, owner, repo, dirPath, ref string) ([]string, error)

	// IsNotFound reports whether err is a GitHub 404 (missing file/dir/ref).
	IsNotFound(err error) bool
}

GitHubClient abstracts the GitHub API calls the watcher and the darkfactory candidate evaluator need.

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)
	// IncPublished increments the published counter with the given status label.
	// status: "create", "skipped", "error"
	IncPublished(status string)
	// IncReposScanned increments the scanned-PR counter (sanity check on the scope filter).
	IncReposScanned()
	// IncFilterSkipped increments the filter-skipped counter with the given reason.
	// reason: "out_of_scope", "details_error", "empty_sha", "not_draft", "not_open",
	// "no_dark_factory_yaml", "no_spec_in_diff", "prompts_in_flight", "evaluate_error"
	IncFilterSkipped(reason string)
}

Metrics exposes counters for observable watcher behaviour.

func NewMetrics

func NewMetrics(registerer prometheus.Registerer) Metrics

NewMetrics returns a Metrics implementation backed by Prometheus counters registered against the given registerer. Passing nil resolves to prometheus.DefaultRegisterer (production); tests pass prometheus.NewRegistry() so repeated construction in the same process does not panic with a duplicate registration.

type PRDetails

type PRDetails struct {
	// HeadSHA is the commit hash of the PR's head branch. Used as the `ref`
	// the agent checks out and as the per-task dedup key.
	HeadSHA string
	// CloneURL is the HTTPS clone URL of the head repo (e.g.
	// `https://github.com/owner/repo.git`). Emitted as `clone_url`.
	CloneURL string
	// Branch is the head branch name (e.g. `dark-factory/foo`). Emitted as `branch`.
	Branch string
	// State is the PR state ("open" / "closed"), re-confirmed via pulls/N.
	State string
	// IsDraft indicates whether the PR is a draft, re-confirmed via pulls/N.
	IsDraft bool
	// Title is the PR title.
	Title string
	// UpdatedAt is the PR last-updated timestamp; drives the cursor watermark.
	UpdatedAt libtime.DateTime
}

PRDetails holds the per-PR fields the watcher needs to re-confirm state and materialize a task the agent can act on. The Search API does not expose most of these; they require a follow-up PullRequests.Get call (which also dodges search-index lag on the draft/state flags).

type PullRequest

type PullRequest struct {
	GlobalID  int64
	Number    int
	Owner     string
	Repo      string
	Title     string
	HTMLURL   string
	IsDraft   bool
	UpdatedAt libtime.DateTime
}

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

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 it
	// unset so the controller's legacy default-vault fallback applies.
	TargetVault string
}

TaskConfig groups the per-task publishing configuration.

type Watcher

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

Watcher polls GitHub and publishes dark-factory-implement task commands to Kafka.

func NewWatcher

func NewWatcher(
	ghClient GitHubClient,
	createSender task.CreateCommandSender,
	metrics Metrics,
	cursorPath string,
	startTime libtime.DateTime,
	scope string,
	scopeFilter filter.TaskCreationFilter,
	cfg TaskConfig,
) Watcher

NewWatcher returns a Watcher that polls GitHub and publishes commands.

Directories

Path Synopsis
Package command defines the TriggerCommand payload and its Kafka sender for the github-dark-factory watcher's request topic.
Package command defines the TriggerCommand payload and its Kafka sender for the github-dark-factory watcher's request topic.
Package darkfactory holds the network-backed candidate evaluator that decides whether an open draft PR carries an approved-not-completed dark-factory spec in its diff and therefore warrants a dark-factory-implement task.
Package darkfactory holds the network-backed candidate evaluator that decides whether an open draft PR carries an approved-not-completed dark-factory spec in its diff and therefore warrants a dark-factory-implement task.
Package factory wires concrete dependencies for the github-dark-factory-watcher binary.
Package factory wires concrete dependencies for the github-dark-factory-watcher binary.
Package filter implements the scope filter chain — the mandatory defense-in-depth predicate that refuses to act on any repo outside the configured REPO_ALLOWLIST, even if the upstream GitHub search returns more.
Package filter implements the scope filter chain — the mandatory defense-in-depth predicate that refuses to act on any repo outside the configured REPO_ALLOWLIST, even if the upstream GitHub search returns more.

Jump to

Keyboard shortcuts

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