contributor

package
v1.84.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package contributor implements the core of the Unstable Build contributor program: domain types, storage-agnostic stores and a deterministic allocation engine.

The package is intentionally free of provider dependencies (no cloud, payment or identity SDKs) so that the entire allocation logic is public and independently auditable. Monetary amounts are always integer cents; credits are allocation weights, never currency.

Index

Constants

View Source
const CalculationVersion = 1

CalculationVersion is the version of the allocation engine implemented by this package. Programs pin the calculation version they were published against; ComputeRound refuses to run on a mismatch.

Variables

View Source
var (
	// ErrProgramNotConfigured is returned when no program policy is
	// effective for the month an operation applies to.
	ErrProgramNotConfigured = errors.New("no program effective")

	// ErrParticipantNotFound is returned when an award names a GitHub
	// handle that belongs to no enrolled participant.
	ErrParticipantNotFound = errors.New("no enrolled participant")

	// ErrAwardNotFound is returned when a decision names an unknown
	// award.
	ErrAwardNotFound = errors.New("award not found")

	// ErrAwardAlreadyDecided is returned when a decision targets an
	// award that has already been approved or rejected. Decisions are
	// final.
	ErrAwardAlreadyDecided = errors.New("award already decided")
)

Administrative operations fail with these sentinels so callers can map them to transport-specific outcomes without matching on message text.

View Source
var ErrRoundClosed = errors.New("round already closed")

ErrRoundClosed is returned by Ledger.CloseRound when the month has already been closed.

Functions

func ActiveCredits

func ActiveCredits(awards []Award, m Month) (map[string]int64, error)

ActiveCredits resolves the active credits per account for month m from approved awards. An award is active in m when Activation <= m < Expiration. Awards that are not approved are ignored; approved awards that fail validation are an error.

func ComputeRound

func ComputeRound(
	p Program, m Month, receipts []Receipt, awards []Award,
	carryforwardCents int64,
) (Round, []Obligation, error)

ComputeRound computes the allocation round for month m from frozen inputs: the effective program, the month's covered receipts, the approved awards and the balance carried forward from the previous round. It returns the Round and the obligations it produces, in ObligationEarned state. It persists nothing and reads no clocks: closing is the caller's responsibility.

When the pool is not positive or no credits are active, the round carries its pool forward through Round.UnallocatedCents and produces no obligations.

func DueDate

func DueDate(p Program, m Month) time.Time

DueDate returns when obligations of round m become due under policy p: PayoutDueDays days after the end of the calendar quarter containing m.

func ObligationID

func ObligationID(m Month, account string) string

ObligationID returns the deterministic identifier of the obligation for month m and the given account. Determinism makes payment instructions idempotent.

func PoolShareCents

func PoolShareCents(poolBps int, netReceiptsCents int64) int64

PoolShareCents returns the pool contribution of the given net receipts under a pool share expressed in basis points. The result is truncated toward zero; sub-cent remainders stay with the operator, never the pool.

func VerifyRound

func VerifyRound(s Statement) error

VerifyRound recomputes the allocation of a closed round from its frozen inputs and returns an error describing the first discrepancy found, or nil if the round's published results are reproducible.

Types

type Adjustment

type Adjustment struct {
	// Kind is the deduction category.
	Kind AdjustmentKind
	// AmountCents is the deducted amount in cents. Always non-negative;
	// it is subtracted from the gross.
	AmountCents int64
	// Description optionally explains the adjustment.
	Description string
}

Adjustment is a single deduction from a receipt's gross amount.

type AdjustmentKind

type AdjustmentKind string

AdjustmentKind enumerates the permitted deductions from a receipt's gross amount. Only enumerated kinds may reduce the covered pool base.

const (
	// AdjustmentTax covers sales tax, VAT and similar collected taxes.
	AdjustmentTax AdjustmentKind = "tax"
	// AdjustmentRefund covers refunds and chargebacks.
	AdjustmentRefund AdjustmentKind = "refund"
	// AdjustmentPassThrough covers amounts collected on behalf of third
	// parties that are not program revenue.
	AdjustmentPassThrough AdjustmentKind = "pass-through"
)

type Award

type Award struct {
	// ID is the award's identifier, assigned by the store on creation.
	ID string

	// Splits assigns the award's credits to one or more recipients.
	Splits []Split

	// Activation is the first round in which the credits are active.
	Activation Month

	// Expiration is the first round in which the credits are no longer
	// active (exclusive bound), normally derived from the policy's
	// credit duration via Program.ExpirationMonth.
	Expiration Month

	// Evidence lists public URLs backing the award, e.g. merged pull
	// requests. At least one is required.
	Evidence []string

	// Description summarizes the contribution being awarded.
	Description string

	// PolicyVersion is the Program.Version under which the award was
	// proposed.
	PolicyVersion string

	// Status is the award's review status.
	Status AwardStatus

	// ProposedBy identifies who proposed the award.
	ProposedBy string
	// ProposedAt is when the award was proposed.
	ProposedAt time.Time

	// DecidedBy identifies who approved or rejected the award.
	DecidedBy string
	// DecidedAt is when the award was approved or rejected.
	DecidedAt time.Time
	// DecisionNote optionally explains the decision.
	DecisionNote string

	CreatedAt time.Time
	UpdatedAt time.Time
}

Award is an explicit grant of credits for a contribution, always backed by public evidence (e.g. merged pull request URLs). Awards are never auto-computed from activity metrics.

func (Award) ActiveIn

func (a Award) ActiveIn(m Month) bool

ActiveIn reports whether the award's credits are active in month m, i.e. Activation <= m < Expiration.

func (Award) TotalCredits

func (a Award) TotalCredits() int64

TotalCredits returns the sum of the award's split credits.

func (Award) Validate

func (a Award) Validate() error

Validate returns an error if the Award is not well formed. Recipients must be unique within an award and every split must carry positive credits.

type AwardStatus

type AwardStatus string

AwardStatus is the review status of an Award.

const (
	// AwardProposed awards are pending review and carry no weight.
	AwardProposed AwardStatus = "proposed"
	// AwardApproved awards contribute active credits between their
	// activation and expiration months.
	AwardApproved AwardStatus = "approved"
	// AwardRejected awards were reviewed and declined.
	AwardRejected AwardStatus = "rejected"
)

type AwardStore

type AwardStore interface {
	// CreateAward stores a new award and returns its assigned ID.
	CreateAward(context.Context, Award) (string, error)

	// GetAward returns the award with the given ID.
	GetAward(ctx context.Context, id string) (Award, error)

	// SetAward overwrites the award identified by Award.ID.
	SetAward(context.Context, Award) error

	// ListAwards lists awards with the given status, or all awards if
	// status is empty.
	ListAwards(ctx context.Context, status AwardStatus) (
		iterator.Iterator[Award], error)
}

AwardStore persists Awards.

func NewDocumentAwardStore

func NewDocumentAwardStore(db document.Service) AwardStore

NewDocumentAwardStore returns an AwardStore backed by db.

type Entitlement

type Entitlement struct {
	// Account is the participant's account reference.
	Account string
	// Credits is the participant's active credits in the round —
	// a frozen input of the calculation.
	Credits int64
	// AmountCents is the resulting entitlement in cents.
	AmountCents int64
}

Entitlement is one participant's share of a round's pool.

func Allocate

func Allocate(poolCents int64, credits map[string]int64) ([]Entitlement, error)

Allocate splits poolCents among accounts proportionally to their credits: amount_i = poolCents × credits_i / Σcredits, rounded with the largest-remainder method so that the allocated total always equals poolCents exactly. Entitlements are returned ordered by account.

A zero credit total yields no entitlements: the caller carries the pool forward. A zero pool yields zero-amount entitlements that still record each account's credits.

type HandleSplit added in v1.84.0

type HandleSplit struct {
	// GitHubHandle is the recipient's public GitHub username. It is
	// matched against enrolled participants case-insensitively.
	GitHubHandle string

	// Credits is the credit quantity assigned to the recipient.
	Credits int64
}

HandleSplit assigns a share of an award's credits to a recipient identified by GitHub handle.

type Ledger

type Ledger struct {
	Programs     ProgramStore
	Participants ParticipantStore
	Awards       AwardStore
	Receipts     ReceiptStore
	Rounds       RoundStore
	Obligations  ObligationStore
}

Ledger orchestrates the contributor program stores: it assembles the frozen inputs of a round from the stores, delegates the calculation to the pure engine and persists the results. It contains no allocation logic of its own.

func (Ledger) CloseRound

func (l Ledger) CloseRound(
	ctx context.Context, m Month, closedAt time.Time, closedBy string,
) (Round, error)

CloseRound freezes month m: it computes the round from the effective program, the month's receipts, the approved awards and the previous round's carryforward, then persists the round and its obligations in ObligationEarned state. It returns ErrRoundClosed if m was already closed. Closing out of order is rejected; a closed round is never modified.

func (Ledger) DecideAward added in v1.84.0

func (l Ledger) DecideAward(
	ctx context.Context, id string, approve bool, decidedBy, note string,
) (Award, error)

DecideAward approves or rejects a proposed award and returns the decided record. Only proposed awards can be decided: it returns ErrAwardNotFound for an unknown ID and ErrAwardAlreadyDecided if the award was already approved or rejected.

func (Ledger) Estimate

func (l Ledger) Estimate(ctx context.Context, m Month) (
	Round, []Obligation, error,
)

Estimate computes the round for month m from current data without persisting anything. The returned obligations are labeled ObligationEstimated: nothing is final until the round closes.

func (Ledger) ImportReceipts added in v1.84.0

func (l Ledger) ImportReceipts(
	ctx context.Context, receipts []Receipt,
) (imported, duplicates int, err error)

ImportReceipts stores receipts of covered revenue, reporting how many were created and how many were already present. Receipts are keyed by their source reference, so re-importing a batch is a no-op by design and the operation is safe to retry.

func (Ledger) ProposeAward added in v1.84.0

func (l Ledger) ProposeAward(
	ctx context.Context, req ProposeAwardRequest,
) (string, error)

ProposeAward records a proposed award and returns its assigned ID. The award's expiration and policy version are derived from the program effective in the activation month, so a later policy change never retroactively alters the terms an award was granted under.

It returns ErrProgramNotConfigured if no policy is effective in the activation month and ErrParticipantNotFound if a split names a handle that is not enrolled.

func (Ledger) SetProgram added in v1.84.0

func (l Ledger) SetProgram(ctx context.Context, p Program) error

SetProgram publishes a program policy version. The policy is pinned to the engine's CalculationVersion so a round computed under it refuses to close against a different engine.

type Month

type Month string

Month identifies a monthly allocation round in "YYYY-MM" form. The representation sorts lexicographically in chronological order, which makes it usable directly in document store filters.

func MonthOf

func MonthOf(t time.Time) Month

MonthOf returns the Month containing the given time, in UTC.

func ParseMonth

func ParseMonth(s string) (Month, error)

ParseMonth parses a Month in "YYYY-MM" form.

func (Month) Add

func (m Month) Add(n int) Month

Add returns the Month n months after m. Negative values of n are permitted and move backwards in time.

func (Month) Before

func (m Month) Before(o Month) bool

Before reports whether m is strictly earlier than o.

func (Month) Next

func (m Month) Next() Month

Next returns the month immediately after m.

func (Month) Prev

func (m Month) Prev() Month

Prev returns the month immediately before m.

func (Month) Time

func (m Month) Time() time.Time

Time returns the first instant of the month in UTC. It panics if the Month is invalid; call Validate first on untrusted input.

func (Month) Validate

func (m Month) Validate() error

Validate returns an error if the Month is not in "YYYY-MM" form.

type Obligation

type Obligation struct {
	// Account is the recipient participant's account reference.
	Account string

	// Month is the round that produced the obligation.
	Month Month

	// AmountCents is the gross amount owed in cents.
	AmountCents int64

	// Currency is the ISO 4217 currency code.
	Currency string

	// DueDate is when the obligation becomes due under the payment
	// timetable.
	DueDate time.Time

	// State is the current lifecycle state.
	State ObligationState

	// ProviderTransferRef is an opaque reference to the payout
	// provider's transfer, set once a transfer is initiated.
	ProviderTransferRef string

	// History records every state transition.
	History []ObligationEvent

	CreatedAt time.Time
	UpdatedAt time.Time
}

Obligation is a payable resulting from a closed round's entitlement. Its identity is derived from the round month and recipient account (see ObligationID), which also serves as the idempotency key for provider transfers.

func (Obligation) ID

func (o Obligation) ID() string

ID returns the obligation's deterministic identifier.

func (Obligation) Transition

func (o Obligation) Transition(
	to ObligationState, at time.Time, note string,
) (Obligation, error)

Transition returns a copy of the obligation moved to state, recording the transition in History. It returns an error if the transition is not legal.

type ObligationEvent

type ObligationEvent struct {
	From ObligationState
	To   ObligationState
	At   time.Time
	Note string
}

ObligationEvent records one state transition for audit purposes.

type ObligationState

type ObligationState string

ObligationState is the lifecycle state of an Obligation. States label where the amount stands; no state implies funds are reserved.

const (
	// ObligationEstimated is a running estimate for a round that has not
	// closed yet. Estimates are never persisted as obligations.
	ObligationEstimated ObligationState = "estimated"
	// ObligationEarned means the round closed and the entitlement is
	// final, but not yet due under the payment timetable.
	ObligationEarned ObligationState = "earned"
	// ObligationOwed means the obligation is due for payment.
	ObligationOwed ObligationState = "owed"
	// ObligationInitiated means a provider transfer was created.
	ObligationInitiated ObligationState = "initiated"
	// ObligationSettled means the transfer completed.
	ObligationSettled ObligationState = "settled"
)

type ObligationStore

type ObligationStore interface {
	// CreateObligation stores a new obligation. It returns
	// document.ErrAlreadyExists if it was already created.
	CreateObligation(context.Context, Obligation) error

	// GetObligation returns the obligation for month m and account.
	GetObligation(ctx context.Context, m Month, account string) (
		Obligation, error)

	// SetObligation overwrites the obligation record.
	SetObligation(context.Context, Obligation) error

	// ListAccountObligations lists all obligations of an account.
	ListAccountObligations(ctx context.Context, account string) (
		iterator.Iterator[Obligation], error)

	// ListStateObligations lists all obligations in the given state.
	ListStateObligations(ctx context.Context, state ObligationState) (
		iterator.Iterator[Obligation], error)
}

ObligationStore persists Obligations, keyed by ObligationID.

func NewDocumentObligationStore

func NewDocumentObligationStore(db document.Service) ObligationStore

NewDocumentObligationStore returns an ObligationStore backed by db.

type Participant

type Participant struct {
	// Account is an opaque reference to the participant's user account
	// in the embedding system. It is the Participant's identity.
	Account string

	// GitHubHandle is the participant's public GitHub username.
	GitHubHandle string

	// AgreementVersion is the contributor agreement version accepted at
	// enrollment.
	AgreementVersion string

	// AgreementAcceptedAt is when the agreement was accepted.
	AgreementAcceptedAt time.Time

	// AgreementEvidence is an opaque reference to evidence of the
	// acceptance, e.g. a request audit log entry.
	AgreementEvidence string

	// Status is the participant's eligibility status.
	Status ParticipantStatus

	// PayoutProviderRef is an opaque reference to the participant's
	// account at the payout provider. Empty until payment onboarding
	// completes; enrollment never requires it.
	PayoutProviderRef string

	// PayoutReady reports whether the payout provider accepted the
	// participant's onboarding and transfers can be initiated.
	PayoutReady bool

	CreatedAt time.Time
	UpdatedAt time.Time
}

Participant is an enrolled contributor. Public recognition (the GitHub handle) is kept separate from private payee identity: this type carries only an opaque payout provider reference, never banking details.

func (Participant) Validate

func (p Participant) Validate() error

Validate returns an error if the Participant is missing required fields.

type ParticipantStatus

type ParticipantStatus string

ParticipantStatus is the eligibility status of a Participant.

const (
	// ParticipantEligible participants receive entitlements in rounds.
	ParticipantEligible ParticipantStatus = "eligible"
	// ParticipantSuspended participants are temporarily excluded from
	// payouts, e.g. pending compliance review.
	ParticipantSuspended ParticipantStatus = "suspended"
	// ParticipantWithdrawn participants have left the program.
	ParticipantWithdrawn ParticipantStatus = "withdrawn"
)

type ParticipantStore

type ParticipantStore interface {
	// CreateParticipant stores a new participant. It returns
	// document.ErrAlreadyExists if the account is already enrolled.
	CreateParticipant(context.Context, Participant) error

	// GetParticipant returns the participant with the given account.
	GetParticipant(ctx context.Context, account string) (Participant, error)

	// SetParticipant overwrites the participant record.
	SetParticipant(context.Context, Participant) error

	// ListParticipants lists all participants.
	ListParticipants(context.Context) (iterator.Iterator[Participant], error)
}

ParticipantStore persists Participants, keyed by account reference.

func NewDocumentParticipantStore

func NewDocumentParticipantStore(db document.Service) ParticipantStore

NewDocumentParticipantStore returns a ParticipantStore backed by db.

type Program

type Program struct {
	// Version identifies this policy revision, e.g. "2026-01".
	Version string

	// Currency is the ISO 4217 currency code all receipts, pools and
	// obligations are denominated in, e.g. "usd".
	Currency string

	// PoolBps is the share of covered net receipts allocated to the
	// monthly pool, in basis points (2000 = 20%).
	PoolBps int

	// CoveredReceipts is a human-readable definition of which receipts
	// are covered by the program.
	CoveredReceipts string

	// CreditDurationRounds is the number of monthly rounds an awarded
	// credit stays active, counted from its activation month.
	CreditDurationRounds int

	// PaymentTimetable is a human-readable description of when earned
	// entitlements become owed and are paid out.
	PaymentTimetable string

	// PayoutDueDays is the number of days after the end of the calendar
	// quarter containing a round in which its obligations become due.
	PayoutDueDays int

	// CalculationVersion is the allocation engine version this policy
	// was published against. Rounds refuse to close if it does not match
	// the engine's CalculationVersion.
	CalculationVersion int

	// EffectiveFrom is the first month this policy applies to.
	EffectiveFrom Month

	CreatedAt time.Time
	UpdatedAt time.Time
}

Program is a versioned policy of the contributor program. A Program is immutable once published: policy changes are new Program versions with a later EffectiveFrom month.

func (Program) ExpirationMonth

func (p Program) ExpirationMonth(activation Month) Month

ExpirationMonth returns the first month in which a credit activated in the given month is no longer active under this policy.

func (Program) Validate

func (p Program) Validate() error

Validate returns an error if the Program is not a usable policy.

type ProgramStore

type ProgramStore interface {
	// PutProgram stores the program, keyed by Program.Version.
	PutProgram(context.Context, Program) error

	// GetProgram returns the program with the given version.
	GetProgram(ctx context.Context, version string) (Program, error)

	// ProgramFor returns the policy in effect for month m: the program
	// with the latest EffectiveFrom that is not after m. It returns
	// document.ErrNotFound if no program is effective.
	ProgramFor(ctx context.Context, m Month) (Program, error)

	// ListPrograms lists all program versions.
	ListPrograms(context.Context) (iterator.Iterator[Program], error)
}

ProgramStore persists Program policy versions.

func NewDocumentProgramStore

func NewDocumentProgramStore(db document.Service) ProgramStore

NewDocumentProgramStore returns a ProgramStore backed by db.

type ProposeAwardRequest added in v1.84.0

type ProposeAwardRequest struct {
	// Splits assigns the award's credits to one or more recipients.
	Splits []HandleSplit

	// Activation is the first round in which the credits are active.
	Activation Month

	// Evidence lists public URLs backing the award. At least one is
	// required.
	Evidence []string

	// Description summarizes the contribution being awarded.
	Description string

	// ProposedBy identifies who proposed the award.
	ProposedBy string
}

ProposeAwardRequest describes an award to propose. Recipients are named by GitHub handle so operators never handle opaque account references.

type Receipt

type Receipt struct {
	// Source is the opaque, unique reference to the originating billing
	// record (e.g. an invoice identifier). It is the Receipt's identity.
	Source string

	// Month is the round the receipt is attributed to.
	Month Month

	// GrossCents is the gross amount in cents.
	GrossCents int64

	// Adjustments enumerates deductions from the gross amount.
	Adjustments []Adjustment

	// Currency is the ISO 4217 currency code, e.g. "usd". It must match
	// the program currency for the receipt to be covered.
	Currency string

	// Evidence is an opaque reference to supporting documentation.
	Evidence string

	CreatedAt time.Time
	UpdatedAt time.Time
}

Receipt records covered revenue attributed to a month. Receipts are imported from the billing system with an opaque source reference that doubles as their identity, making imports idempotent.

func (Receipt) NetCents

func (r Receipt) NetCents() int64

NetCents returns the gross amount minus all adjustments.

func (Receipt) Validate

func (r Receipt) Validate() error

Validate returns an error if the Receipt is not well formed.

type ReceiptStore

type ReceiptStore interface {
	// CreateReceipt stores a new receipt. It returns
	// document.ErrAlreadyExists if a receipt with the same source was
	// already imported.
	CreateReceipt(context.Context, Receipt) error

	// GetReceipt returns the receipt with the given source reference.
	GetReceipt(ctx context.Context, source string) (Receipt, error)

	// ListMonthReceipts lists the receipts attributed to month m.
	ListMonthReceipts(ctx context.Context, m Month) (
		iterator.Iterator[Receipt], error)
}

ReceiptStore persists Receipts, keyed by source reference so that imports are idempotent.

func NewDocumentReceiptStore

func NewDocumentReceiptStore(db document.Service) ReceiptStore

NewDocumentReceiptStore returns a ReceiptStore backed by db.

type Round

type Round struct {
	// Month identifies the round.
	Month Month

	// PolicyVersion is the Program.Version applied.
	PolicyVersion string

	// CalculationVersion is the engine version used to compute the
	// round.
	CalculationVersion int

	// Currency is the program currency the round is denominated in.
	Currency string

	// NetReceiptsCents is the sum of covered net receipts of the month.
	NetReceiptsCents int64

	// CarryforwardCents is the unallocated balance carried in from the
	// previous round.
	CarryforwardCents int64

	// PoolCents is the allocatable pool:
	// CarryforwardCents + NetReceiptsCents × PoolBps / 10000.
	PoolCents int64

	// TotalCredits is the allocation denominator: the sum of all active
	// credits in the round.
	TotalCredits int64

	// Entitlements are the per-participant results, ordered by account.
	Entitlements []Entitlement

	// UnallocatedCents is the part of the pool not allocated in this
	// round; it is carried forward into the next round. It is the whole
	// pool when TotalCredits is zero and zero otherwise.
	UnallocatedCents int64

	// ClosedAt is when the round was closed.
	ClosedAt time.Time
	// ClosedBy identifies who closed the round.
	ClosedBy string

	CreatedAt time.Time
	UpdatedAt time.Time
}

Round is a closed monthly allocation. All inputs are frozen at closing time so the calculation can be reproduced and verified independently (see VerifyRound). A closed Round is immutable: corrections are new events (e.g. adjustment receipts in a later round), never edits.

func (Round) AllocatedCents

func (r Round) AllocatedCents() int64

AllocatedCents returns the sum of the round's entitlement amounts.

type RoundStore

type RoundStore interface {
	// CreateRound stores a closed round. It returns
	// document.ErrAlreadyExists if the month is already closed.
	CreateRound(context.Context, Round) error

	// GetRound returns the closed round for month m.
	GetRound(ctx context.Context, m Month) (Round, error)

	// ListRounds lists all closed rounds.
	ListRounds(context.Context) (iterator.Iterator[Round], error)
}

RoundStore persists closed Rounds, keyed by month. Rounds are immutable: the interface deliberately offers no update operation.

func NewDocumentRoundStore

func NewDocumentRoundStore(db document.Service) RoundStore

NewDocumentRoundStore returns a RoundStore backed by db.

type Split

type Split struct {
	// Account is the recipient participant's account reference.
	Account string
	// Credits is the credit quantity assigned to the recipient.
	// Credits are allocation weights, not currency.
	Credits int64
}

Split assigns a share of an Award's credits to one recipient.

type Statement

type Statement struct {
	Program Program
	Round   Round
}

Statement is a non-sensitive export of a closed round together with the policy it was computed under. It contains everything needed to recompute the allocation: frozen aggregate inputs and per-account credits, but no receipts, payee identities or provider references.

Jump to

Keyboard shortcuts

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