Documentation
¶
Index ¶
- Constants
- Variables
- func ActionOutputsFor(db *sql.DB, stepID int, action string) ([]string, error)
- func AddLabelToIssue(db *sql.DB, issueID int, labelName, color string, author string) error
- func AddLabelsToIssue(db *sql.DB, issueID int, labelNames []string, color string, author string) error
- func AddRunIssue(db *sql.DB, runID, issueID int) error
- func AdvanceSagaTx(tx *sql.Tx, id int, from, to string, nowMS int64) error
- func AllProjectPrefixes(conn *sql.DB) ([]string, error)
- func AttachFiles(db *sql.DB, issueID int, filePaths []string, changedBy string) error
- func AuthorizeLeaseMutation(tx *sql.Tx, id int, token string, nowMS int64) error
- func AuthorizeStepRead(db *sql.DB, id int, token string, nowMS int64) error
- func AuthorizeStepTx(tx *sql.Tx, id int, token string, nowMS int64) (*model.Lease, error)
- func AutoRegisterEnabledTx(tx *sql.Tx, projectID int) (bool, error)
- func BindRunIssueTx(tx *sql.Tx, ri *RunIssue) error
- func BreachRunBudgetTx(tx *sql.Tx, runID int, reason string, nowMS int64) (bool, error)
- func BudgetDefaultTx(tx *sql.Tx, projectID int) (float64, error)
- func BudgetUnitTx(tx *sql.Tx, projectID int) (string, error)
- func CacheRunFloorTx(tx *sql.Tx, runID int, floor float64) error
- func CascadeDeleteIssue(db *sql.DB, id int) error
- func CheckAndBumpVersion(tx *sql.Tx, table string, id int, ifVersion *int) error
- func ClaimIssue(db *sql.DB, id int, owner string, ttlMS int64, nowMS int64) (token string, lease *model.Lease, err error)
- func ClaimStep(db *sql.DB, id int, owner string, ttlMS, nowMS int64) (string, *model.Lease, error)
- func ClaimStepTx(tx *sql.Tx, id int, owner string, ttlMS, nowMS int64) (token string, lease *model.Lease, err error)
- func ClaimStepWithRecoveryTx(tx *sql.Tx, id int, owner, recoverySecret string, ttlMS, nowMS int64) (token string, lease *model.Lease, err error)
- func ClassMaxOverridesTx(tx *sql.Tx, projectID int) (map[string]ClassMaxOverride, error)
- func ClearAllData(db *sql.DB) error
- func ClearAllDataTx(tx *sql.Tx) error
- func ClearProjectDataTx(tx *sql.Tx, projectID int) error
- func ClearRunBreachTx(tx *sql.Tx, runID int, newReason string, nowMS int64) error
- func ClearStaleBudgetReasonTx(tx *sql.Tx, runID int, newReason string, nowMS int64) error
- func ClearStepInputsTx(tx *sql.Tx, stepID int) error
- func ClearStepStartTx(tx *sql.Tx, id int) error
- func CloseDispatchTx(tx *sql.Tx, id int, status, reason string, nowMS int64) (bool, error)
- func CloseOpenProposalsTx(tx *sql.Tx, ids []int, reason string) (int, error)
- func CloseProposal(db *sql.DB, id int, reason string) error
- func CommitProposal(db *sql.DB, id int, outcome string, escalationReason string) error
- func CountActivity(db *sql.DB, issueID int) (int, error)
- func CountByPriority(db *sql.DB, projectID int) (map[string]int, error)
- func CountBySize(db *sql.DB, projectID int) (map[string]int, error)
- func CountByStatus(db *sql.DB, projectID int) (map[string]int, error)
- func CountIssues(db *sql.DB, projectID int) (int, error)
- func CountRootIssues(db *sql.DB, projectID int) (int, error)
- func CountSteps(db *sql.DB, runID int) (int, error)
- func CoverGateOverrideGrantsTx(tx *sql.Tx, ids []int) error
- func CreateComment(db *sql.DB, comment *model.Comment) (int, error)
- func CreateCommentIdempotent(db *sql.DB, comment *model.Comment, idempotencyKey string) (int, error)
- func CreateCommentIdempotentTx(tx *sql.Tx, comment *model.Comment, scope, idempotencyKey string) (int, bool, error)
- func CreateDoc(db *sql.DB, doc *model.Doc) (int, error)
- func CreateDocComment(db *sql.DB, c *model.DocComment) (int, error)
- func CreateDocCommentIdempotent(db *sql.DB, c *model.DocComment, idempotencyKey string) (int, error)
- func CreateDocIdempotent(db *sql.DB, doc *model.Doc, idempotencyKey string) (int, error)
- func CreateIssue(db *sql.DB, issue *model.Issue, labels []string, files []string) (int, error)
- func CreateIssueIdempotent(db *sql.DB, issue *model.Issue, labels []string, files []string, ...) (int, bool, error)
- func CreateProposal(db *sql.DB, p *model.Proposal) (int, error)
- func CreateProposalIdempotent(db *sql.DB, p *model.Proposal, idempotencyKey string) (int, error)
- func CreateRelation(db *sql.DB, rel *model.Relation) (int, error)
- func DefaultProjectIDOr(id int) int
- func DeleteDoc(db *sql.DB, id int, cascade bool) error
- func DeleteIssue(db *sql.DB, id int) error
- func DeleteLabel(db *sql.DB, labelID int, name, author string) ([]int, error)
- func DeleteProject(conn *sql.DB, id int) error
- func DeleteRelation(db *sql.DB, sourceID, targetID int, relType string) error
- func DeprecateSchema(db *sql.DB, projectID int, name string, version int, nowMS int64) (*model.Schema, error)
- func DeprecateWorkflow(db *sql.DB, projectID int, name string, version int, nowMS int64) (*model.Workflow, error)
- func DerivePrefix(name string) string
- func DetachFiles(db *sql.DB, issueID int, filePaths []string, changedBy string) error
- func DispatchGraceTx(tx *sql.Tx, projectID int) (time.Duration, error)
- func DispatchTTLTx(tx *sql.Tx, projectID int) (time.Duration, error)
- func DoneTransitionClearsResolution(prior, target model.Status, explicitResolution bool) bool
- func EnsureProject(conn *sql.DB, identity, name string, nowMS int64) (int, error)
- func EnsureProjectCreated(conn *sql.DB, identity, name string, nowMS int64) (int, bool, error)
- func EventsRetain(db *sql.DB) (time.Duration, error)
- func ExemptStepAttemptFromBudgetTx(tx *sql.Tx, id int, nowMS int64) error
- func ExtendDispatchTx(tx *sql.Tx, id int, expiresMS, extendedSeq int64) (bool, error)
- func GetActivity(db *sql.DB, issueID int, limit int) ([]model.Activity, error)
- func GetAllDirectionalRelations(db *sql.DB) ([]model.Relation, error)
- func GetAllRelations(db *sql.DB, projectID int) ([]model.Relation, error)
- func GetBatchSubIssueProgress(conn *sql.DB, parentIDs []int) (map[int][2]int, error)
- func GetComment(db *sql.DB, id int) (*model.Comment, error)
- func GetDoc(db *sql.DB, id int) (*model.Doc, error)
- func GetDocComment(db *sql.DB, id int) (*model.DocComment, error)
- func GetDocIssues(db *sql.DB, docID int) ([]int, error)
- func GetDocProposals(db *sql.DB, docID int) ([]int, error)
- func GetDocRevision(db *sql.DB, docID, rev int) (*model.DocRevision, error)
- func GetIssue(db *sql.DB, id int) (*model.Issue, error)
- func GetIssueDocs(db *sql.DB, issueID int) ([]int, error)
- func GetIssueFiles(db *sql.DB, issueID int) ([]string, error)
- func GetIssueFilesTx(tx *sql.Tx, issueID int) ([]string, error)
- func GetIssueLabelObjects(db *sql.DB, issueID int) ([]*model.Label, error)
- func GetIssueLabels(db *sql.DB, issueID int) ([]string, error)
- func GetIssueLease(db *sql.DB, id int) (*model.Lease, error)
- func GetIssueProposals(db *sql.DB, issueID int) ([]model.Proposal, error)
- func GetIssueRelations(db *sql.DB, issueID int) ([]model.Relation, error)
- func GetIssuesByIDs(db *sql.DB, ids []int) (map[int]*model.Issue, error)
- func GetLabelByName(db *sql.DB, projectID int, name string) (*model.LabelWithCount, error)
- func GetProject(conn *sql.DB, id int) (*model.Project, error)
- func GetProposal(db *sql.DB, id int) (*model.Proposal, error)
- func GetProposalDocs(db *sql.DB, proposalID int) ([]int, error)
- func GetProposalIssues(db *sql.DB, proposalID int) ([]int, error)
- func GetProposalTx(tx *sql.Tx, id int) (*model.Proposal, error)
- func GetProposalVoteUsageSources(db *sql.DB, proposalID int) (map[int]map[string]string, error)
- func GetProposalVotes(db *sql.DB, proposalID int) ([]*model.Vote, error)
- func GetProposalVotesTx(tx *sql.Tx, proposalID int) ([]*model.Vote, error)
- func GetRun(db *sql.DB, id int) (*model.Run, error)
- func GetRunTx(tx *sql.Tx, id int) (*model.Run, error)
- func GetSchema(db *sql.DB, projectID int, name string, version int) (*model.Schema, error)
- func GetSchemaTx(tx *sql.Tx, projectID int, name string, version int) (*model.Schema, error)
- func GetStepLease(db *sql.DB, id int) (*model.Lease, error)
- func GetSubIssueProgress(db *sql.DB, parentID int) (int, int, error)
- func GetSubIssueTree(db *sql.DB, parentID int) ([]*model.Issue, error)
- func GetSubIssues(db *sql.DB, parentID int) ([]*model.Issue, error)
- func GetVersion(db *sql.DB, table string, id int) (int, error)
- func GetWorkflow(db *sql.DB, projectID int, name string, version int) (*model.Workflow, error)
- func GrantLoopTx(tx *sql.Tx, runID, issueID int) (int, error)
- func HasGateResult(conn *sql.DB, stepID int, gate string) (bool, error)
- func HeartbeatIssue(db *sql.DB, id int, token string, ttlMS int64, nowMS int64) (*model.Lease, error)
- func HeartbeatStep(db *sql.DB, id int, token string, ttlMS, nowMS int64) (*model.Lease, error)
- func HydrateDocs(db *sql.DB, issues []*model.Issue) error
- func HydrateFiles(db *sql.DB, issues []*model.Issue) error
- func HydrateLabels(db *sql.DB, issues []*model.Issue) error
- func HydrateLinkedIssues(db *sql.DB, docIDs []int) (map[int][]model.IssueRef, error)
- func IdempotencyKeyOf(db *sql.DB, scope string, entityID int) (string, bool, error)
- func IncrementLoopCountTx(tx *sql.Tx, runID, issueID int) (int, error)
- func Initialize(db *sql.DB) error
- func InsertActionResultTx(tx *sql.Tx, r ActionResultRow) error
- func InsertActivityWithID(tx *sql.Tx, a *model.Activity) (bool, error)
- func InsertArtifactTx(tx *sql.Tx, a Artifact, nowMS int64) (int, error)
- func InsertCommentWithID(tx *sql.Tx, comment *model.Comment) (bool, error)
- func InsertDispatchRowTx(tx *sql.Tx, dispatchID int, row DispatchRow) error
- func InsertDispatchTx(tx *sql.Tx, runID int, openedSeq, expiresMS, nowMS int64) (int, error)
- func InsertDocCommentWithID(tx *sql.Tx, c *model.DocComment) (bool, error)
- func InsertDocIssueLink(tx *sql.Tx, docID, issueID int, createdAt string) (bool, error)
- func InsertDocRevisionWithID(tx *sql.Tx, r *model.DocRevision) (bool, error)
- func InsertDocWithID(tx *sql.Tx, doc *model.Doc) (bool, error)
- func InsertEngineComment(tx *sql.Tx, issueID int, body string, nowMS int64) (int, error)
- func InsertFenceTx(tx *sql.Tx, f RunFence) error
- func InsertGapIssueTx(tx *sql.Tx, projectID int, gap GapIssue, relatedIssueID int) (int, error)
- func InsertGateOverrideGrantTx(tx *sql.Tx, g GateOverrideGrant) (int, error)
- func InsertGateResultTx(tx *sql.Tx, r GateResultRow) error
- func InsertIssueFileMapping(tx *sql.Tx, issueID int, filePath string) (bool, error)
- func InsertIssueLabelMapping(tx *sql.Tx, issueID, labelID int) (bool, error)
- func InsertIssueWithID(tx *sql.Tx, issue *model.Issue) (bool, error)
- func InsertLabelWithID(tx *sql.Tx, label *model.Label) (bool, error)
- func InsertNoteTx(tx *sql.Tx, issueID int, body, author, now string) (int, error)
- func InsertPinTx(tx *sql.Tx, p Pin) error
- func InsertProposalDocLink(tx *sql.Tx, proposalID, docID int, createdAt string) (bool, error)
- func InsertProposalIssueLink(tx *sql.Tx, proposalID, issueID int) (bool, error)
- func InsertProposalWithID(tx *sql.Tx, p *model.Proposal) (bool, error)
- func InsertReapAckTx(tx *sql.Tx, ack ReapAck, nowMS int64) error
- func InsertRelationWithID(tx *sql.Tx, rel *model.Relation) (bool, error)
- func InsertRun(db *sql.DB, projectID int, request string, budget float64, nowMS int64) (*model.Run, error)
- func InsertRunNoteTx(tx *sql.Tx, n RunNote) (int, error)
- func InsertRunWithContext(db *sql.DB, projectID int, request string, budget float64, nowMS int64, ...) (*model.Run, error)
- func InsertRunWithContextIdempotent(db *sql.DB, projectID int, request string, budget float64, nowMS int64, ...) (run *model.Run, inserted bool, err error)
- func InsertSchema(db *sql.DB, s *model.Schema, nowMS int64) (stored *model.Schema, created bool, err error)
- func InsertSchemaTx(tx *sql.Tx, s *model.Schema, nowMS int64) (stored *model.Schema, created bool, err error)
- func InsertStaleTargetWaiverTx(tx *sql.Tx, w StaleTargetWaiver) (int, error)
- func InsertStepInputTx(tx *sql.Tx, stepID, position, artifactID int) error
- func InsertStepTx(tx *sql.Tx, s StepRow, nowMS int64) error
- func InsertTrustCacheTx(tx *sql.Tx, runID int, kind, gate, argvSHA256, entryName string, ...) error
- func InsertUsageRowTx(tx *sql.Tx, row UsageRow, nowMS int64) error
- func InsertVoteUsageTx(tx *sql.Tx, voteID int64, unit string, quantity float64, source string, ...) error
- func InsertVoteWithID(tx *sql.Tx, v *model.Vote) (bool, error)
- func InsertWorkflow(db *sql.DB, wf *model.Workflow, nowMS int64) (stored *model.Workflow, created bool, err error)
- func InsertWorkflowTx(tx *sql.Tx, wf *model.Workflow, nowMS int64) (stored *model.Workflow, created bool, err error)
- func IsDescendant(db *sql.DB, issueID, potentialDescendantID int) (bool, error)
- func IssueExists(db *sql.DB, issueID int) (bool, error)
- func IssueOwnerPrefix(conn *sql.DB, issueID int) (string, error)
- func IssueProjectID(db *sql.DB, issueID int) (int, error)
- func IssueScopeGlobs(db *sql.DB, issueID int) (string, error)
- func IssueScopeGlobsTx(tx *sql.Tx, issueID int) (string, error)
- func IssueStatusResolutionTx(tx *sql.Tx, issueID int) (model.Status, string, error)
- func IssueStepRuns(db *sql.DB, issueID int) ([]int, error)
- func KnownConfigKeys() []string
- func LeaseTTL(db *sql.DB, projectID int, class string) (time.Duration, error)
- func LinkDocIssue(db *sql.DB, docID, issueID int) error
- func LinkProposalDoc(db *sql.DB, proposalID, docID int) error
- func LinkProposalIssue(db *sql.DB, proposalID, issueID int) error
- func ListAllActivity(db *sql.DB, projectID int) ([]*model.Activity, error)
- func ListAllComments(db *sql.DB, projectID int) ([]*model.Comment, error)
- func ListAllDocComments(db *sql.DB, projectID int) ([]*model.DocComment, error)
- func ListAllDocIssueLinks(db *sql.DB, projectID int) ([]model.DocIssueLink, error)
- func ListAllDocRevisions(db *sql.DB, projectID int) ([]*model.DocRevision, error)
- func ListAllDocs(db *sql.DB, projectID int) ([]*model.Doc, error)
- func ListAllIssueFileMappings(db *sql.DB, projectID int) ([]model.IssueFileMapping, error)
- func ListAllIssueLabelMappings(db *sql.DB, projectID int) ([]model.IssueLabelMapping, error)
- func ListAllIssues(db *sql.DB, projectID int) ([]*model.Issue, error)
- func ListAllLabels(db *sql.DB, projectID int) ([]*model.LabelWithCount, error)
- func ListAllLabelsRaw(db *sql.DB, projectID int) ([]*model.Label, error)
- func ListAllProposalDocs(db *sql.DB, projectID int) ([]model.ProposalDocLink, error)
- func ListAllProposalIssues(db *sql.DB, projectID int) ([]model.ProposalIssueLink, error)
- func ListAllProposals(db *sql.DB, projectID int) ([]*model.Proposal, error)
- func ListAllVotes(db *sql.DB, projectID int) ([]*model.Vote, error)
- func ListComments(db *sql.DB, issueID int) ([]*model.Comment, error)
- func ListDocComments(db *sql.DB, docID int) ([]*model.DocComment, error)
- func ListDocRevisions(db *sql.DB, docID int) ([]*model.DocRevision, error)
- func ListDocs(db *sql.DB, opts DocListOptions) ([]*model.Doc, int, error)
- func ListIssues(db *sql.DB, opts ListOptions) ([]*model.Issue, int, error)
- func ListProjects(conn *sql.DB) ([]*model.Project, error)
- func ListProposals(db *sql.DB, projectID int, status string, criticality string, domainTag string, ...) ([]*model.Proposal, int, error)
- func ListRuns(db *sql.DB, opts RunListOptions) ([]*model.Run, int, error)
- func ListSchemas(db *sql.DB, opts SchemaListOptions) ([]*model.Schema, int, error)
- func ListWorkflows(db *sql.DB, opts WorkflowListOptions) ([]*model.Workflow, int, error)
- func LookupIdempotencyKey(db *sql.DB, scope, key string) (int, bool, error)
- func LookupIdempotencyKeyTx(tx *sql.Tx, scope, key string) (int, bool, error)
- func LookupIdempotencyKeys(db *sql.DB, scope, prefix string) (map[string]int, error)
- func LookupIdempotencyKeysTx(tx *sql.Tx, scope, prefix string) (map[string]int, error)
- func LookupProject(conn *sql.DB, identity string) (int, bool, error)
- func LoopGrantsTx(tx *sql.Tx, runID, issueID int) (int, error)
- func MarkExpandedTx(tx *sql.Tx, runID, issueID int, nowMS int64) error
- func MarkStepAttemptFailedTx(tx *sql.Tx, id int, nowMS int64) error
- func MarkStepClaimReapedTx(tx *sql.Tx, id int, nowMS int64) error
- func MarkStepUsageRecordedTx(tx *sql.Tx, stepID int) error
- func Migrate(db *sql.DB) error
- func MoveIssueProject(conn *sql.DB, issueID, targetProjectID int, author string, nowMS int64) ([]int, error)
- func NextActionOrdinalTx(tx *sql.Tx, stepID int, action string) (int, error)
- func NextGateOrdinal(conn *sql.DB, stepID int, gate string) (int, error)
- func Open(dbPath string) (*sql.DB, error)
- func OpenReader(dbPath string) (*sql.DB, error)
- func OrphanSubIssues(db *sql.DB, parentID int, author string) error
- func PrefixHolder(conn *sql.DB, prefix string, exclude int) (int, error)
- func ProjectPrefix(conn *sql.DB, id int) (string, error)
- func ProjectRefCounts(conn *sql.DB, id int) (map[string]int, error)
- func ProposalIDsNaming(db *sql.DB, token string) ([]int, error)
- func ProposalStatusesTx(tx *sql.Tx, ids []int) (map[int]model.ProposalStatus, error)
- func ReMintStepTokenTx(tx *sql.Tx, id int, owner string, nowMS int64) (token string, err error)
- func ReapStepTx(tx *sql.Tx, id int, nowMS int64) error
- func RecordActivity(ex execer, issueID int, field, oldVal, newVal, changedBy string) error
- func RecordIdempotencyKeyTx(tx *sql.Tx, scope, key string, entityID int) error
- func RefreshClaimLeaseTx(tx *sql.Tx, id int, tokenHash string, ttlMS, nowMS int64) (bool, error)
- func RegisteredVoteRules(db *sql.DB, projectID int) ([]string, error)
- func RegisteredVoteRulesTx(tx *sql.Tx, projectID int) ([]string, error)
- func ReleaseIssue(db *sql.DB, id int, token string, nowMS int64) (*model.Lease, error)
- func ReleaseStepLeaseTx(tx *sql.Tx, id int, nowMS int64) error
- func RemoveLabelFromIssue(db *sql.DB, issueID int, labelName string, author string) error
- func RemoveLabelsFromIssue(db *sql.DB, issueID int, labelNames []string, author string) error
- func RemoveRunIssue(db *sql.DB, runID, issueID int) error
- func RenderMetadataValue(v any) string
- func ReportedUsageTx(tx *sql.Tx, runID int, unit string) (float64, error)
- func ResetStepRetryBudgetTx(tx *sql.Tx, id int, nowMS int64) error
- func RestoreSchema(db *sql.DB, projectID int, name string, version int) (*model.Schema, error)
- func RestoreWorkflow(db *sql.DB, projectID int, name string, version int) (*model.Workflow, error)
- func RetireStepTokenTx(tx *sql.Tx, id int) error
- func RoutingRecord(routing, reason string) string
- func RunConductorHash(db *sql.DB, id int) (string, error)
- func RunConductorHashTx(tx *sql.Tx, id int) (string, error)
- func RunEverDispatchedTx(tx *sql.Tx, runID int) (bool, error)
- func RunPauseOriginTx(tx *sql.Tx, id int) (model.RunPauseOrigin, error)
- func RunProjectID(conn *sql.DB, runID int) (int, error)
- func RunProjectIDTx(tx *sql.Tx, runID int) (int, error)
- func SchemaVersion(db *sql.DB) (int, error)
- func SetConfig(db *sql.DB, projectID int, key, value string) error
- func SetConfigTx(tx *sql.Tx, projectID int, key, value string) (prior string, err error)
- func SetIssueFiles(db *sql.DB, issueID int, filePaths []string, changedBy string) error
- func SetIssueResolutionTx(tx *sql.Tx, issueID int, resolution string) error
- func SetIssueScopeGlobs(db *sql.DB, issueID int, globsJSON string) error
- func SetProjectPrefix(conn *sql.DB, id int, prefix string) error
- func SetRunBudgetTx(tx *sql.Tx, runID int, budget float64, ifVersion *int, nowMS int64) error
- func SetRunConductorHashTx(tx *sql.Tx, id int, hash string) error
- func SetRunIssueBodyTx(tx *sql.Tx, runID, issueID int, body, sha string) error
- func SetRunIssueSnapshotTx(tx *sql.Tx, runID, issueID int, snapshot string) error
- func SetRunPauseOriginTx(tx *sql.Tx, id int, origin model.RunPauseOrigin) error
- func SetRunStatus(db *sql.DB, id int, status model.RunStatus, reason string, nowMS int64) error
- func SetRunStatusTx(tx *sql.Tx, id int, status model.RunStatus, reason string, nowMS int64) error
- func SetStepAuthorityTx(tx *sql.Tx, id int, authority, authorityRef string, nowMS int64) error
- func SetStepClaimPhaseTx(tx *sql.Tx, id int, phase string, nowMS int64) error
- func SetStepGateTrailTx(tx *sql.Tx, id int, trail string, nowMS int64) error
- func SetStepLoopHistoryTx(tx *sql.Tx, id, roundsRun int, triggerStep, latestVerdict string, nowMS int64) error
- func SetStepLoopHistoryWithCapTx(tx *sql.Tx, id, roundsRun, loopCap int, triggerStep, latestVerdict string, ...) error
- func SetStepMetadataTx(tx *sql.Tx, id int, metadata string, nowMS int64) error
- func SetStepRoutingTx(tx *sql.Tx, id int, routing, reason, status string, class ParkClass, ...) error
- func SetStepRoutingWithParkReasonTx(tx *sql.Tx, id int, routing, reason, status string, class ParkClass, ...) error
- func SetStepStatusTx(tx *sql.Tx, id int, status string, nowMS, activityMS int64) error
- func SortedUnits[V any](byUnit map[string]V) []string
- func SplitNameList(value string) []string
- func StartStepTx(tx *sql.Tx, id int, nowMS int64) error
- func StatusReachedAt(db *sql.DB, ids []int, status string) (map[int]time.Time, error)
- func StepAttemptsFor(db *sql.DB, runID int) (map[int]int, error)
- func StepInputCount(db *sql.DB, stepID int) (int, error)
- func StepOffScheduler(status string) bool
- func StepStatusCounts(db *sql.DB, runID int) ([]model.StatusCount, error)
- func StepTerminal(status string) bool
- func UnlinkDocIssue(db *sql.DB, docID, issueID int) error
- func UnlinkProposalDoc(db *sql.DB, proposalID, docID int) error
- func UnlinkProposalIssue(db *sql.DB, proposalID, issueID int) error
- func UpdateDoc(db *sql.DB, id int, upd DocUpdate) (int, error)
- func UpdateIssue(db *sql.DB, id int, updates map[string]interface{}, changedBy string) error
- func UpdateIssueCAS(db *sql.DB, id int, updates map[string]interface{}, changedBy string, ...) error
- func UpdateIssueCASLease(db *sql.DB, id int, updates map[string]interface{}, changedBy string, ...) error
- func UpdateIssueCASNote(db *sql.DB, id int, updates map[string]interface{}, changedBy string, ...) error
- func UsageBudgetUnitTx(tx *sql.Tx, projectID int) (string, error)
- func ValidateConfigValue(spec ConfigSpec, value string) error
- func ValidateNameList(noun, value string) error
- func ValidateUnitName(name string) error
- func VoteRuleCriticalityKey(rule string) string
- func VoteRuleExists(db *sql.DB, projectID int, rule string) (bool, error)
- func VoteRuleExistsTx(tx *sql.Tx, projectID int, rule string) (bool, error)
- func VoteRuleHoldOnDissentKey(rule string) string
- func VoteRuleSealedKey(rule string) string
- func VoteRuleSetElsewhere(db *sql.DB, projectID int, rule string) (projects int, value string, err error)
- func VoteRuleSetElsewhereTx(tx *sql.Tx, projectID int, rule string) (projects int, value string, err error)
- func VoteRuleThresholdKey(rule string) string
- type AckReapResult
- type ActionResultRow
- type Artifact
- func GetArtifactTx(tx *sql.Tx, id int) (*Artifact, error)
- func ListRunArtifacts(db *sql.DB, runID int) ([]*Artifact, error)
- func ListRunArtifactsTx(tx *sql.Tx, runID int) ([]*Artifact, error)
- func ListStepArtifacts(db *sql.DB, stepID int) ([]*Artifact, error)
- func ListStepInputArtifactsTx(tx *sql.Tx, stepID int) ([]*Artifact, error)
- type CastVoteResult
- type ClassMaxOverride
- type ConfigEntry
- type ConfigSpec
- type ConfigValueKind
- type CycleError
- type Dispatch
- type DispatchRow
- type DocListOptions
- type DocSummary
- type DocUpdate
- type GapIssue
- type GateOverrideGrant
- type GateResultRow
- type ListOptions
- type MetadataKeyRollup
- type MetadataValueCount
- type ParkClass
- type Pin
- type ProposalRunLink
- type ProposalRunLinkFilter
- type ReapAck
- type ResultTrailRow
- type RunBudgetFacts
- type RunContext
- type RunFence
- type RunIssue
- type RunListOptions
- type RunNote
- type SchemaListOptions
- type SilentVoteSeatRow
- type StaleTargetWaiver
- type Step
- func GetStep(db *sql.DB, id int) (*Step, error)
- func GetStepTx(tx *sql.Tx, id int) (*Step, error)
- func ListActiveRunSteps(db *sql.DB) ([]*Step, error)
- func ListIssueStepsTx(tx *sql.Tx, runID, issueID int) ([]*Step, error)
- func ListRunSteps(db *sql.DB, runID int) ([]*Step, error)
- func ListRunStepsTx(tx *sql.Tx, runID int) ([]*Step, error)
- type StepRow
- type StepUsageRow
- type UnitTotal
- type UsageRow
- type VerdictCount
- type VoteUsageCoverage
- type WorkflowListOptions
- type WorkflowVersion
Constants ¶
const ( ActionVerdictPass = "pass" ActionVerdictFail = "fail" ActionVerdictUnmatched = "unmatched" )
Action verdicts as stored, mirroring the gate vocabulary exactly. `unmatched` is a FIRST-CLASS outcome: the action name matched no trust entry, so nothing ran — which is neither a pass nor an error.
const ( TrustKindGate = "gate" TrustKindAction = "action" )
Trust-cache kinds. `trust_cache` gains a `kind` column rather than a second table: the table answers "what did this run consider trusted, and when", and splitting that answer by the SHAPE OF THE CALLER would make the one question two queries.
const ( // DispatchOpen is the state `idx_dispatches_one_open` admits one of per run. DispatchOpen = "open" // DispatchClosed is `dispatch close`: the relay reconciled its batch. DispatchClosed = "closed" // DispatchAbandoned is `dispatch abandon` or the TTL's lazy auto-abandon: // the batch was given up on rather than reconciled. DispatchAbandoned = "abandoned" )
Dispatch statuses. A dispatch is `open` until exactly one of the two closing paths moves it, and the status is what the partial unique index keys on — so these strings are load-bearing rather than decorative.
const ( // CloseReasonTTL is the lazy auto-abandon: the manifest outlived // `dispatch.ttl` and `next` retired it. CloseReasonTTL = "ttl" // CloseReasonReconciled is the ordinary `dispatch close`. CloseReasonReconciled = "reconciled" // CloseReasonAcceptedMissingUsage: closed over missing-usage // discrepancies, with the acceptance RECORDED rather than implied. CloseReasonAcceptedMissingUsage = "accepted-missing-usage" // CloseReasonOperator is `dispatch abandon` driven by a person. CloseReasonOperator = "abandoned" )
Close reasons core writes. They are a small closed vocabulary because `run report` and `events list` render them, and a free-text reason from the engine's own paths would make the feed inconsistent with itself. A caller's `--reason` on `abandon` rides in the EVENT's data, not here.
const ( // AckByGuardSpawn is `guard spawn --ack-reap`. AckByGuardSpawn = "guard-spawn" // AckByDispatchOpen is `dispatch open --ack-reap`. AckByDispatchOpen = "dispatch-open" // AckByDispatchClose is a reconciled `dispatch close` acknowledging a // NON-FORCED reap of a claim admitted under that same dispatch // (DKT-3286). Abandon, forced reaps, and claims from another dispatch keep // the hold. AckByDispatchClose = "dispatch-close" )
Acknowledgers — a closed set. The value records the acknowledging VERB and never a user identity, because core has no identity model.
const ( // KeyLeaseTTLDefault is the fallback lease TTL for any class without its // own entry. KeyLeaseTTLDefault = "lease.ttl.default" // KeyLeaseTTLPrefix is the per-class TTL namespace: lease.ttl.<class>. // The class is an opaque string — core never interprets its value. KeyLeaseTTLPrefix = "lease.ttl." // KeyClassMaxPrefix is the per-class concurrency cap namespace: // limits.max.<class>. A set value replaces the pinned workflows' merged // `[limits]` max for that class. The scheduler reads it on every snapshot, // never pins it, so a run moved to a different host follows that host's // value. Unset leaves the pinned max in force. KeyClassMaxPrefix = "limits.max." // KeyAttemptMax caps retries per entity (`max_attempts`). KeyAttemptMax = "attempt.max" // KeyBudgetDefault is the per-run budget cap; 0 means unlimited. // Enforcement lands with runs; the key exists now so the default is // pinned before the verbs that read it. KeyBudgetDefault = "budget.default" // KeyBudgetUnit names WHICH recorded usage unit the run cap counts. Empty — // the default and the only value core ships — means `reported` is 0 and // the cap rests on the declared-cost floor alone. // // A config key rather than a workflow field because the cap is a run-level // control, so the unit it counts is run-level too. Putting it on a step // would let two steps in one run disagree about what the run's budget // means. KeyBudgetUnit = "budget.unit" // KeyUsageBudgetDefault is the default cap over MEASURED usage — the // second budget dimension (DKT-238); 0 means unlimited. // // It is a SEPARATE key from budget.default because the two count different // things and are not commensurable. `budget.default` counts declared // expected costs, which is a discipline over how much WORK a run schedules; // this counts what the ledger recorded, which is a bound on what the work // actually consumed. A run wants both, and one number cannot say both. KeyUsageBudgetDefault = "budget.usage.default" // KeyUsageBudgetUnit names WHICH recorded usage unit the measured cap // counts. Empty — the default — leaves the dimension DORMANT: a cap with // no unit has nothing to count, so it enforces nothing. // // Deliberately distinct from budget.unit. That key names the unit whose // reported total may RAISE the declared-cost spend; this one names the // unit the measured cap is taken over. Sharing one key would make an // operator setting a token unit for one dimension silently arm the other. KeyUsageBudgetUnit = "budget.usage.unit" // KeyDispatchTTL is how long a dispatch manifest stays open before `next` // auto-abandons it. // // It is engine-side rather than instance policy for the reason that holds // for every other enforced number: the expiry is what keeps a crashed relay // from wedging a run, and a bound only a live relay could set would be a // bound the crash case never gets. KeyDispatchTTL = "dispatch.ttl" // KeyDispatchGrace is how long a claimed step may go unrecorded before it // counts as a dispatch discrepancy. // // It is DELIBERATELY a different key from `lease.ttl.default` even though // both measure silence. A lease TTL decides when the engine takes work back; // this decides when a relay's batch is judged unreconciled. Sharing one key // would make an operator lengthening leases silently stop `next` refusing. KeyDispatchGrace = "dispatch.grace" // KeyContextWarnBytes and KeyContextErrorBytes are the context-size caps. KeyContextWarnBytes = "context.warn_bytes" KeyContextErrorBytes = "context.error_bytes" // KeyEventsRetain is the RETENTION WINDOW: how long an event must have // existed before `events prune` may delete it. // // It is the "artifact-retention boundary", implemented as the one window a // future artifact GC would also read. Prune and "artifact GC per // run-retention config" are one lifecycle, and no artifact GC ships — so // read literally there would be no boundary to cross. Implementing it as // this window means the GC inherits a boundary already enforced rather // than one retrofitted, and the reading can only ever REFUSE MORE than the // literal one would. // // THE READING IS DELIBERATE, and it carries a known objection: a key // spelled `events.retain` governing only events is not what // "artifact-retention" names. // // The default is "0", which imposes NO RETENTION WINDOW: prune at 0 is // bounded only by its `--before` or `--before-run` target and the live-run // refusal, not by age. That is the dormant posture — nothing is deleted // until an operator runs prune with a target. KeyEventsRetain = "events.retain" // KeyVoteRulePrefix is the named-threshold-configuration namespace: // vote.rule.<name>.threshold, vote.rule.<name>.criticality, // vote.rule.<name>.sealed and vote.rule.<name>.hold_on_dissent. // // A workflow's `type="vote"` step names a rule rather than passing flags, // because a step cannot pass flags. The <name> is an OPAQUE string exactly // as lease.ttl.<class>'s class is, and this reuses the config machinery // rather than adding a table: a rule "exists" iff its `.threshold` is set. KeyVoteRulePrefix = "vote.rule." // KeyVoteRuleThresholdSuffix, KeyVoteRuleCriticalitySuffix, // KeyVoteRuleSealedSuffix and KeyVoteRuleHoldOnDissentSuffix complete a // rule's four keys. // // `.sealed` is opt-in and defaults to false: a sealed rule opens proposals // whose casts the read verbs withhold (verdict, weights, findings, summary) // until the tally closes the proposal, so a seat reading the ballot cannot // anchor on a sibling's verdict. It is a RENDERING rule only — the tally // and the one-cast-per-voter constraint never consult it. // // `.hold_on_dissent` is opt-in and defaults to false (DKT-2449): under a // keyed rule an APPROVED tally that carries at least one `reject` parks // the vote step for the operator instead of passing. The tally itself is // untouched — the weighted mean is still db.CastVote's — and the park is // strictly additive: it displaces a `pass` and never a fail route. KeyVoteRuleThresholdSuffix = ".threshold" KeyVoteRuleCriticalitySuffix = ".criticality" KeyVoteRuleSealedSuffix = ".sealed" KeyVoteRuleHoldOnDissentSuffix = ".hold_on_dissent" // KeyVoteHoldRule and KeyVoteHoldVoters configure how a MATERIALIZED HELD // step is decided: by one operator (the default) or by a tally. // // A held step is the one step in a run no author declared — the engine // mints it when a `hold_spread` trips, so its `voters` and `vote_rule` // cannot come from a `[[step]]` table the way a declared vote step's do. // These two keys are where an instance says them instead, and they sit in // the `vote.` family beside `vote.rule.<name>` because that is the same // subject: how a vote is tallied and who casts it. // // BOTH ARE EMPTY BY DEFAULT, and empty means EXACTLY the prior behavior — // held steps are minted `human` and one operator approves or rejects them. // A tally is something an instance opts into, never something core assumes, // for the same reason core ships no default threshold: a roster nobody // chose is not a roster. // // They are ONE PAIR rather than per-workflow settings because a hold is the // engine's own question about its own computation. It is asked identically // whichever pipeline held, so who answers it is a project-level policy. KeyVoteHoldRule = "vote.hold.rule" KeyVoteHoldVoters = "vote.hold.voters" // KeyVoteHoldCost is the declared `expected_cost` a MATERIALIZED HELD vote // step is minted with (DKT-584). An engine-minted held ballot has no // `[[step]]` table to declare a cost in, so it carried 0 by construction — // and since a vote step's declared cost accrues to the budget floor at // materialization, that made every held panel invisible to the floor. // // Default "0", which is EXACTLY the prior behavior: a held ballot accrues // nothing until an instance states what its panels cost. It applies only // when a hold is minted as `vote` (both vote.hold.* keys set); a hold // minted `human` is one operator's decision, not a panel's spend. KeyVoteHoldCost = "vote.hold.cost" // KeyRoutingSecurityLabel names the label that sends an issue down the // security review track, and KeyRoutingSecurityLexicon the terms that make // an UNLABELLED issue look like it belongs there (DKT-4275). `run activate // --dry-run` lints against both; nothing else reads them, and neither // changes routing. The lexicon ships empty because the engine attaches no // domain meaning to a word: the instance that knows its own security // surface supplies the vocabulary. An empty label turns the lint off. KeyRoutingSecurityLabel = "routing.security_label" KeyRoutingSecurityLexicon = "routing.security_lexicon" // KeyAutoRegister toggles auto-registration: whether `run activate` // registers a workflow/schema it finds in an instance-config root // (~/.docket/config, <repo>/.docket/config) on its own, or leaves that to // an explicit `workflow register` / `schema register`. // // DEFAULT TRUE — auto-registration is the zero-touch behavior it exists // for, and an operator who wants it off states that, rather than every // operator who wants it (the common case) opting in project by project. // // Project-scoped, like any other config key (v12): `--global` sets the // store-wide default every project without its own override falls back // to, and a bare `config set` overrides ONE project — the two knobs the // requirement asks for ("a given project vs all projects") are exactly // GetConfig's existing project-override-then-store-wide resolution, not a // second mechanism. // // It gates ONLY registration, not the pinning half of the same scan // (contracts/, fragments/, policy.toml). Pinning has no version to adopt — // it is "read the current bytes", which is what a repo with this off // still needs to render a step's `packet`. Turning registration off and // pinning off together would leave a project unable to activate at all // without hand-supplying every `--pin` the corpus already offers, which is // not what "I don't want silent version upgrades" is asking for. KeyAutoRegister = "registration.auto" )
Engine configuration keys: lease TTLs per class, attempt caps, budget default, context caps. Values live in the meta table, prefixed so they cannot collide with schema_version or any other internal key.
const ( GateVerdictPass = "pass" GateVerdictFail = "fail" GateVerdictUnmatched = "unmatched" // GateVerdictSkipped: the gate's tree was gone at spawn time, so nothing // ran and nothing was measured (DKT-169). Not a pass for routing. GateVerdictSkipped = "skipped" )
Gate verdicts as stored. `unmatched` is a FIRST-CLASS outcome, not an error and not a pass: the command was not trusted, so it did not run.
const ( ScopeIssueCreate = "issue.create" ScopeDocCreate = "doc.create" ScopeVoteCreate = "vote.create" ScopeIssueComment = "issue.comment.add" // ScopeStepComment keys the comment a step records with `step complete // --comment-file`. No verb takes a caller-supplied key in it, so a key // cannot be pre-seeded to suppress a step's comment. ScopeStepComment = "step.comment" ScopeDocComment = "doc.comment.add" ScopeRunStart = "run.start" )
Idempotency scopes. One per create verb, so the same key used on two different verbs does not collide.
const ( PinKindWorkflow = "workflow" PinKindFile = "file" // PinKindSchema pins a registered payload schema by its source hash. A // schema is a registered object, and the pinning rule is "registered // objects by version" — therefore it pins. What a run validates its // payloads against is a fact about the run, not about the table's current // contents. PinKindSchema = "schema" )
Pin kinds. `workflow` pins a registered `name@version` by its source hash; `file` pins an arbitrary operator-supplied path by its content hash — "how the reference instance pins its contracts, fragments, and policy without core knowing what they are". Core reads bytes, hashes them, stores the path, and never opens the content again.
const ( StepPending = "pending" StepClaimed = "claimed" StepRunning = "running" StepGated = "gated" StepDone = "done" StepWaitingHuman = "waiting-human" StepSkipped = "skipped" StepSuperseded = "superseded" StepFailedRouted = "failed-routed" // StepReady is the COMPUTED status a read verb renders when the readiness // predicate holds. It is a rendering value, never a column value, and // TestReadyIsNeverPersisted asserts nothing writes it. StepReady = "ready" // StepStaged is the second COMPUTED status, rendered only on offer rows // (`next`, `dispatch open`): the step is NOT ready — its `after` // predecessors have not all recorded — but every unsatisfied predecessor // is itself in the same offer at a lower stage, so a dispatcher that runs // the offer's stages in order will find it claimable by the time its stage // begins. Like `ready` it is a rendering value, never a column value, and // never an answer `step show` gives (stagedness is a property of one // offer's membership, not of the step). A `staged` row is NOT claimable // yet: `claim` re-checks the predicate and refuses until the predecessors // actually record, which is what keeps a stage-skipping dispatcher safe. StepStaged = "staged" )
The nine PERSISTED step statuses. `ready` is the tenth member of the MACHINE and is deliberately absent here: it is computed at read time by the readiness predicate and never stored as intent, exactly as v6's effective lease status is. Both numbers are stated because the enum's size and the machine's size are different questions.
const ( ClaimEndFailed = "failed" ClaimEndReaped = "reaped" )
Claim-end reasons: the value `last_claim_end` holds (v27, DKT-1279). Each names how the MOST RECENT claim to leave a step ended — `ClaimEndFailed` for an explicit `step fail`, `ClaimEndReaped` for a lease expiry, `max_step_duration`, or a forced `step reap`, the same split FailedAttempts/ReapedClaims already count. A claim that RECORDED writes neither, so the column keeps whatever it held before — that step is terminal and `last_claim_end` is never read again.
const ( // SagaRecorded is stage 1's commit: the artifact is in, the token is // retired, status is `gated`. From here the saga is engine-owned and needs // no lease. SagaRecorded = "recorded" // SagaRouting is the last stage's resume point: gates are all recorded and // routing is what remains. SagaRouting = "routing" // SagaHeld is the stage an `aggregate` step enters when `hold_spread` // trips. Its artifact is recorded and its `<step>-held` question is open; // its routing is DEFERRED until an operator resolves that question. // // It is a stage rather than a status because "gating the routing step" is a // DEFERRAL OF ROUTING, and the machinery for deferring a saga already // exists. The step's status stays `gated` — non-terminal — so every // downstream successor fails the predecessors-done clause and nothing // proceeds: no new status, no synthetic `after` edge, no second readiness // rule. SagaHeld = "held" // SagaGatePrefix prefixes a per-gate resume point, `gate:<name>`. SagaGatePrefix = "gate:" )
Saga stages. The value stored in `steps.saga_stage` is the RESUME POINT: the stage that has committed, so the next engine invocation knows which one to run next. NULL means "not in the saga" — either never entered or complete.
const ArtifactMaxBytes = 1 << 20
ArtifactMaxBytes is the explicit artifact size cap. An artifact over it is a VALIDATION_ERROR naming the size and the cap — a refusal rather than a truncation, because a silently truncated artifact is one a downstream step consumes as if it were whole.
const ClaimPhasePreGates = "pre-gates"
ClaimPhasePreGates marks a claim whose lease committed and whose pre-gate phase and context transaction have not yet both committed.
const ClassMaxAuto = "auto"
ClassMaxAuto is the limits.max.<class> value that derives the cap from the host's CPU count when the scheduler reads it.
const DefaultProjectID = 1
DefaultProjectID is the row every pre-v12 datum backfilled to, and the row an invocation with no resolvable identity falls back to.
const EngineAuthor = "docket-engine"
EngineAuthor is the fixed Author value for comments the engine writes itself (step claimed, gate/vote opened, step failed, issue completed or abandoned), so they read as machine-authored without a schema column to mark them.
const IssueResolutionAbandoned = model.ResolutionAbandoned
IssueResolutionAbandoned is the resolution the `abandon-issue` routing and `run abandon --issue` write: the machine stopped working this issue and did not finish it. It is model.ResolutionAbandoned rather than a second literal, so the value the engine writes and the value the renderer tests for cannot drift apart.
const NameMaxBytes = 64
NameMaxBytes caps an opaque name stored in config or recorded in a ledger, because such a name is attacker-controlled text that lands in a ledger and in a report. ONE number for every opaque-name validator, so two of them cannot drift apart.
const UnitNameMaxBytes = NameMaxBytes
UnitNameMaxBytes is NameMaxBytes under the name `--usage`'s validator has always called it.
const UnregisteredProjectID = -1
UnregisteredProjectID is the id an invocation resolves to when its identity has NO project row and this invocation is not allowed to create one (DKT-58: a read verb, or an identity that is not a repository).
It is negative so it references nothing: every scoped SELECT returns empty and every scoped INSERT fails its foreign key rather than silently landing in someone else's project. That is the honest answer to "show me this directory's issues" when the directory has no project — the pre-DKT-58 code answered it by MINTING one, and a read that creates permanent state is how the store filled with rows nobody asked for.
It is deliberately NOT DefaultProjectID: falling back to project 1 would show a legacy store's issues under an unrelated repository and, worse, let a write from an unregistered directory land in that project's history.
const UsageSourceReported = "reported"
UsageSourceReported is the only source core writes at completion: a claimant said so. A back-fill supplies its own (engine.UsageSourceBackfilled).
const UsageUnitsMax = 32
UsageUnitsMax is the first cap: at most 32 units per report.
The caps exist because `--usage` is ATTACKER-CONTROLLED JSON FROM A CLAIMANT that lands in a ledger and in a report — bytes going to a terminal. A claimant that could write ten thousand units would make every subsequent `run report` on that run unreadable, which is a denial of the verb an operator reaches for when something has gone wrong.
const VoteMetadataMaxBytes = 16 << 10
VoteMetadataMaxBytes caps the encoded size of a vote's opaque KV bag, in the shape ArtifactMaxBytes uses: the limit lives beside the column it protects, so every writer of `votes.metadata` crosses it — `vote cast`, and `import` through InsertVoteWithID — rather than only the one command that happens to parse a flag. The value matches the 16 KiB a step's own metadata bag gets; it is duplicated rather than imported because internal/engine already imports this package.
Variables ¶
var ( // ErrLeaseHeld means a live lease is held by someone else — the claim // race's loser. Surfaced as CONFLICT (exit 4). ErrLeaseHeld = errors.New("lease held") // ErrNotHolder means the caller presented no matching capability: either // the entity is unclaimed, or the token is wrong. Surfaced as AUTH_ERROR // (exit 5). // // The two cases are deliberately not distinguished. "Unclaimed" and "wrong // token" are the same answer to the caller — you do not hold this lease — // and separating them would leak whether a lease exists to a caller // holding no capability. ErrNotHolder = errors.New("not the lease holder") // ErrLeaseExpired means the token is RIGHT but the lease lapsed. Surfaced // as STALE_LEASE (exit 6), which is the entire value of a separate code: a // holder seeing it knows to re-claim (its work may be redone), while // AUTH_ERROR means it never held the lease at all. ErrLeaseExpired = errors.New("lease expired") )
Lease refusal sentinels. Each maps to exactly one CLI error code in the refusal matrix.
var ( ErrSelfRelation = errors.New("self-referential relation") ErrDuplicateRelation = errors.New("duplicate relation") ErrCycleDetected = errors.New("cycle detected") )
Sentinel errors for relation operations.
var ( // ErrSchemaConflict means `name@version` is already registered with // DIFFERENT bytes. Surfaced as CONFLICT (exit 4), naming both hashes. // // Why a conflict and not an overwrite: the pinning property — // "editing a pipeline never changes an in-flight run" — is worth nothing if // the pinned bytes can be swapped underneath the run. A schema decides // whether a worker's payload is ACCEPTED, so a mutable findings@1 means a // run's acceptance criteria change mid-flight. Bump the version. ErrSchemaConflict = errors.New("schema already registered with different content") // ErrSchemaNotFound means no row matches the requested name/version. // Surfaced as NOT_FOUND (exit 2). ErrSchemaNotFound = errors.New("schema not found") )
Schema registration sentinels. They mirror the workflow ones exactly, because the two registries have the same immutability contract and a caller mapping errors to exit codes should not have to learn it twice.
var ( // ErrSchemaAlreadyDeprecated means the version is already retired. // Surfaced as CONFLICT (exit 4), for ErrWorkflowAlreadyDeprecated's // reason: the second caller's "I am the one taking this out of service" // is wrong, and the timestamp they would expect to see is not the one // stored. ErrSchemaAlreadyDeprecated = errors.New("schema version is already deprecated") // ErrSchemaBuiltin means the version ships in the binary. It is refused // rather than retired because a builtin row is visible to EVERY project // through its flag, so retiring it in one project's registry would retire // it for the whole store — and nothing an operator can register replaces // what the binary seeds. ErrSchemaBuiltin = errors.New("schema is builtin and cannot be deprecated") )
Schema retirement sentinels (v36). They mirror the workflow ones, because the two registries share the retirement contract and a caller mapping errors to exit codes should not have to learn it twice.
var ( // ErrStepNotFound means no `steps` row matches. NOT_FOUND (exit 2). ErrStepNotFound = errors.New("step not found") // ErrSagaStageMoved means a saga stage's CAS guard matched zero rows: // another engine invocation advanced the saga first ("resume is // lazy and idempotent"). It is not a failure — the loser re-reads and // either finds the work done or advances the next stage — so no CLI verb // maps it to an exit code; the saga driver handles it internally. ErrSagaStageMoved = errors.New("saga stage already advanced") )
Step sentinels.
var ( // ErrWorkflowConflict means `name@version` is already registered with // DIFFERENT bytes. Surfaced as CONFLICT (exit 4), naming both hashes. // // A registered name@version is frozen: re-registering identical bytes is // an idempotent success, and re-registering different bytes is this. The // version pinning the engine requires ("editing a pipeline never // changes an in-flight run") is worth nothing if the pinned bytes can be // swapped underneath a run. ErrWorkflowConflict = errors.New("workflow already registered with different content") // ErrWorkflowNotFound means no row matches the requested name/version. // Surfaced as NOT_FOUND (exit 2). ErrWorkflowNotFound = errors.New("workflow not found") )
Workflow registration sentinels.
var ErrConflict = errors.New("conflict")
ErrConflict is returned when an operation violates a uniqueness or state constraint.
var ErrDispatchAlreadyOpen = errors.New("a dispatch is already open for this run")
ErrDispatchAlreadyOpen is the open race's loser — a CONFLICT.
var ErrIssueHasParent = errors.New("issue has a parent")
ErrIssueHasParent refuses a project migration of a non-root issue: sub-issues follow their root, so the root is what migrates (or the issue is reparented first).
var ErrIssueInRun = errors.New("issue belongs to a run")
ErrIssueInRun refuses a project migration of an issue any run holds: the run's snapshots, steps, and events are project-scoped bookkeeping, and an issue migrated from under them would strand every record.
var ErrLabelColorConflict = errors.New("label color conflict")
ErrLabelColorConflict is returned when --color specifies a different color than an existing label already has.
var ErrNoOpenDispatch = errors.New("no dispatch is open")
ErrNoOpenDispatch is returned when a verb that needs an open manifest finds none. It is a sentinel so the CLI maps it once rather than matching a message.
var ErrNotAttached = errors.New("label not attached")
ErrNotAttached is returned when a label is not attached to the specified issue.
var ErrNotFound = errors.New("not found")
ErrNotFound is returned when a requested resource does not exist.
var ErrPrefixTaken = errors.New("prefix already held by another project")
ErrPrefixTaken refuses a prefix another project already holds (DKT-60). The prefix is a project's only discriminator in a listing or an event feed, so two projects sharing one makes every id in the store ambiguous about its owner.
var ErrProjectInUse = errors.New("project still has rows")
ErrProjectInUse refuses the deletion of a project anything still references (DKT-59).
var ErrProjectIsDefault = errors.New("the default project cannot be deleted")
ErrProjectIsDefault refuses the deletion of the default row.
var ( // ErrRunNotFound means no `runs` row matches. NOT_FOUND (exit 2). ErrRunNotFound = errors.New("run not found") )
Run sentinels.
var ErrUnknownConfigKey = errors.New("unknown config key")
ErrUnknownConfigKey is returned for a key outside the known set. A typo'd key must not silently store a value nothing reads.
var ErrUsageAlreadyRecorded = errors.New("usage already recorded for this step, attempt, and unit")
ErrUsageAlreadyRecorded is the (step_id, attempt, unit) key firing — this unit was already recorded for this attempt.
It is a SENTINEL rather than a raw constraint error so a caller can phrase the refusal for whoever hit it: the same violation means "the claimant already reported this" at completion and "you are back-filling twice" at back-fill, and the two want different sentences.
var ErrValidation = errors.New("validation")
ErrValidation is returned when an input fails a validation precondition that the DB layer enforces (e.g., negative revision number). CLI surfaces map this to output.ErrValidation.
var ErrVersionConflict = errors.New("version conflict")
ErrVersionConflict is returned when a CAS-guarded mutation is attempted against a row whose version differs from the caller's expectation — someone else wrote to it in between. Callers surface this as CONFLICT (exit 4), distinct from ErrNotFound (exit 2).
var ErrWorkflowAlreadyDeprecated = errors.New("workflow version is already deprecated")
ErrWorkflowAlreadyDeprecated means the version is already retired. Surfaced as CONFLICT (exit 4): retiring twice is not a silent success, because the second caller's mental model ("I am the one taking this out of service") is wrong and the timestamp they would expect to see is not the one stored.
Functions ¶
func ActionOutputsFor ¶
ActionOutputsFor reads the `output` column of a step's PASSING rows for one action name, ordered by ordinal.
This is deliberately NOT ActionTrail: that rollup's Output is blank on any passing row (its output channel exists for diagnosis, and a pass carries no diagnosis worth the read), and even on a failing row it is tail-truncated for display. The `aggregate` builtin's `route_at` split (DKT-593) records its below-floor clusters as a PASSING row's own output — a fact to compute from, not a diagnostic — so a reader of that fact needs the untruncated column directly.
func AddLabelToIssue ¶
AddLabelToIssue attaches a label to an issue within a transaction. The label is created if it does not already exist (with the given color). Activity is recorded and the issue's updated_at timestamp is touched.
func AddLabelsToIssue ¶
func AddLabelsToIssue(db *sql.DB, issueID int, labelNames []string, color string, author string) error
AddLabelsToIssue attaches multiple labels to an issue atomically within a single transaction. Labels are created if they do not already exist (with the given color). Activity is recorded for each newly attached label and the issue's updated_at timestamp is touched once.
func AddRunIssue ¶
AddRunIssue attaches an issue to a run before activation. The binding, snapshots, and expansion timestamp are all NULL until activation fills them — a run in `planning` carries its issue list and nothing else.
func AdvanceSagaTx ¶
AdvanceSagaTx moves a step's saga stage under a CAS guard on the CURRENT stage — "each stage's transaction is WHERE saga_stage = <expected> CAS-guarded, so two concurrent engine invocations resuming the same saga produce exactly one advance".
The loser matches zero rows and gets ErrSagaStageMoved, which is not an error condition: it re-reads and either finds the saga finished or advances the stage that is now current. This is the whole of the concurrent-resume story, and it is one WHERE clause because a saga guarded by a read-then-write would have the window this exists to close.
`from` is "" for the NULL stage (entering the saga) and `to` is "" to leave it (the saga completing).
func AllProjectPrefixes ¶
AllProjectPrefixes reads every prefix registered in the store — the root hook's roster for model.SetKnownProjectPrefixes (DKT-110), so id parsing accepts exactly the prefixes some project actually holds and refuses the rest (`ANSI-16` resolving issue 16 was the defect).
func AttachFiles ¶
AttachFiles inserts rows into issue_files for each file path. Duplicate attachments are silently ignored (INSERT OR IGNORE). Activity is recorded for each batch of newly attached files.
func AuthorizeLeaseMutation ¶
AuthorizeLeaseMutation verifies that a caller may mutate an issue whose lease may or may not be live, and ends the lease when the caller is the holder.
This is the guard for terminal verbs (`issue close`), which end a lease as a side effect the way a step's token retires when its artifact records.
The dormancy rule is here, in the first branch: an issue with no LIVE lease is outside the mechanism entirely. No token is required, nothing is refused, and behavior is exactly what it was at v5. The token check fires only when a live lease exists — which is what keeps the dormancy guarantee for a repo that never claims.
func AuthorizeStepRead ¶
AuthorizeStepRead is AuthorizeStepTx's predicate over a READ, for the one caller that must refuse a non-holder before opening a write transaction: stage 0's payload validation. It shares the predicate rather than restating it, so the two cannot disagree, and it is ADVISORY — AuthorizeStepTx remains the authority.
func AuthorizeStepTx ¶
AuthorizeStepTx is the saga's stage-0 holder check and the step refusal matrix in one call: the token must hold a LIVE lease on the step. It is leaseSteps.authorize, exported under a name the saga reads naturally.
func AutoRegisterEnabledTx ¶
AutoRegisterEnabledTx resolves KeyAutoRegister inside a CALLER'S transaction — activation's own, since that is the only place this reads (the auto-registration scan runs inside activateTx, and a pool read there would deadlock against the one-connection pool exactly as VoteRuleExistsTx's doc explains).
The parse cannot fail on a value that reached storage: SetConfig already ran it through ValidateConfigValue's KindBool case, which is the same strconv.ParseBool this calls.
func BindRunIssueTx ¶
BindRunIssueTx records an issue's binding and its activation-time snapshots — stages 1 and 4 of the fat transaction, written together because they are one fact: this issue, bound to this workflow, as it read at this moment.
func BreachRunBudgetTx ¶
BreachRunBudgetTx flips the run `active -> waiting-human` with its reason, CAS-guarded on the status it is moving FROM.
The CAS is the whole mechanism. Two invocations that both observe the cap crossed both call this; exactly one matches a row, and the loser writes neither a second reason nor — because the caller keys the event on this return — a second event. The guard is the STATUS ITSELF rather than a flag we maintain, so there is no second piece of state that could disagree with it.
`reason` is written to BOTH `reason` and `breach_reason`. `reason` is the run machine's general "why is it parked" field that `run status` already renders, and `breach_reason` is the budget's own, so a later `pause` for an unrelated cause cannot overwrite the record of the breach.
`pause_origin` is written in the SAME statement (DKT-305): this park is a RUN-LEVEL decision — it parks no step — and the reconciliation rollup reads that column to know it must not auto-resume. Before the column existed the rollup read `breach_reason` for the same purpose, which worked only because a breach is the one run-level park that leaves a second trace; an operator's `run pause` leaves none, and was silently undone.
func BudgetDefaultTx ¶
BudgetDefaultTx is the config cap, read in the deciding transaction. 0 means unlimited.
func BudgetUnitTx ¶
BudgetUnitTx resolves WHICH recorded usage unit the run cap counts.
Empty — the default and the only value core ships — means `reported` is 0 and the enforcement rests entirely on the floor. That is the honest default: with reporting disabled, the run still pauses at the cap from the floor.
func CacheRunFloorTx ¶
CacheRunFloorTx writes `runs.usage_floor`.
IT IS A CACHE FOR THE REPORT AND NOTHING ELSE. No enforcement path reads it: the floor that decides is a SUM over claim events, computed inside the deciding transaction, because a stored running total is a read-modify-write and a read-modify-write is the one shape with no defense against concurrent writers. decision behaves as though it said the truth.
It does NOT bump `row_version`: the cache is a derived number, not a state change an operator's CAS should collide with. A claim that bumped the run's version for a number nobody asserted against would make `--if-version` on runs unusable during any active run.
func CascadeDeleteIssue ¶
CascadeDeleteIssue deletes an issue and all its descendants recursively in a single transaction. The recursive CTE finds all descendant issues; ON DELETE CASCADE constraints on comments, issue_labels, issue_relations, and activity_log handle cleanup of related rows automatically.
func CheckAndBumpVersion ¶
CheckAndBumpVersion enforces an --if-version precondition inside tx and increments the row's version.
ifVersion nil means "no precondition": the version is still bumped, so every mutation advances it and concurrent CAS writers are detected.
The zero-rows case is deliberately re-probed rather than assumed: a missing row is ErrNotFound (exit 2) while a present row at a different version is ErrVersionConflict (exit 4). Collapsing the two would report a live conflict as a missing entity.
func ClaimIssue ¶
func ClaimIssue(db *sql.DB, id int, owner string, ttlMS int64, nowMS int64) (token string, lease *model.Lease, err error)
ClaimIssue takes a lease on an issue and returns the minted capability token. It is one CAS transaction: exactly one of N concurrent claimants wins, and the losers get ErrLeaseHeld.
Expiry is reaped lazily, here and only here (lazy lease reaping confined to next/claim; reads never write). The `expires_ms <= now` disjunct IS the reaping — there is no reaper, no background pass, and no write from any read path. An expired lease is therefore re-claimable with no operator action beyond the claim itself, which is the liveness mechanism.
attempt increments on every winning claim, including the one that replaces a holder that died mid-work. That is the complete attempt trail: it counts claims for all time and is never decremented or reset.
func ClaimStep ¶
ClaimStep is the standalone claim, for tests and for any caller that needs only the lease. `step claim` uses ClaimStepTx.
func ClaimStepTx ¶
func ClaimStepTx( tx *sql.Tx, id int, owner string, ttlMS, nowMS int64, ) (token string, lease *model.Lease, err error)
ClaimStepTx takes a lease on a step inside the caller's transaction, through the SAME generalized implementation issues use.
It is transaction-scoped rather than standalone because `step claim` must mint the token and assemble the context bundle in ONE transaction — "an unclaimed executor has nothing, a claimed one has everything" — so the caller owns the transaction and this is one statement inside it.
func ClaimStepWithRecoveryTx ¶
func ClaimStepWithRecoveryTx( tx *sql.Tx, id int, owner, recoverySecret string, ttlMS, nowMS int64, ) (token string, lease *model.Lease, err error)
ClaimStepWithRecoveryTx is ClaimStepTx for a claimant that supplies a recovery secret. The step row keeps only model.HashToken(recoverySecret) in `recovery_hash`, or NULL when the secret is empty. Every claim writes the column, so a hash never outlives the lease it was claimed with.
func ClassMaxOverridesTx ¶
ClassMaxOverridesTx resolves every set limits.max.<class> inside the scheduler's snapshot transaction, keyed by class, in one statement however many classes a run names. A project's override wins over the store-wide value, GetConfig's order. A class absent from the map keeps its pinned `[limits]` max.
ClassMaxAuto resolves here, at read time, to half the host's logical CPUs and never below one, so the value tracks whichever host runs the scheduler; its entry keeps Auto set so a report can say the cap was derived.
func ClearAllData ¶
ClearAllData deletes all data from every persistent table within a single transaction. The schema and meta table are preserved.
Tables are deleted in FK-correct order — children before parents. FK CASCADE would handle dependents implicitly, but the explicit ordering keeps behaviour identical to the pre-v4 function and makes the contract auditable from the function body.
Doc tables and the pre-existing proposals/votes/proposal_issues tables are included; prior to v4 the latter three were silently omitted, which broke `--replace` import on any DB containing proposals.
func ClearAllDataTx ¶
func ClearProjectDataTx ¶
ClearProjectDataTx is ClearAllDataTx scoped to ONE project (v12): the same tracker tables, children before parents, but only the rows reachable from this project's roots. Under the shared store, "replace everything" scoped any wider would delete projects the operator was not looking at.
func ClearRunBreachTx ¶
ClearRunBreachTx clears `breach_reason` once a cap change has resolved the breach (DKT-80): a row still asserting "budget: spend N of cap M reached" after the cap moved past N misleads every reader that trusts it. When `newReason` is non-empty the run machine's general `reason` is rewritten too — the caller passes it only when the row's reason IS the breach reason, so an unrelated pause's reason is never overwritten.
`row_version` is not bumped here: every caller runs inside a transaction that already bumped it for the cap write itself.
func ClearStaleBudgetReasonTx ¶
ClearStaleBudgetReasonTx retires a run's `reason` when it still carries a PREVIOUS raise's "cap changed from X to Y" sentence (DKT-80's own text, written by `ClearRunBreachTx`'s `newReason` argument) and no breach is currently standing. `newReason` replaces it; an empty `newReason` blanks the field instead, for a run with no other reason to state.
`ClearRunBreachTx` only rewrites `reason` on the raise that RESOLVES a standing breach — right for `breach_reason` itself, since there is nothing left to clear a second time, but the decorative sentence that raise wrote survives every later, unrelated raise untouched, naming a cap that has since moved again (DKT-47). This is the caller's own leftover text retired, never an operator's pause reason: SetRunBudget only reaches for it once it has confirmed `reason` carries the exact prefix this package writes, and it supplies `newReason` itself only for a run still parked on the sentence (waiting-human) — blanking that one would leave a parked run with no stated reason at all.
func ClearStepInputsTx ¶
ClearStepInputsTx drops a step's recorded input bindings, so a re-claim can record the bindings of the attempt that is about to run in their place (DKT-1054).
The table's key is (step, position, artifact), so without this a retried step's second claim would ADD its bindings beside the first attempt's rather than replace them, and a read-back would see the union of two attempts — neither of which any executor saw. The step's snapshot is the snapshot of its CURRENT attempt; a lapsed or retried attempt's bindings are history the event log keeps, not a second answer to "what did this step see".
func ClearStepStartTx ¶
ClearStepStartTx clears the schedule-to-close clock, so a step returned to the unclaimed pool starts a fresh budget on its next claim. Reaping and explicit failure both go through it.
func CloseDispatchTx ¶
CloseDispatchTx: close and abandon are both CAS on (id, status='open').
It reports whether it MOVED the row. A close racing the TTL abandon matches zero rows and learns so, which is what lets the caller report `CONFLICT` naming what actually happened rather than "not open" — the loser needs to know WHY, and the row it then reads says.
func CloseOpenProposalsTx ¶
CloseOpenProposalsTx closes every OPEN proposal among `ids`, inside a caller's transaction, and reports how many it closed (DKT-262).
It exists because closing a stranded proposal is not an operator act — it is the tail of a transition that already happened. `run abandon` ends the run that opened them; the ack of a reap answers the question its proposal asked. Those transitions are transactional, so the close has to be able to ride inside them: a close committed separately can be lost while the transition stands, which puts the row back in the state this exists to prevent.
ONLY `open` ROWS MOVE, exactly as CloseProposal insists. Every other status is the record of a decision, and a bulk close that rewrote one would be the overwrite the immutable-record rule forbids — which matters more here than in the single-id case, because a caller passing a set has not looked at each one.
`reason` lands in `final_outcome`, so the row itself says how the question ended. It should name the TRANSITION, not the verdict: these proposals were never decided, and a reason that read like a decision would be a worse lie than the stale `open` was.
func CloseProposal ¶
CloseProposal retires an OPEN proposal without a tally (DKT-114).
The case it exists for: a gate's underlying decision was made another way — an operator authorized the guarded action directly — and the proposal the panel would have decided has no votes and no future. Before this verb such a proposal sat `open` forever, misreporting a settled question as a pending one.
Only `open` closes. Every other status is the record of a decision, and a close that rewrote one would be exactly the overwrite the immutable-record rule forbids. The reason lands in `final_outcome`, so the row itself says how the question ended.
func CommitProposal ¶
CommitProposal transitions an approved proposal to committed status with a final outcome. If escalationReason is non-empty, it is stored on the proposal.
func CountActivity ¶
CountActivity returns the total number of activity log entries for an issue, ignoring any limit. Callers pair it with GetActivity to report an honest pre-limit total and flag truncation.
func CountByPriority ¶
CountByPriority returns a map of priority -> count, scoped to a project when projectID is non-zero.
func CountBySize ¶
CountBySize returns a map of size -> count, scoped to a project when projectID is non-zero. The "" key counts issues with no size declared.
func CountByStatus ¶
CountByStatus returns a map of status -> count, scoped to a project when projectID is non-zero.
func CountIssues ¶
CountIssues returns the number of issues, scoped to a project when projectID is non-zero.
func CountRootIssues ¶
CountRootIssues returns the number of issues with no parent, scoped to a project when projectID is non-zero.
func CountSteps ¶
CountSteps returns a run's step count, for `run status` without paging the whole table.
func CoverGateOverrideGrantsTx ¶
CoverGateOverrideGrantsTx bumps `covered_steps` on each grant that just auto-passed one step, in the same transaction as the routing it decided — the counter and the pass must not be separable by a crash.
A miss is a caller error rather than a no-op: the ids came from a read of this same table moments ago, and a grant that vanished between the read and the cover means the routing was decided on evidence that no longer stands.
func CreateComment ¶
CreateComment inserts a new comment for an issue, records activity, and returns its ID. The insert and activity log are wrapped in a single transaction so they succeed or fail together.
func CreateCommentIdempotent ¶
func CreateCommentIdempotent(db *sql.DB, comment *model.Comment, idempotencyKey string) (int, error)
CreateCommentIdempotent is CreateComment with an optional idempotency key. A repeat call with the same key returns the original comment id and inserts nothing; the key is recorded in the same transaction as the insert.
func CreateCommentIdempotentTx ¶
func CreateCommentIdempotentTx(tx *sql.Tx, comment *model.Comment, scope, idempotencyKey string) (int, bool, error)
CreateCommentIdempotentTx is CreateCommentIdempotent inside the caller's transaction, for a writer whose comment must commit or roll back with something else (a step's recorded artifact). It reports whether it inserted: a key already recorded returns that comment's id and false, and inserts nothing. The key is recorded in scope. It neither begins nor commits.
func CreateDoc ¶
CreateDoc inserts a new doc and appends revision #1 with change_kind="create" in a single transaction. Returns the new doc ID. The supplied doc must have Type, Status, Title, Body, and Author set; CreatedAt/UpdatedAt are stamped by this function.
func CreateDocComment ¶
CreateDocComment inserts a comment on a doc and returns its ID. The doc existence check and insert run in a single transaction. Returns ErrNotFound if the doc does not exist.
func CreateDocCommentIdempotent ¶
func CreateDocCommentIdempotent(db *sql.DB, c *model.DocComment, idempotencyKey string) (int, error)
CreateDocCommentIdempotent is CreateDocComment with an optional idempotency key. A repeat call with the same key returns the original comment id and inserts nothing; the key is recorded in the same transaction as the insert.
func CreateDocIdempotent ¶
CreateDocIdempotent is CreateDoc with an optional idempotency key. A repeat call with the same key returns the original doc id and inserts nothing; the key is recorded in the same transaction as the insert.
func CreateIssue ¶
CreateIssue inserts a new issue and returns its ID. Labels are created (find-or-create) and linked to the issue within the same transaction. Files are attached to the issue if provided.
func CreateIssueIdempotent ¶
func CreateIssueIdempotent(db *sql.DB, issue *model.Issue, labels []string, files []string, idempotencyKey string) (int, bool, error)
CreateIssueIdempotent is CreateIssue with an optional idempotency key.
When idempotencyKey is non-empty and was already used for this scope, the original issue's id is returned with inserted=false and nothing is inserted — a retried create after a dropped response must succeed, not fail. inserted is true only when this call created the issue, so a caller that writes more after the insert keys on it rather than on a lookup of its own, which another process could race. The key record and the insert commit in the SAME transaction, so a crash between them cannot orphan either.
func CreateProposal ¶
CreateProposal inserts a new proposal and returns its ID.
func CreateProposalIdempotent ¶
CreateProposalIdempotent is CreateProposal with an optional idempotency key. A repeat call with the same key returns the original proposal id and inserts nothing. Unlike the plain path this runs in a transaction, so the insert and the key record commit together.
func CreateRelation ¶
CreateRelation inserts a new relation between two issues within a single transaction. It validates that both issues exist, rejects self-referential and duplicate relations, runs cycle detection for blocks/depends_on types, and records activity on both issues.
func DefaultProjectIDOr ¶
DefaultProjectIDOr is projectOrDefault for callers outside this package that render their own SQL and need the same zero-means-default rule.
func DeleteDoc ¶
DeleteDoc removes the doc with the given ID. When cascade is true, FK cascades drop doc_revisions, doc_comments, doc_issue_links, proposal_docs. When cascade is false, the call returns ErrConflict if any links (issue or proposal) exist for the doc — comments and revisions are part of the doc's own history and never block deletion. Returns ErrNotFound if no doc with that ID exists.
func DeleteIssue ¶
DeleteIssue removes an issue by ID. Foreign key cascades handle cleanup of related rows (comments, labels, activity, relations).
func DeleteLabel ¶
DeleteLabel removes a label by ID. CASCADE constraints handle cleanup of issue_labels rows. Activity is recorded for each affected issue using the provided name. Returns the list of issue IDs that were attached to the label.
func DeleteProject ¶
DeleteProject removes an EMPTY project row (DKT-59).
It exists because the auto-registration defect (DKT-58) minted permanent rows nobody asked for, and `docket project` had no verb that could take one back out: the operator's only remedy was a raw sqlite DELETE against a store shared by every repository on the machine.
It REFUSES a project any row references, and refuses the default row outright. That is what makes it safe to expose: the verb can remove junk and cannot remove history, so there is no version of "I meant the other project" that costs anything. Re-homing real rows is `issue move --project`'s job, and a project emptied that way becomes deletable by this verb afterwards.
func DeleteRelation ¶
DeleteRelation removes a relation matching the given source, target, and type. Activity is recorded on both issues within a single transaction.
func DeprecateSchema ¶
func DeprecateSchema(db *sql.DB, projectID int, name string, version int, nowMS int64) (*model.Schema, error)
DeprecateSchema retires ONE registered schema version from service.
It writes a timestamp and NOTHING ELSE, DeprecateWorkflow's contract applied to the other registry: the row, its bytes, its ordered index, and its hash are untouched, so `schema show name@n` still renders it, a run that pinned it keeps validating payloads against it, and the lineage stays legible. The only thing that changes is that a NEW `payload` reference to it is refused at registration.
There is deliberately no delete verb, for the workflow registry's reason.
func DeprecateWorkflow ¶
func DeprecateWorkflow(db *sql.DB, projectID int, name string, version int, nowMS int64) (*model.Workflow, error)
DeprecateWorkflow retires ONE registered version from binding.
It writes a timestamp and NOTHING ELSE. The row, its body, its parsed form, and its hash are untouched, so:
- `workflow show name@n` still renders it, and `--source` still emits the exact registered bytes;
- definitionByID still resolves it, so a run that pinned this version before it was retired continues to completion — retirement is a binding-time filter, not a retraction;
- the lineage stays legible: `workflow list` shows the version with its retirement date rather than a gap where a version used to be.
There is deliberately no delete verb. The operator was offered one and rejected it: old versions stay registered, and that is the point.
func DerivePrefix ¶
DerivePrefix proposes a display prefix from a project's name (DKT-60).
A multi-word name becomes its INITIALS — `agentic-mcp-services` reads as `AMS` — because the alternative, the first three letters, collapses whole families of sibling repositories onto the same three characters. A single-word name takes its first three letters instead, since `D` alone carries nothing.
The result is always a legal prefix per model.ValidateProjectPrefix (letters only, 1-8) or empty when the name yields no letters at all; uniqueness and the reserved names are availablePrefix's job, because both are facts about the store rather than about the name.
func DetachFiles ¶
DetachFiles deletes rows from issue_files matching the issue ID and file paths. Activity is recorded for removed files.
func DispatchGraceTx ¶
DispatchGraceTx is the grace window: how long a claimed step may go unrecorded.
func DispatchTTLTx ¶
DispatchTTLTx and DispatchGraceTx resolve the two dispatch durations inside the caller's transaction, for the reason configValueTx exists: internal/db caps the pool at ONE connection, so a pool read from inside an open transaction deadlocks rather than failing.
func DoneTransitionClearsResolution ¶
DoneTransitionClearsResolution reports whether a status write must clear the issue's resolution. Every writer that can move an issue into done decides through it.
Moving an issue INTO done supersedes a routing's earlier "abandoned": the delivered issue must not read as cancelled. `run_disposition` keeps the run-level record. Re-asserting done on an issue already there is not a transition and keeps the resolution, and a caller that sets `resolution` explicitly keeps the value it set.
func EnsureProject ¶
EnsureProject resolves identity to a project id, creating the row on first contact.
The resolution ladder:
- A row already bound to this identity wins.
- The UNCLAIMED default row — id 1 with an empty identity, seeded by the v12 migration — is claimed in place. A legacy store holds exactly one project's history under project 1, and the first repository to open it is that project; claiming rather than inserting is what keeps that history attached to its repo.
- Otherwise a new row is inserted.
An EMPTY identity never claims and never inserts: it reads as "this invocation could not be resolved to a project" and falls back to the default row, which is the pre-v12 behavior exactly.
func EnsureProjectCreated ¶
EnsureProjectCreated is EnsureProject with the fact the caller needs in order to report a first contact: whether this call is what brought the row into being. The root hook writes a `project-registered` event on true (DKT-61).
func EventsRetain ¶
EventsRetain resolves the retention window, with 0 meaning "no retention window".
It returns the DURATION rather than a cutoff timestamp, because the caller computes the cutoff inside the prune's own transaction against that transaction's clock — a helper that read the clock here would hand back a boundary that had already moved by the time it was applied. EventsRetain is deliberately STORE-WIDE (projectID 0): the event stream and its prune are one machine-level lifecycle, and a per-project window would let one project's policy delete rows another project's audit still needs.
func ExemptStepAttemptFromBudgetTx ¶
ExemptStepAttemptFromBudgetTx exempts ONE spent attempt from a step's retry budget — the forced reap's classification (DKT-585) — by moving `attempt_base` forward by exactly one. Exhaustion compares `attempt - attempt_base` against `max_attempts`, so from here on the budget reads one fewer spent: the attempt still happened, it just does not count against the declared allowance.
It is a NUDGE, not ResetStepRetryBudgetTx's reset. A forced reap asserts that ONE claim's holder is gone — a relay declaring a dead spawn is not an executor failure (RUN-30 STEP-755: a wave stopped by an accidental interrupt consumed the step's last attempt, leaving it one interrupt from `waiting-human` on a healthy charter) — and the exemption must be as narrow as the assertion: this one attempt, nothing else. `attempt_base = attempt` here would also forgive every EARLIER genuinely-failed attempt, silently granting a full fresh budget on a verb that never claimed to be a retry.
`attempt` ITSELF IS UNTOUCHED, for exactly ResetStepRetryBudgetTx's reason (DKT-86, DKT-90): it is the usage ledger's key half (`UNIQUE(step_id, attempt, unit)`), and the dead attempt's usage stays back-fillable against its own attempt number after this runs — RUN-30's dead attempt had real measured usage back-filled, and decrementing the counter would have made that row collide with the successor's, the RUN-13 STEP-132 failure mode ReleaseStepLeaseTx documents.
The `attempt_base < attempt` guard keeps the base at or below the counter. Without it, reaching this twice for one claim — or on a row whose base has already caught up — would push the base PAST the counter, and `attempt - attempt_base` would go negative: budget minted out of nothing.
func ExtendDispatchTx ¶
ExtendDispatchTx records an append on an open manifest — its new expiry and the event seq it happened at — as a CAS on (id, status='open').
Both fields move together because they describe ONE append: an extend that wrote the seq without the expiry would name a manifest grown past the wall clock it was budgeted for, and one that wrote the expiry without the seq is the state DKT-2071's AC2 refuses — a manifest that has demonstrably grown and cannot say when.
The CAS is the same exclusion CloseDispatchTx relies on: a close or a TTL abandon racing an extend matches zero rows and the extend learns it lost rather than writing onto a manifest that is no longer open.
func GetActivity ¶
GetActivity retrieves activity log entries for an issue, ordered by most recent first.
func GetAllDirectionalRelations ¶
GetAllDirectionalRelations returns all relations where the relation type is "blocks" or "depends_on", in INSERTION order (`id`) — see GetAllRelations.
func GetAllRelations ¶
GetAllRelations returns every relation in the database, in INSERTION order (`id`).
It was `ORDER BY created_at ASC` with no tiebreak, and `created_at` here is RFC3339 at SECOND resolution (see AddRelation). Relations created inside one second therefore had NO defined relative order, so `docket export` run twice against an unchanged database could emit the relations array in two different orders and a manifest diff would carry noise that is not a change (DKT-330).
`id` is `INTEGER PRIMARY KEY AUTOINCREMENT`: strictly ascending, never reused. Ordering by it is not an approximation of creation order, it IS creation order, and it is total. Thirteen of the fifteen export-manifest readers already order by a primary or natural key; this was the one with no total order at all.
Same defect class as DKT-378's on `comments`, reached through another table — and the same remedy, for the same reason.
func GetBatchSubIssueProgress ¶
GetBatchSubIssueProgress returns (done, total) counts for descendants of each given parent ID in a single query, avoiding N+1 overhead.
func GetComment ¶
GetComment retrieves a comment by ID.
func GetDocComment ¶
GetDocComment returns a single doc comment by ID, or ErrNotFound.
func GetDocIssues ¶
GetDocIssues returns issue IDs linked to a doc, ordered by issue_id ASC.
func GetDocProposals ¶
GetDocProposals returns proposal IDs linked to a doc, ordered by proposal_id ASC.
func GetDocRevision ¶
GetDocRevision returns revision rev of the doc with the given ID: rev < 0 → ErrValidation; rev > MAX → ErrNotFound. rev == 0 is treated as "the current revision" (the most-recent one).
func GetIssueDocs ¶
GetIssueDocs returns doc IDs linked to an issue, ordered by doc_id ASC.
func GetIssueFiles ¶
GetIssueFiles returns the file paths attached to an issue, sorted alphabetically.
func GetIssueFilesTx ¶
GetIssueFilesTx is GetIssueFiles inside a transaction: the issue's attached paths sorted by file_path. The result is never nil; an issue with no attachments yields an empty slice, so callers can encode it as a JSON [].
func GetIssueLabelObjects ¶
GetIssueLabelObjects returns the full Label objects attached to an issue, sorted alphabetically by name.
func GetIssueLabels ¶
GetIssueLabels returns the label names attached to an issue, sorted alphabetically.
func GetIssueLease ¶
GetIssueLease reads an issue's lease without writing anything.
Reads never write. Liveness is computed from ExpiresMS by the caller via Lease.Live; nothing here reaps.
func GetIssueProposals ¶
GetIssueProposals returns the proposals linked to an issue, ordered by proposal id ascending. It is the reverse edge of GetProposalIssues.
func GetIssueRelations ¶
GetIssueRelations returns all relations where the given issue is either the source or the target, in INSERTION order (`id`) — see GetAllRelations for why that is the creation order rather than an approximation of it.
func GetIssuesByIDs ¶
GetIssuesByIDs retrieves multiple issues by their IDs in a single query. The returned map is keyed by issue ID. IDs that don't exist are silently skipped (no error for missing rows). Labels are hydrated on all returned issues.
func GetLabelByName ¶
GetLabelByName retrieves a label by name WITHIN ONE PROJECT, including the count of issues currently attached to it. Returns ErrNotFound if no label with that name exists in the project.
func GetProject ¶
GetProject reads one project row.
func GetProposal ¶
GetProposal returns a proposal by ID, or ErrNotFound if it does not exist.
func GetProposalDocs ¶
GetProposalDocs returns doc IDs linked to a proposal, ordered by doc_id ASC.
func GetProposalIssues ¶
GetProposalIssues returns the issue IDs linked to a proposal.
func GetProposalTx ¶
GetProposalTx is GetProposal inside a CALLER'S transaction — the engine's context assembly resolves `<step>.vote-record` inputs (DKT-545) in the claim transaction, and reading through the pool there would see a different snapshot than the bundle it is assembling.
func GetProposalVoteUsageSources ¶
GetProposalVoteUsageSources returns, per vote id, each usage unit's stored source (UsageSourceReported for the seat's cast-time report, or the back-fill's own source) read from that unit's own vote_usage row (DKT-2775). Every vote of the proposal has an entry; a vote with no rows maps to an empty, non-nil map. It is a db-level return rather than a field on model.Vote because the source describes the ledger row, not the cast.
func GetProposalVotes ¶
GetProposalVotes returns all votes for a proposal, ordered by creation time.
func GetProposalVotesTx ¶
GetProposalVotesTx is GetProposalVotes inside a CALLER'S transaction — see GetProposalTx for why the vote-record input resolution needs one.
func GetSchema ¶
GetSchema returns one registered schema visible to a project — its own registration or a builtin. A version of 0 selects the HIGHEST version still IN SERVICE, which is what `schema show NAME` without `@version` means — GetWorkflow's rule (v36 mirrors v11): a name whose every version is retired resolves to ErrSchemaNotFound, while an explicit version still resolves a retired row, because retired versions stay registered and reachable for the runs that pinned them.
func GetSchemaTx ¶
GetSchemaTx is GetSchema at an exact version, inside a transaction. It is what activation's pin stage reads, so the hash it records and the row it checked are the same read.
func GetStepLease ¶
GetStepLease reads a step's lease without writing anything (reads never write).
func GetSubIssueProgress ¶
GetSubIssueProgress returns (done, total) counts for all descendants of an issue.
func GetSubIssueTree ¶
GetSubIssueTree returns the full recursive tree of all descendants under an issue.
func GetSubIssues ¶
GetSubIssues returns all direct children of an issue.
func GetVersion ¶
GetVersion returns the current CAS version of a row. The table must be one of versionedTables.
func GetWorkflow ¶
GetWorkflow returns one registered workflow WITHIN ONE PROJECT (v12 — a name@version is a per-project registration). A version of 0 selects the HIGHEST NON-DEPRECATED registered version, which is what `workflow show NAME` without `@version` means — and, by design, the same version `run activate` binds (engine.bindableDefinitions retires deprecated rows first, then takes the highest of what remains; DKT-616). A name whose every version is deprecated therefore resolves to ErrWorkflowNotFound, mirroring binding removing the name from routing altogether. An explicit version still resolves a deprecated row: retired versions stay registered and reachable for the runs that pinned them.
func GrantLoopTx ¶
GrantLoopTx authorizes ONE more fix loop for an issue and returns the new total.
It RAISES A GRANT rather than editing `max_fix_loops`, because the two say different things: the workflow's bound is the author's standing policy over every issue it matches, while this is one operator's decision about one issue on one occasion. Editing the bound to unstick a single issue would quietly loosen it for every issue after.
func HasGateResult ¶
HasGateResult reports whether a step+gate has any recorded result.
This is the at-least-once detector: a `saga_stage` of `gate:<name>` with no result row is a started-but-unrecorded gate — a crash between the `gate-started` commit and the result commit.
func HeartbeatIssue ¶
func HeartbeatIssue(db *sql.DB, id int, token string, ttlMS int64, nowMS int64) (*model.Lease, error)
HeartbeatIssue extends a live lease held by token, and returns the new expiry. Any tool activity in the holder's session can drive this, so a working holder keeps its lease and a wedged or dead one lets it lapse.
attempt and owner are untouched: a heartbeat is not a new claim.
func HeartbeatStep ¶
HeartbeatStep extends a live lease held by token. attempt is untouched — a heartbeat is not a new claim.
func HydrateFiles ¶
HydrateFiles bulk-loads files for a set of issues, populating each issue's Files field. This avoids N+1 queries in list views and the planner.
func HydrateLabels ¶
HydrateLabels bulk-loads labels for a set of issues, populating each issue's Labels field. This avoids N+1 queries when displaying lists.
func HydrateLinkedIssues ¶
func IdempotencyKeyOf ¶
IdempotencyKeyOf is the REVERSE lookup: the key recorded for (scope, entityID), and whether one exists. It exists for the caller that holds an entity and needs the identity its create was keyed under — the engine recovering a vote-step proposal's run from the key OpenVoteProposal recorded — without a second, disagreeable copy of that link on the entity's own row. A create performed without a key (the historical non-idempotent wrappers) simply reports no row.
func IncrementLoopCountTx ¶
IncrementLoopCountTx raises the issue's loop counter by one and returns the NEW value.
The read-back is in the same statement's transaction rather than a separate SELECT so the value returned is the one this UPDATE wrote. Two concurrent routings incrementing the same issue would otherwise both read the same "new" count and both believe they were loop k+1 — and `max_fix_loops` would bound nothing.
func Initialize ¶
Initialize creates all tables if they don't exist and sets the schema version.
func InsertActionResultTx ¶
func InsertActionResultTx(tx *sql.Tx, r ActionResultRow) error
InsertActionResultTx records one action result.
It takes a transaction because the result commits with the routing stage's own writes — the subprocess ran OUTSIDE any transaction and only its recorded fact lands in one.
func InsertActivityWithID ¶
InsertActivityWithID inserts an activity_log row with a caller-supplied ID, skipping if the ID already exists. Must be called within an existing transaction. Returns true if inserted. Mirrors InsertIssueWithID.
func InsertArtifactTx ¶
InsertArtifactTx records one artifact inside the caller's transaction — saga stage 1, alongside the token's retirement, because "the token retires when the artifact records" is one commit or it is not the hinge it is specified to be.
func InsertCommentWithID ¶
InsertCommentWithID inserts a comment with a specific ID (not auto-increment), skipping if the ID already exists. Returns true if the row was inserted. Must be called within an existing transaction.
func InsertDispatchRowTx ¶
func InsertDispatchRowTx(tx *sql.Tx, dispatchID int, row DispatchRow) error
InsertDispatchRowTx stores one manifest row at its position.
func InsertDispatchTx ¶
InsertDispatchTx opens a manifest, and the race guard IS THE INSERT.
`idx_dispatches_one_open` is a partial UNIQUE index on (run_id) WHERE status='open', so two relays racing produce one row and one constraint violation — never a check-then-insert's window, in which both SELECT no open dispatch and both then INSERT one. The loser gets ErrDispatchAlreadyOpen and its whole computation is DISCARDED rather than merged.
func InsertDocCommentWithID ¶
InsertDocCommentWithID inserts a doc_comments row with a caller-supplied ID, skipping if the ID already exists. Returns true if inserted. Must be called within an existing transaction. Mirrors InsertCommentWithID.
func InsertDocIssueLink ¶
InsertDocIssueLink inserts a doc_issue_links row, skipping on PK conflict. Used by export/import round-trip. Must be called within a transaction. Returns true if inserted.
func InsertDocRevisionWithID ¶
InsertDocRevisionWithID inserts a doc_revisions row with a caller-supplied ID, skipping if the ID already exists. Must be called within a transaction. Returns true if inserted. Mirrors InsertIssueWithID.
func InsertDocWithID ¶
InsertDocWithID inserts a doc row with a caller-supplied ID, skipping if the ID already exists. Mirrors InsertIssueWithID (round-trip helpers). Must be called within an existing transaction. Returns true if inserted.
func InsertEngineComment ¶
InsertEngineComment inserts an engine-authored comment against the caller's already-open transaction and returns its auto-minted ID. It never begins or commits a transaction of its own, so engine code already inside a transaction can drop an activity-trail comment without nesting a second top-level transaction. The comment's Author is always EngineAuthor.
The caller supplies the timestamp as epoch milliseconds rather than the writer reading the clock: an engine transaction stamps the issue row, the activity log and this comment from ONE `nowMS`, and a second clock read here would let the narration of a transition carry a different time than the transition itself.
func InsertFenceTx ¶
InsertFenceTx records one harvested command. Harvesting happens at ACTIVATION so post-activation edits cannot inject — the hash is of the command as it read when the operator approved the plan.
func InsertGapIssueTx ¶
InsertGapIssueTx materializes a backlog issue from a recorded gap artifact (DKT-72), inside the completion saga's transaction, and relates it to the issue whose step recorded the gap.
One transaction with the artifact, because the pair is the whole point: a gap that recorded an artifact but no issue is residue nothing re-reads — the failure mode this replaces — and an issue without its artifact is a claim with no record behind it. `relates_to` rather than a directional relation: a gap is out-of-scope BY DEFINITION, so it must not block the issue that surfaced it.
The ranking rides in the same transaction as the row it ranks (DKT-1082): a drained high-severity cluster that landed `priority none` was invisible to every priority-ordered planning pass, which is the same "residue nothing re-reads" failure one layer up.
func InsertGateOverrideGrantTx ¶
func InsertGateOverrideGrantTx(tx *sql.Tx, g GateOverrideGrant) (int, error)
InsertGateOverrideGrantTx records one grant and returns its id.
It takes a transaction because the grant commits with the resolution that raised it (the DKT-237 loop-grant discipline): a grant recorded without the override-pass would cover failures the operator never ruled on, and an override-pass without the grant would silently drop the ruling's reach.
func InsertGateResultTx ¶
func InsertGateResultTx(tx *sql.Tx, r GateResultRow) error
InsertGateResultTx records one gate result.
It takes a transaction because the result commits with the saga's stage advance — the subprocess ran OUTSIDE any transaction and only its recorded fact lands in one.
func InsertIssueFileMapping ¶
InsertIssueFileMapping inserts a single file mapping using INSERT OR IGNORE. Returns true if inserted, false if already existed. Must be called within an existing transaction.
func InsertIssueLabelMapping ¶
InsertIssueLabelMapping inserts an issue_labels row linking an issue to a label, skipping if the mapping already exists. Returns true if the row was inserted. Must be called within an existing transaction.
func InsertIssueWithID ¶
InsertIssueWithID inserts an issue with a specific ID (not auto-increment), skipping if the ID already exists. Returns true if the row was inserted. Must be called within an existing transaction.
func InsertLabelWithID ¶
InsertLabelWithID inserts a label with a specific ID (not auto-increment), skipping if the ID already exists. Returns true if the row was inserted. Must be called within an existing transaction.
func InsertNoteTx ¶
InsertNoteTx records an author-attributed comment against the caller's already-open transaction: the row `issue comment add` writes, minus the transaction it opens for itself. It is what a verb taking `--note` uses to land the note and the mutation it explains in ONE transaction (DKT-480), so a failed move leaves no orphan comment and a recorded comment never narrates a move that did not happen.
Unlike InsertEngineComment the author is the caller's, not EngineAuthor: a note an operator typed is an operator's comment, indistinguishable from the two-verb form it replaces. It touches the issue's updated_at and records the same `comment_added` activity CreateComment does, for the same reasons.
The updated_at touch runs FIRST so a missing issue is ErrNotFound rather than a foreign-key error from the insert.
The caller supplies the timestamp rather than this reading the clock, so the note carries the same stamp as the mutation it narrates.
func InsertPinTx ¶
InsertPinTx records a pin. `INSERT OR IGNORE` on the UNIQUE(run_id, kind, ref) key makes pinning the same workflow for two issues in one run a single row rather than a conflict — a run pins a workflow once, however many issues bind to it.
func InsertProposalDocLink ¶
InsertProposalDocLink inserts a proposal_docs row, skipping on PK conflict. Must be called within a transaction. Returns true if inserted.
func InsertProposalIssueLink ¶
InsertProposalIssueLink inserts a proposal_issues row, skipping on PK conflict. Must be called within a transaction. Returns true if inserted.
func InsertProposalWithID ¶
InsertProposalWithID inserts a proposal row with a caller-supplied ID, skipping if the ID already exists. Must be called within an existing transaction. Returns true if inserted. Mirrors InsertIssueWithID; domain_tags and files_changed are JSON-encoded identically to CreateProposal.
func InsertReapAckTx ¶
InsertReapAckTx records a reap as unacknowledged, in the reap's own transaction.
The caller is responsible for calling this only for a class whose `[limits] max` is finite, because that decision needs the scheduler's merged limits, which the storage layer does not have and must not guess at.
func InsertRelationWithID ¶
InsertRelationWithID inserts a relation with a specific ID (not auto-increment), skipping if the ID already exists. Returns true if the row was inserted. Must be called within an existing transaction.
func InsertRun ¶
func InsertRun(db *sql.DB, projectID int, request string, budget float64, nowMS int64) (*model.Run, error)
InsertRun creates a run in `planning` — `docket run start`.
`budget` was STORED before anything enforced it. Accepting it early meant the enforcing upgrade added enforcement rather than a flag: a flag appearing later would have broken the `run start` invocation an earlier harness scripted.
func InsertRunNoteTx ¶
InsertRunNoteTx records one note and returns its id.
It takes a transaction because the verb records the note and its `run-note-added` event together: a note with no event would be a packet change no feed reader can attribute, and an event with no note would name a ruling that renders nowhere.
func InsertRunWithContext ¶
func InsertRunWithContext(db *sql.DB, projectID int, request string, budget float64, nowMS int64, ctx RunContext) (*model.Run, error)
InsertRunWithContext is InsertRun carrying the invocation's context.
func InsertRunWithContextIdempotent ¶
func InsertRunWithContextIdempotent(db *sql.DB, projectID int, request string, budget float64, nowMS int64, ctx RunContext, idempotencyKey string, issueIDs ...int) (run *model.Run, inserted bool, err error)
InsertRunWithContextIdempotent is InsertRunWithContext with an optional idempotency key — `docket run start --idempotency-key`.
A repeat call with the same key returns the ORIGINAL run unchanged with inserted=false and creates nothing, the same create-verb replay-protection pattern CreateIssueIdempotent, CreateDocIdempotent, and CreateProposalIdempotent use (internal/db/idempotency.go): a `run start` that commits but dies before its response must be safe to retry, or the key protects nothing. The key record and the insert commit in the SAME transaction, so a crash between them cannot orphan either. The roster (issueIDs, one run_issues row each) commits in that same transaction, so a replay finds the roster the original call wrote; a replay's own issueIDs are ignored, like its other parameters. inserted tells the caller whether this call created the run.
func InsertSchema ¶
func InsertSchema(db *sql.DB, s *model.Schema, nowMS int64) (stored *model.Schema, created bool, err error)
InsertSchema registers a schema document, or returns the existing row when the same bytes are already registered at that `name@version`.
The three outcomes are `InsertWorkflow`'s, verbatim in behavior:
- no row at name@version -> insert, created = true
- a row with the SAME source_sha256 -> return it, created = false
- a row with a DIFFERENT source_sha256 -> ErrSchemaConflict
Idempotency is decided on the CONTENT HASH, not on a normalized form: two documents that validate identically but differ in whitespace or key order are different registered bytes, because `source_sha256` is what pins refer to and what a run reproduces against.
func InsertSchemaTx ¶
func InsertSchemaTx( tx *sql.Tx, s *model.Schema, nowMS int64, ) (stored *model.Schema, created bool, err error)
InsertSchemaTx is InsertSchema inside a CALLER'S transaction — the schema half of what activation's auto-registration needs.
It mirrors InsertWorkflowTx exactly, because the two registries have the same immutability contract and a caller should not have to learn it twice. The reason both are needed inside a transaction: auto-registration runs in activation's fat transaction, so a failure refuses the whole activation and leaves no definitions behind from a run that never started.
func InsertStaleTargetWaiverTx ¶
func InsertStaleTargetWaiverTx(tx *sql.Tx, w StaleTargetWaiver) (int, error)
InsertStaleTargetWaiverTx records one waiver and returns its id.
It takes a transaction because the verb records one waiver per named step plus one event per waiver, and a batch that half-applied would leave the operator's ruling covering rows they never listed — or missing rows they did.
func InsertStepInputTx ¶
InsertStepInputTx records that a step consumed an artifact at a declared position — the input resolution, MATERIALIZED.
The record exists because resolution is a function of run state at ASSEMBLY time, and run state moves: a later ordinal's artifact would re-resolve the same input differently. Storing what was actually handed over is what makes the ledger answer "what did this step see" rather than "what would it see now" — and ListStepInputArtifactsTx is how a read-back asks it (DKT-1054).
func InsertStepTx ¶
InsertStepTx writes one expanded step. The UNIQUE(run_id, issue_id, instance) index is the loop/fanout correctness guard — two rows claiming the same identity is the bug instance naming exists to prevent — so a duplicate is an error here rather than a silently-ignored insert.
func InsertTrustCacheTx ¶
func InsertTrustCacheTx( tx *sql.Tx, runID int, kind, gate, argvSHA256, entryName string, matched, prefix bool, atMS int64, ) error
InsertTrustCacheTx records what a run considered trusted, and when.
IT IS AN AUDIT RECORD, NEVER AN AUTHORIZATION SHORTCUT. Every gate consults the LIVE trust store on every execution; nothing is ever executed because a row here says a previous run matched. The opposite implementation is the tempting one and it is a revocation failure: a cache hit that authorized a spawn would make `trust rm` take effect only after the cache cleared.
`kind` is TrustKindGate or TrustKindAction. It exists so the one question this table answers stays one query when actions start consulting the same store: a second table keyed by the shape of the caller would split it. Every pre-v9 row reads `gate` from the column default, which is what it was.
func InsertUsageRowTx ¶
InsertUsageRowTx records one unit's quantity for one attempt of one step.
The unique key is (step_id, attempt, unit), so a reaped-and-reclaimed step's SECOND attempt records beside the first rather than overwriting it — which is what makes "retries re-accrue" true on the reported side as well as on the floor side. A second `complete` for the SAME attempt is impossible (the saga's stage-0 CAS), so the key is a belt-and-braces assertion rather than an upsert: if it ever fires, something upstream broke a guarantee and silently merging the rows would hide it.
func InsertVoteUsageTx ¶
func InsertVoteUsageTx( tx *sql.Tx, voteID int64, unit string, quantity float64, source string, nowMS int64, ) error
InsertVoteUsageTx records one unit's quantity against one SEAT's cast in the vote_usage ledger, source explicit (v17, DKT-115): the cast-time writer passes UsageSourceReported, the vote-scoped back-fill its own source. The (vote_id, unit) key firing maps to ErrUsageAlreadyRecorded so each caller can phrase the refusal for whoever hit it — the same split the step ledger's writer makes.
func InsertVoteWithID ¶
InsertVoteWithID inserts a vote row with a caller-supplied ID, skipping if the ID already exists. Must be called within an existing transaction. Returns true if inserted. findings_json and metadata are JSON-encoded (NULL when absent) identically to CastVote, so an export/import round trip carries a vote's provenance claim exactly as it carries its findings.
func InsertWorkflow ¶
func InsertWorkflow(db *sql.DB, wf *model.Workflow, nowMS int64) (stored *model.Workflow, created bool, err error)
InsertWorkflow registers a definition, or returns the existing row when the same bytes are already registered at that `name@version`.
The three outcomes:
- no row at name@version -> insert, created = true
- a row with the SAME source_sha256 -> return it, created = false
- a row with a DIFFERENT source_sha256 -> ErrWorkflowConflict
Idempotency is decided on the CONTENT HASH rather than on the parsed form: two files that parse identically but differ in comments are different registered bytes, and `source_sha256` is what pins and audits refer to.
func InsertWorkflowTx ¶
func InsertWorkflowTx( tx *sql.Tx, wf *model.Workflow, nowMS int64, ) (stored *model.Workflow, created bool, err error)
InsertWorkflowTx is InsertWorkflow inside a CALLER'S transaction.
It exists for auto-registration, which runs inside activation's FAT TRANSACTION: a registration failure must refuse the whole activation and write nothing, and a registration that committed on its own could not be rolled back with the binding that followed it. It is also the only correct shape against a one-connection pool — the self-committing version would deadlock if called from inside a transaction.
The three outcomes and the immutability contract are unchanged: this is the SAME body InsertWorkflow ran, lifted so both callers share it rather than a second path drifting from the first: no `auto` variant with looser rules.
func IsDescendant ¶
IsDescendant returns true if potentialDescendantID is a descendant of issueID. This is used to detect cycles when reparenting an issue.
func IssueExists ¶
IssueExists returns true if an issue with the given ID exists.
func IssueOwnerPrefix ¶
IssueOwnerPrefix reports the prefix of the project that OWNS an issue, or "" when the issue does not exist or its project has none (DKT-256).
One indexed lookup joining the issue to its project. It is the single-id form deliberately: issue ids are store-wide and a rendering pass touches whichever handful it happens to render, so a caller that preloaded the whole store would pay for every issue to display five.
A missing row is "" AND NO ERROR. The caller is a renderer, and a renderer that failed because an id it was handed does not exist would turn a cosmetic question into a broken command — the fallback is the reader's own prefix, which is exactly what it rendered before this existed.
func IssueProjectID ¶
IssueProjectID returns the project an issue is homed in, or ErrNotFound.
It exists for validations that need the project WITHOUT the issue's whole row: attaching issues to runs checks a whole set before writing anything (DKT-21), and loading each candidate's labels and lease state to read one column would make that loop's cost proportional to data it discards.
func IssueScopeGlobs ¶
IssueScopeGlobs reads an issue's declared scope as stored JSON, or "" when none is declared.
func IssueScopeGlobsTx ¶
IssueScopeGlobsTx is IssueScopeGlobs inside a transaction — the snapshot's reader at activation stage 4.
func IssueStatusResolutionTx ¶
IssueStatusResolutionTx reads an issue's status and resolution inside tx, or returns ErrNotFound.
func IssueStepRuns ¶
IssueStepRuns reads the ids of every run holding a step for one issue, in run order. `step list --issue ISSUE-N` needs it because an issue's steps are not confined to one run — a re-activation mints a fresh round under a new run — and the caller who asks about an issue rarely knows which runs those are (DKT-244).
func KnownConfigKeys ¶
func KnownConfigKeys() []string
KnownConfigKeys lists the fixed keys, plus the open-ended patterns.
func LeaseTTL ¶
LeaseTTL resolves the effective lease TTL for an executor class, falling back to lease.ttl.default when the class has no entry of its own. An empty class means "use the default".
func LinkDocIssue ¶
LinkDocIssue links a doc to an issue. Returns ErrNotFound if either side is missing; ErrConflict if the link already exists.
func LinkProposalDoc ¶
LinkProposalDoc links a proposal (vote) to a doc. Returns ErrNotFound if either side is missing; ErrConflict if the link already exists.
func LinkProposalIssue ¶
LinkProposalIssue links a proposal to an issue. Returns ErrNotFound if the proposal or issue does not exist. Returns ErrConflict if the link already exists.
func ListAllActivity ¶
ListAllActivity returns every activity_log row ordered by id ASC, for a full export.
func ListAllComments ¶
ListAllComments returns every comment in the database across all issues, ordered by insertion (`id`) for the reason given above ListComments — the same table read through a second query, so the two agree on what "in order" means.
func ListAllDocComments ¶
ListAllDocComments returns every doc_comments row ordered by id ASC, for a full export.
func ListAllDocIssueLinks ¶
ListAllDocIssueLinks returns every doc_issue_links row ordered by (doc_id, issue_id), for a full export.
func ListAllDocRevisions ¶
ListAllDocRevisions returns every doc_revisions row ordered by id ASC, for a full export.
func ListAllDocs ¶
ListAllDocs returns every doc row ordered by id ASC, for a full export.
func ListAllIssueFileMappings ¶
ListAllIssueFileMappings returns all rows from issue_files as IssueFileMapping structs. This is needed by the export command.
func ListAllIssueLabelMappings ¶
ListAllIssueLabelMappings returns all (issue_id, label_id) pairs from the issue_labels table.
func ListAllIssues ¶
ListAllIssues returns every issue in the database, including done issues, with no filters, sorting, or pagination. Labels are hydrated on all results.
func ListAllLabels ¶
ListAllLabels returns a project's labels along with the count of issues using each, sorted alphabetically by name. A zero projectID lists every project's labels.
func ListAllLabelsRaw ¶
ListAllLabelsRaw returns every label as a model.Label object (without issue counts), sorted alphabetically by name.
func ListAllProposalDocs ¶
ListAllProposalDocs returns every proposal_docs row ordered by (proposal_id, doc_id), for a full export.
func ListAllProposalIssues ¶
ListAllProposalIssues returns every proposal_issues row ordered by (proposal_id, issue_id), for a full export.
func ListAllProposals ¶
ListAllProposals returns every proposal row ordered by id ASC, for a full export.
func ListAllVotes ¶
ListAllVotes returns every vote row ordered by id ASC, for a full export.
func ListComments ¶
ListComments retrieves all comments for an issue, ordered by the auto-minted `id` alone — INSERTION ORDER, which for the activity trail is the only order that is always true (DKT-378).
It was `created_at ASC, id ASC`, and the tiebreak was the whole reason: the column is RFC3339 at SECOND resolution and the engine writes several trail comments per transaction, so same-second rows came back in whatever order SQLite chose. But `created_at` stopped being monotonic with insertion when InsertEngineComment began taking the CALLER's `nowMS` instead of reading the clock (that change is right — see its doc comment — and is not what is being undone here). The saga threads ONE `nowMS` through a whole gate execution and `next` drives every ready action step on a single one, so two comments committed seconds apart can carry the same stamp, or the later one an EARLIER stamp. A tiebreak cannot repair a primary key that is itself out of order.
`id` is `INTEGER PRIMARY KEY AUTOINCREMENT`: strictly ascending, never reused after a delete. Sorting by it alone makes the trail read in the order it was written, which is what a narrative of transitions IS.
Clamping the stamp instead (the way engine/event.go's `monotonicAtMS` does for events, against 57s of measured drift) was the other workable remedy and was rejected here: a clamped `created_at` is no longer the transition's own time, which is precisely the invariant threading `nowMS` exists to hold. Events have no monotonic insertion key to fall back on; comments do.
func ListDocComments ¶
ListDocComments returns all comments for a doc ordered by created_at ASC. Returns an empty slice (not nil) when the doc has no comments; returns ErrNotFound when the doc itself is missing.
func ListDocRevisions ¶
ListDocRevisions returns every revision row for the doc, ordered by revision_number ascending. Returns ErrNotFound when the doc itself is missing.
func ListDocs ¶
ListDocs returns docs matching opts, ordered and paginated. Returns the matching rows and the total count before limit/offset.
func ListIssues ¶
ListIssues retrieves issues matching the given filters. It returns the matching issues, the total count of matching rows (ignoring Limit/Offset), and an error.
func ListProjects ¶
ListProjects returns every project, default row first, then by id.
func ListProposals ¶
func ListProposals(db *sql.DB, projectID int, status string, criticality string, domainTag string, limit int) ([]*model.Proposal, int, error)
ListProposals returns proposals with optional filters. It returns the matching proposals and the total count (before limit). A non-zero projectID scopes the list to one project (v12); zero lists every project.
func ListRuns ¶
ListRuns returns runs newest first, and the TRUE total before the limit — the Collection contract needs a total a limit cannot distort, so truncation is computable rather than guessed.
func ListSchemas ¶
ListSchemas returns registered schemas and the TRUE total before the limit — the Collection contract requires a total a limit cannot distort.
func ListWorkflows ¶
ListWorkflows returns registered workflows, newest registration first, and the TRUE total before the limit — the Collection contract requires a total that a limit cannot distort, so truncation is computable rather than guessed.
func LookupIdempotencyKey ¶
LookupIdempotencyKey returns the entity id previously recorded for (scope, key), and whether such a record exists.
A hit means the caller already performed this create — the correct response is to return the original entity with exit 0, not an error. A retried create after a dropped response must succeed, or the key is useless to the caller it exists for.
func LookupIdempotencyKeyTx ¶
LookupIdempotencyKeyTx is LookupIdempotencyKey inside a CALLER'S transaction.
It exists for the same reason the plural form does: internal/db caps the pool at one connection, so a reader holding an open transaction cannot ask this question through the pool without deadlocking permanently. The single-key form is the one a caller wants when it knows exactly which key it is after — a prefix scan to find one row would read the whole family to discard it.
func LookupIdempotencyKeys ¶
LookupIdempotencyKeys is LookupIdempotencyKeysTx through the POOL, for a reader that holds no transaction — the run report's rollup phase, which deliberately reads everything it can before its snapshot opens (DKT-584's reap-ack attribution). Same query, same escaping; two spellings of one scan would drift at the first key family that mattered.
func LookupIdempotencyKeysTx ¶
LookupIdempotencyKeysTx returns every (key, entity id) in one scope whose key starts with prefix, inside a CALLER'S transaction.
It exists for the reader that needs MANY of these at once and holds the single pooled connection while asking: one query over a family of keys rather than one per key, resolved where a pool read would deadlock rather than fail. What a prefix MEANS is the caller's business — this matches bytes.
A `%` or `_` inside prefix is escaped rather than passed through to LIKE, so a caller cannot accidentally widen its own question with a key it built out of data.
func LookupProject ¶
LookupProject resolves an identity to its project WITHOUT creating anything.
It is the read half EnsureProject used to keep private, split out for DKT-58: every invocation must be able to ask "which project is this?", and only some of them may answer "a new one".
func LoopGrantsTx ¶
LoopGrantsTx reads how many ADDITIONAL fix loops an operator has authorized for one issue in one run (DKT-237). Zero on every issue nobody has reopened.
func MarkExpandedTx ¶
MarkExpandedTx stamps an issue as expanded — the record that this issue's phase is done, so re-activation skips it.
func MarkStepAttemptFailedTx ¶
MarkStepAttemptFailedTx counts one claim ended by an explicit `step fail` into the step's outcome breakdown (v23, DKT-490).
It exists because `attempt` spends one count per claim WHATEVER the ending, and a consumer that needed "how many attempts genuinely failed" had nothing to read but the event log — which is prunable, and whose instance labels repeat across a run's issues. Both `step fail` branches call it (the below-budget return to the pool and the exhausted routing): each is a claim whose holder measured its own work and recorded the failure.
It is a COUNTER BUMP ONLY — the lease write it accompanies (ReapStepTx or RetireStepTokenTx) stays the caller's, per ReapStepTx's mechanism/ classification split. It follows SetStepMetadataTx's shape — row_version bump, updated_at_ms — because a counter move is a step-row mutation CAS-guarded readers must see.
func MarkStepClaimReapedTx ¶
MarkStepClaimReapedTx counts one claim reaped WITHOUT a recorded failure into the step's outcome breakdown (v23, DKT-490) — MarkStepAttemptFailedTx's other half, called on the reap paths only: the lazy expiry reap (`next`, `dispatch open`, `claim`) and the forced `step reap`.
The distinction is the whole point. A reaped claim spent an `attempt` without anything failing — the holder went silent, or an operator asserted it dead — and an escalation policy that cannot tell that from a measured failure escalates on it, which is the DKT-490 misread. `step fail`'s return to the pool shares ReapStepTx's row reset but never this counter.
func MarkStepUsageRecordedTx ¶
MarkStepUsageRecordedTx sets `steps.usage_recorded` — the fast path group 2's discrepancy probe reads.
Group 1 writes it so the column is populated from the moment the ledger exists: a probe that arrived later and found the column empty on steps that DID record usage would report every one of them as a discrepancy.
WHAT THE COLUMN MEANS is "the ledger question is SETTLED for this step", and it has exactly one reader — engine's `missingUsage` — which is what makes that reading the operative one. There are three ways to settle it: recording usage (budget.go), backfilling it (backfill.go), and an operator's `dispatch close --accept-missing-usage` (DKT-315), which settles the question without answering it. The three are distinguished in the RECORD — the ledger rows exist for the first two and not the third, and the acceptance rides in the `dispatch-closed` event — not in this flag, whose whole job is to let the probe skip a join.
func Migrate ¶
Migrate checks the current schema version and applies any pending migrations sequentially. It is a no-op when already at the latest version.
func MoveIssueProject ¶
func MoveIssueProject( conn *sql.DB, issueID, targetProjectID int, author string, nowMS int64, ) ([]int, error)
MoveIssueProject migrates a ROOT issue — and its entire sub-issue tree — to another project (DKT-27).
Gaps recorded by `step complete --gap-file` land in the run's own project unconditionally, because cwd is the record's only routing; when the surfaced work belongs to another repository, this verb is how the residue gets re-homed without export/import. Labels are re-mapped by NAME into the target project (created there when missing, color preserved) because label rows are per-project; relations and comments ride along untouched — ids are store-wide, so cross-project relations stay resolvable.
It returns the migrated issue ids, the root first.
func NextActionOrdinalTx ¶
NextActionOrdinalTx returns the ordinal a new result for this step+action takes.
Ordinals are per (step, action) and ascend, so flaky-declared re-runs are recorded individually. The same holds for the resume case: a routing stage re-entered after a crash must not collide with the interrupted attempt's row, which the UNIQUE(step_id, action, ordinal) index would refuse.
func NextGateOrdinal ¶
NextGateOrdinal returns the ordinal a new result for this step+gate takes.
Ordinals are per (step, gate) and ascend, which is what carries "flaky-declared re-runs recorded individually": each attempt is its own row, never an overwrite and never an aggregate.
func Open ¶
Open opens or creates the SQLite database at the given path. It sets pragmas for WAL mode, foreign key enforcement, and busy timeout, and makes every transaction not opened with sql.TxOptions{ReadOnly: true} BEGIN IMMEDIATE; a read-only one is a deferred BEGIN.
func OpenReader ¶
OpenReader opens a SEPARATE connection to the same database file, for best-effort point reads that must never contend with Open's single writer connection.
Open caps its pool at one connection because SQLite is single-writer — but that same cap means a query issued on THAT connection while it holds an open transaction (a caller mid-`BeginTx`) blocks forever: database/sql serializes all use of one *sql.DB through its pool, transaction included, and there is no second connection for the query to check out. That is a guaranteed self-deadlock, not a race — it fires every time a caller formats or looks up something from inside its own open transaction.
WAL mode (set by Open, and already on disk by the time this connects) is what makes a SECOND, independent connection the fix rather than a new hazard: a reader on its own connection sees a consistent snapshot without blocking, or being blocked by, an in-flight writer transaction on Open's connection.
This is for best-effort lookups only — id rendering, not decision logic — exactly the callers that already tolerate a failed read as "unknown" rather than an error.
func OrphanSubIssues ¶
OrphanSubIssues sets parent_id to NULL for all direct children of the given issue. Activity is recorded for each affected child within a transaction.
func PrefixHolder ¶
PrefixHolder reports the id of the project holding prefix, other than `exclude`. It returns 0 when the prefix is free.
func ProjectPrefix ¶
ProjectPrefix reads one project's display prefix — the root hook's second query, feeding model.SetDisplayPrefix before any command runs.
func ProjectRefCounts ¶
ProjectRefCounts reports how many rows in each project-scoped table belong to a project. Tables with no rows are omitted.
func ProposalIDsNaming ¶
ProposalIDsNaming returns the ids of proposals whose description or rationale names `token` as a whole word, ordered by id (DKT-584).
It exists for the conversational-gate ballots a conductor opens with `vote create` — an activation panel, say — which carry no idempotency key and no step, and whose ONLY link to the run they gate is that their text names it ("activation panel for RUN-40"). The LIKE narrows candidates in SQL; the word-boundary check runs in Go because LIKE '%RUN-4%' would also match RUN-40, and a run must never inherit another run's panels.
func ProposalStatusesTx ¶
ProposalStatusesTx reads many proposals' statuses in ONE query, inside a caller's transaction.
It exists for the reader that holds the single pooled connection and needs a handful of these at once: internal/db caps the pool at one connection, so a pool read from inside an open transaction deadlocks permanently rather than failing, and a per-proposal GetProposal loop from such a reader is the shape that produces it.
Status ONLY. A report that wanted the whole row would be asking for a different function; narrowing it here keeps this from becoming a second GetProposal that drifts from the first.
func ReMintStepTokenTx ¶
ReMintStepTokenTx replaces the token on a LIVE lease held by `owner`, leaving every other fact of the claim alone (DKT-1564).
It exists for the executor whose own claim committed and then lost its token — the process exited before the response reached it. The lease is genuinely that executor's, but a token is returned exactly once, so without this the only exits are the full TTL and an operator-gated reap.
IT IS NOT A CLAIM. `attempt`, `started_ms` and the claim's event are untouched: nothing about the scheduling facts changed, and the budget floor sums `step-claimed` events, so a second one would bill the run twice for one claim. The guard is the OWNER plus lease liveness — the two conditions that make the claim the caller's — so it can only ever re-key a lease the caller already holds, never award one.
The previous token stops authorizing, which is the point: one lease carries one live capability, and the stranded one is by definition unreachable.
func ReapStepTx ¶
ReapStepTx returns an expired step to the unclaimed pool: lease cleared, status back to `pending`, `started_ms` cleared, `attempt` LEFT ALONE.
attempt already counted this try — it incremented at claim — so incrementing again here would double-count a single death. "Returning them to ready with attempt++" is satisfied by the claim's own increment: the trail records one attempt per claim, for all time, which is what a complete attempt trail asks for.
It is MECHANISM, not classification: `step fail`'s below-the-budget branch shares it to return a failed step to the pool, so the outcome counters (DKT-490) deliberately live outside it — each caller records what actually ended the claim, MarkStepClaimReapedTx on the reap paths and MarkStepAttemptFailedTx on the failure paths. Folding either bump in here would classify a failure as a reap at exactly the call site that knows better.
This is one of the two places a lease write may happen (lazy reaping is confined to next/claim). No read verb reaches it.
func RecordActivity ¶
RecordActivity logs a field change on an issue.
func RecordIdempotencyKeyTx ¶
RecordIdempotencyKeyTx records (scope, key) -> entityID inside tx.
It MUST be called in the same transaction as the insert it protects, so a crash between the two cannot orphan either. The (scope, key) primary key makes a concurrent duplicate a database constraint rather than an application-level check.
created_at_ms and seq are millisecond-resolution and monotonic. They live here, in a table created at v5, and never on a pre-existing column — mutating an existing timestamp format would break byte-compatibility for every existing verb.
func RefreshClaimLeaseTx ¶
RefreshClaimLeaseTx recomputes a live claim's `expires_ms` from the caller's own time, guarded by the claim identity.
WHY IT EXISTS: a claim with pre-gates runs subprocesses between the CAS and the response, and all of that wall time would otherwise be deducted from a lease the caller has not yet received. With a short TTL the caller can be handed an ALREADY-EXPIRED lease, so its first `step complete` fails on a lease it never had a chance to use — a livelock shaped like a too-short TTL but caused by docket's own pre-gate phase.
IT IS A HEARTBEAT BY ANOTHER NAME, NOT A SECOND AUTHORIZATION. The guard is the token hash transaction A wrote: the claimant still holds the CAS, which is exactly the condition the lease model already sanctions for extending a live claim. It can only FAIL, never award a claim, so the single-winner property is untouched — a claim lost during phase 2 matches zero rows here and the caller gets CONFLICT in the ordinary way.
It reports whether the refresh landed rather than erroring on a miss, because "the claim moved on" is an expected outcome the caller renders as CONFLICT, not a database failure.
func RegisteredVoteRules ¶
RegisteredVoteRules lists the rules that have a threshold set, so a refusal can name the alternatives rather than only the mistake.
func RegisteredVoteRulesTx ¶
RegisteredVoteRulesTx lists the set rules — store-wide plus the project's own — so a refusal inside activation can name the alternatives exactly as `workflow register`'s does.
func ReleaseIssue ¶
ReleaseIssue ends a live lease held by token.
attempt survives: it counts claims for all time, so releasing does not erase the trail of what has already been tried.
func ReleaseStepLeaseTx ¶
ReleaseStepLeaseTx returns a step to the UNCLAIMED pool without touching its status, its attempt, or its saga (DKT-259).
It exists because `pending` and a live lease are a CONTRADICTION that the system had no way to express and every reader disagreed about. `claimPredicate` says a step is claimable only when `owner` is null or empty or `expires_ms <= now`, so a step returned to `pending` with its lease intact is a step the scheduler offers and no claimant can take. What CAN still happen is the worst case: the ORIGINAL holder's token is still valid, so it re-records without re-claiming — and `attempt` increments only at claim, so the second execution lands on the first one's attempt number.
RUN-13 STEP-132 is what that costs. The step ran twice with two gate rounds and two artifact sets, `run report` said `attempts: 1`, and one execution's usage was permanently unrecordable because `usage_ledger`'s `UNIQUE(step_id, attempt, unit)` had already been taken by the other.
It is deliberately NARROWER than ReapStepTx: no status write, no `started_ms` clear. A reap decides what the step becomes; this decides only that nobody holds it, and leaves the caller to say the rest. Two callers with different intentions sharing one helper is how a release grows a status write that surprises one of them.
func RemoveLabelFromIssue ¶
RemoveLabelFromIssue detaches a label from an issue. Returns an error if the label is not found or is not attached to the issue. Activity is recorded and the issue's updated_at timestamp is touched.
func RemoveLabelsFromIssue ¶
RemoveLabelsFromIssue detaches multiple labels from an issue atomically within a single transaction. Returns an error if any label is not found or not attached — no labels are removed on failure. Activity is recorded for each removed label and the issue's updated_at timestamp is touched once.
func RemoveRunIssue ¶
RemoveRunIssue detaches an issue from a run. The caller enforces WHEN this is legal (DKT-53: only while the run is in `planning` — after activation the issue is bound, snapshotted, and possibly scheduled, and a bare row delete would strand all three). Returns ErrNotFound when the issue was not attached, so a typo reads as a miss rather than a success.
func RenderMetadataValue ¶
RenderMetadataValue turns one opaque metadata value into the string the rollup groups by.
A non-string value is rendered as its JSON, which keeps `1` and `"1"` DISTINCT — they are different values in the bag, and merging them would be the rollup deciding they mean the same thing.
EXPORTED for DKT-868's per-step section, which renders the same column in the same document: the rollup spells a value one way and a report that spelled the per-step copy another would show a reader `1` in one section and `"1"` in the next and invite them to conclude the two came from different bags. One definition, so the two cannot disagree.
func ReportedUsageTx ¶
ReportedUsageTx sums the ledger for ONE unit — the run cap's `reported`.
One unit, never all of them: summing `{tokens: 4000, seconds: 12}` to 4012 would be core asserting those add up.
func ResetStepRetryBudgetTx ¶
ResetStepRetryBudgetTx refreshes a step instance's retry budget — `step resolve --as retry` (retry resets attempts) — by moving `attempt_base` to the current attempt. Exhaustion compares `attempt - attempt_base` against `max_attempts`, so the budget reads zero-spent from here on.
`attempt` ITSELF IS NEVER RESET (DKT-86, DKT-90). It is the usage ledger's key half (`UNIQUE(step_id, attempt, unit)`) and the "claims made against this step, ever": zeroing it here made a retried step's next claim re-mint an attempt number the ledger had already recorded, and the re-execution's genuinely distinct usage became permanently unrecordable through `dispatch backfill-usage`. The retry budget and the ledger key are different facts, and the base column is what lets one column serve as neither's lie.
This is still a DIFFERENT counter from the issue-level `attempt` v6 declared monotonic, on a different entity: the step's budget is live against `max_attempts`, the issue's trail is permanent. Both statements stay true because they are about different rows.
func RestoreSchema ¶
RestoreSchema clears a version's retirement, returning it to service. It is idempotent, as RestoreWorkflow is: a version that was never retired comes back unchanged.
func RestoreWorkflow ¶
RestoreWorkflow clears a version's retirement, returning it to binding.
It exists because retirement is a routing decision and routing decisions get reversed. Without it the only way back would be to re-register the same name at a HIGHER version, which changes what runs pin for a change the operator did not intend to make.
func RetireStepTokenTx ¶
RetireStepTokenTx is the saga's stage-1 hinge: the token retires when the artifact records.
Retirement is the same state change as release — no owner, no hash, no expiry — so it is the shared clearLeaseTx rather than a second UPDATE that must agree with it. What differs is the AUTHORITY: release is the holder giving the lease back, retirement is the engine taking ownership of a saga that no longer needs one. After this commits, `complete` is AUTH_ERROR and any engine invocation may resume the saga.
func RoutingRecord ¶
RoutingRecord is the stored routing string: the routing alone, or `<routing>: <reason>` when a reason was given. It is exported because the engine builds the same shape for the `step-routed` event and for the cascade-abandon update, and a second spelling of it would let the readers that split this string drift from the write that produces it.
func RunConductorHash ¶
RunConductorHash is RunConductorHashTx on the connection, for the CLI's advisory read: whether a verb must look for a token at all. The authoritative check runs inside the verb's own transaction.
func RunConductorHashTx ¶
RunConductorHashTx reads a run's conductor capability hash inside a transaction. The empty string means UNBOUND — a run activated before v29 and never conducted — which the engine treats as "no capability exists, so nothing is required", never as a hash the empty token could match.
func RunEverDispatchedTx ¶
RunEverDispatchedTx answers the usage check's SCOPE: has this run EVER opened a dispatch?
The question is deliberately about HISTORY rather than about an open manifest. A relay that ever dispatched is accountable for every step of that run, including the ones it spawned outside a manifest — which is the drift the check exists to catch. A run no relay ever drove has nobody owing usage, which is what keeps a solo rehearsal and a human-only demo refusal-free.
It reads the `dispatches` table rather than the event log because the row outlives nothing: a dispatch that was opened and closed leaves its row, and the row is the cheaper and more direct record of the same fact.
func RunPauseOriginTx ¶
RunPauseOriginTx reads where a run's park was decided. A run that is not parked, and one parked by its own steps, both read `model.RunPauseOriginNone`.
func RunProjectID ¶
RunProjectID resolves the project a run belongs to — the engine's way into the dimension: an engine verb operates on a run or step it was handed, and the run row, not any ambient state, says whose project that work is.
func RunProjectIDTx ¶
RunProjectIDTx is RunProjectID inside a caller's transaction.
func SchemaVersion ¶
SchemaVersion returns the current schema version from the meta table.
func SetConfig ¶
SetConfig stores a validated engine-configuration value. A non-zero projectID writes the project's override; zero writes the store-wide default every project falls back to.
It is SetConfigTx in a transaction of its own; the validation, the key shape and the upsert are that function's, stated once.
func SetConfigTx ¶
SetConfigTx is SetConfig inside a CALLER'S transaction (DKT-2563), and it returns the value the SAME scope row held before the write — empty when the key was unset at that scope.
It exists so a write to a security-load-bearing key can be recorded in the same transaction as the write itself: the config-changed event carries the prior and the new value, and an event committed separately from the write it describes could survive a write that rolled back, or vice versa. The prior value is read here, under the transaction, rather than by the caller through GetConfig — a pool read from inside a transaction deadlocks against internal/db's single connection, the same constraint GetConfigTx exists for.
"Prior" is the row at THIS scope (the project's override, or the store-wide default), not the effective value GetConfig would resolve: the event describes what the write replaced, and a project override replaces nothing store-wide.
func SetIssueFiles ¶
SetIssueFiles replaces all files for an issue (delete existing, insert new). Activity is recorded showing the change from old files to new files.
func SetIssueResolutionTx ¶
SetIssueResolutionTx records how a routing left an issue.
It touches ONLY `resolution`. `abandon-issue` is deliberate about not forcing the issue's status — the run stopping work is a statement about the run, and closing the issue here would take the operator's triage decision away — so the resolution is an additional fact beside the status, never a replacement for it (DKT-245). `updated_at` moves because the row changed; `version` does not, because this is the machine recording an outcome rather than a CAS-guarded edit competing with a caller's read.
func SetIssueScopeGlobs ¶
SetIssueScopeGlobs stores an issue's declared scope as a JSON array, or NULL when the list is empty. NULL and `[]` are different facts — "no scope declared" versus "declared to touch nothing" — and only NULL is the dormant default every pre-existing row carries.
func SetProjectPrefix ¶
SetProjectPrefix stores a project's display prefix, uppercased. Validation is the caller's (model.ValidateProjectPrefix); this only writes.
func SetRunBudgetTx ¶
SetRunBudgetTx writes a run's cap — `docket run budget --set`.
IT ALWAYS BUMPS `row_version`, whether or not a precondition was given. Before this verb existed, the runbook told an operator to raise a cap with `sqlite3` and reminded them to increment the column by hand — "or a concurrent `--if-version` check will pass against a row that changed underneath it". Making that structural rather than a step somebody remembers is most of why the verb is better than the edit.
`ifVersion` is the optimistic-concurrency precondition: non-nil means the UPDATE also requires that version, and a mismatch affects zero rows and becomes ErrVersionConflict. That is the same shape every other CAS verb uses, so `--if-version` behaves identically here and there.
It runs in the CALLER'S transaction because the write and its event are one fact: a cap that moved with nothing in the log explaining why is exactly the gap the runbook warned about when the manual edit was the only option.
func SetRunConductorHashTx ¶
SetRunConductorHashTx binds a run to a conductor capability, or re-keys one (DKT-2465): `hash` is the SHA-256 of a token the caller minted with model.MintToken and returns exactly once. Only the hash is ever stored, the discipline the lease columns follow, so a copied database yields no live capability.
It does NOT bump `row_version`, for `SetRunPauseOriginTx`'s reason: the binding is written beside an activation or as its own attributed event, and neither is an edit to the run an `--if-version` caller is racing.
func SetRunIssueBodyTx ¶
SetRunIssueBodyTx rewrites ONE run-issue's description snapshot and the sha that names it, and nothing else (DKT-2291).
It is SetRunIssueSnapshotTx's counterpart on the other frozen column, and narrow for the same reason: the body refresh re-reads exactly the issue's description and must leave `workflow_id` and `issue_snapshot` untouched — a refresh that also re-bound the workflow, or re-snapshotted the scope, would smuggle a second ruling through a verb that says it is about the body. The two columns move together because they are one fact: this text, and the digest that names it.
A miss is a caller error rather than a no-op, as above: the row was read moments ago inside this same transaction, so zero rows affected means the membership the decision was made on no longer stands.
func SetRunIssueSnapshotTx ¶
SetRunIssueSnapshotTx rewrites ONE run-issue's `issue_snapshot` blob and nothing else (DKT-869).
It is deliberately narrower than BindRunIssueTx, which writes the binding and all three snapshot columns together because activation produces them as one fact. The scope refresh is not that fact: it re-reads exactly one field of an already-frozen snapshot and must leave `workflow_id`, `body_snapshot`, and `body_sha256` untouched — a refresh that re-bound the workflow, or rewrote the description snapshot from the live issue, would smuggle the run's mid-run edit immunity out through a verb that says it is about scope.
A miss is a caller error rather than a no-op: the row was read moments ago inside this same transaction, so zero rows affected means the membership the decision was made on no longer stands.
func SetRunPauseOriginTx ¶
SetRunPauseOriginTx records WHERE a run's park was decided, and clears the record — `model.RunPauseOriginNone` — when the park ends (DKT-305).
It is written beside a status transition, never on its own: `origin` is only meaningful for a run that IS `waiting-human`, and a stale origin on a running run would make the rollup decline to resume a run nobody parked. Every caller therefore pairs a set with the pause it describes and a clear with the move that ends it, in the same transaction.
It does NOT bump `row_version`, for the same reason `CacheRunFloorTx` does not: the caller's own status write in this transaction already bumped it, and a second bump for one fact would make `--if-version` collide with itself.
func SetRunStatus ¶
SetRunStatus moves a run's status and stamps `updated_at_ms`, bumping the CAS column. `reason` records why a run is parked or abandoned; passing "" leaves the existing reason alone rather than clearing it, so a resume does not erase why the run was paused.
func SetRunStatusTx ¶
SetRunStatusTx is SetRunStatus inside a caller's transaction, for verbs that must write the transition and its event atomically (DKT-86): a status write that commits while its event does not is a transition the ledger never saw.
func SetStepAuthorityTx ¶
SetStepAuthorityTx records the authority a ruling on the step was made under, and the standing authorization it names. A ruling site calls it in the transaction that writes the ruling's routing, so the row and the ruling event cannot disagree. authorityRef is "" except on a standing grant.
func SetStepClaimPhaseTx ¶
SetStepClaimPhaseTx writes the step's claim phase. It leaves `row_version` alone: the phase is bookkeeping inside one claim, not a transition a CAS reader must observe.
func SetStepGateTrailTx ¶
SetStepGateTrailTx records the accumulated gate results in `gate_trail`, the storage-location deviation: the `gate result` shape rides here until v8's `gate_results` table.
func SetStepLoopHistoryTx ¶
func SetStepLoopHistoryTx( tx *sql.Tx, id, roundsRun int, triggerStep, latestVerdict string, nowMS int64, ) error
SetStepLoopHistoryTx is SetStepLoopHistoryWithCapTx with no recorded cap: it stores loop_cap 0.
func SetStepLoopHistoryWithCapTx ¶
func SetStepLoopHistoryWithCapTx( tx *sql.Tx, id, roundsRun, loopCap int, triggerStep, latestVerdict string, nowMS int64, ) error
SetStepLoopHistoryWithCapTx records the four facts a fix loop leaves behind when it exhausts its bound: the rounds that ran, the bound they were measured against, the instance whose verdict opened the loop, and the verdict the last round ended on.
They are ONE statement because they are one fact — this loop ran this many rounds against this bound from here and ended on this. loopCap is the bound the refusal applied; 0 means no recorded cap, which no refusal can apply because a bound of zero disables the check. latestVerdict is the verdict the trigger round recorded, verbatim, and "" when it recorded none; it is never the routing token that entered the loop. The caller derives all four from the loop state its exhaustion decision already holds.
It follows SetStepMetadataTx's shape — same row_version bump, same updated_at_ms — because this is a step-row mutation like any other and CAS-guarded readers must see it move.
func SetStepMetadataTx ¶
SetStepMetadataTx records a step's opaque KV bag, already merged.
The merge itself is engine-side, in Go (engine.mergeMetadata), so this stays a plain assignment and no JSON function dependency enters the schema layer. The bag is OPAQUE here as everywhere: this writes bytes it never parses.
It follows SetStepRoutingTx's shape — same row_version bump, same updated_at_ms — because a metadata write is a step-row mutation like any other and CAS-guarded readers must see it move. It leaves `recorded_at_ms` alone: annotating a finished step is not a record, and the usage grace measures from records.
func SetStepRoutingTx ¶
func SetStepRoutingTx( tx *sql.Tx, id int, routing, reason, status string, class ParkClass, nowMS int64, ) error
SetStepRoutingTx records the routing a step resolved to, alongside its final status. They are ONE statement because they are one fact — the step ended this way, for this reason — and a status without its routing is a step whose disposition cannot be explained.
`reason` is the explanation for that routing. It is appended to the routing record, as it always was, AND it lands in `park_reason` when — and only when — this write parks the step. That condition lives in the SQL rather than at the call sites so no later caller can park a step without recording why, and so a resolution, which writes a different status, cannot overwrite the question it is answering (DKT-1898).
`park_reason` is documented everywhere it is read as the ENGINE's own text, so a caller may use this form only where `reason` IS the engine's — a gate verdict, a tally, a loop bound's sentence. A site whose routing reason is a person's words (a worker's `--note`, a reject note) parks through SetStepRoutingWithParkReasonTx instead, which keeps those words in the routing record and takes the engine's park text separately.
`class` is that same park's ROUTABLE form (DKT-1900), written by the same statement under the same condition, so the two halves of one fact cannot disagree and no reader has to parse the prose to classify a row. The caller derives it from the facts its routing decision already holds — a gate row's verdict, a tally, LoopOutcome.Entered — never from `reason`.
A park with no class is REFUSED rather than stored. The column would otherwise fill with empty strings at whichever park site a later change forgot, and a classifier reading "" cannot tell an unclassified park from one that predates the column. The guard is the only runtime check here because it is the only one the invariant cannot get from a type: `class` being a ParkClass stops a wrong WORD, and this stops a missing one.
func SetStepRoutingWithParkReasonTx ¶
func SetStepRoutingWithParkReasonTx( tx *sql.Tx, id int, routing, reason, status string, class ParkClass, parkReason string, nowMS int64, ) error
SetStepRoutingWithParkReasonTx is SetStepRoutingTx with the park's prose supplied apart from the routing's reason.
The two are one string at most park sites, because both are the engine's. They come apart where the routing reason is a person's words: the attempts-exhausted park routes on the failing worker's `--note`, and a refused fix loop on a rejection routes on the operator's reject note. Those words belong in the routing record and the event that carries the decision, where they always were; `park_reason` is the engine's account of why the row is waiting, and this form is how such a site says both without folding one into the other.
`parkReason` is written only when this write parks the step, under the same condition as `class`, and is otherwise ignored. A move into a terminal status stamps `recorded_at_ms`, as SetStepStatusTx does.
A park is NEVER stored with an empty reason: `step show` omits an empty `park_reason`, so the row would read as a step that never parked. A blank reason falls back to the class word — engine text, and true — rather than being refused like a missing class, because the reason at some sites is a gate's own output, and a gate that failed silently must still park its step rather than fail the completion that recorded it.
func SetStepStatusTx ¶
SetStepStatusTx moves a step's status inside the caller's transaction, bumping the CAS column and refreshing `updated_at_ms`. A move into a terminal status also stamps `recorded_at_ms`.
`activityMS` refreshes the saga's activity clock when non-zero — "every stage commit refreshing the step's activity clock". Passing 0 leaves it alone, which is what a non-saga transition wants.
func SortedUnits ¶
SortedUnits orders unit names for a deterministic rendering. It is here rather than at the call site so every renderer of an opaque-unit map orders it the same way, whatever the map carries beside the names.
func SplitNameList ¶
SplitNameList parses a KindNameList value into its names, ignoring the space an author may have written after a comma. An empty value yields no names, which is how every KindNameList key spells "unset".
It is the ONE reader of the encoding, so a caller can never disagree with the validator about where one name ends and the next begins.
func StartStepTx ¶
StartStepTx stamps `started_ms` — the schedule-to-close clock `max_step_duration` is evaluated against.
It is set at CLAIM, not at first heartbeat, and only when NULL. That is what makes the bound schedule-to-close rather than activity-to-close: a runaway holder that heartbeats forever cannot push its own deadline out, because the deadline was fixed the moment it took the lease. A re-claim after expiry re-stamps it (the new holder gets its own full budget); the `IS NULL` guard is against the same holder's later stages, not against a new attempt.
func StatusReachedAt ¶
StatusReachedAt returns, for each of the issues, when its status was last set to status, read from the activity log (second precision). An issue the log holds no such change for is absent from the map.
func StepAttemptsFor ¶
StepAttemptsFor reports each step's attempt count for the report, keyed by step id.
func StepInputCount ¶
StepInputCount counts the artifacts recorded as `stepID`'s inputs at its most recent claim (`step_inputs`, DKT-1054). It is the one generic way to answer "how many upstream artifacts fed this step": the table holds only the LATEST attempt's bindings, and a step never claimed (an action step) has no rows at all.
func StepOffScheduler ¶
StepOffScheduler reports whether a status takes a step OUT OF THE SCHEDULER'S ANSWER — a strictly wider question than StepTerminal's (DKT-65).
The two are different because a step's life can pause without ending. A `waiting-human` step is very much alive — an operator will approve, reject, or resolve it — but `next` does not offer it, so anything comparing a stored manifest against a fresh recomputation must expect it to be ABSENT rather than to have moved. `dispatch verify` used StepTerminal for that comparison and consequently reported
does not match its current rendering ... recomputed: (no row at this position)
with exit 4 for a step that had recorded correctly and legitimately parked.
THE ASK NAMED THREE STATUSES AND THE STEP MACHINE HAS ONE. `paused` is a RUN status and `held` is a SAGA STAGE (SagaHeld); neither is ever written to `steps.status`, whose persisted vocabulary is the nine constants above. A paused run removes its steps from the scheduler through their run, not through their own status, so this predicate covers what a step can actually say about itself and the run-level case stays the run's to answer.
It is a SEPARATE predicate rather than a widening of StepTerminal because the callers that ask "is this step over?" — reap, claim, re-offer — must keep getting `false` for `waiting-human`: a step waiting on a person is one an operator is still expected to act on.
func StepStatusCounts ¶
StepStatusCounts returns a run's steps grouped by status, sorted by status so the rendering is stable.
func StepTerminal ¶
StepTerminal reports whether a status ends a step's life. A terminal step is never re-offered by `next`, never claimable, and never reaped.
func UnlinkDocIssue ¶
UnlinkDocIssue removes a doc↔issue link. Returns ErrNotFound if no such link exists.
func UnlinkProposalDoc ¶
UnlinkProposalDoc removes a proposal↔doc link. Returns ErrNotFound if no such link exists.
func UnlinkProposalIssue ¶
UnlinkProposalIssue removes a link between a proposal and an issue. Returns ErrNotFound if the link does not exist.
func UpdateDoc ¶
UpdateDoc applies the changes in upd to the doc with the given ID and appends one revision row capturing the combined change_kind. Returns the new revision number (0 if no revision appended because nothing changed).
Every persisted field change appends one revision; the change_kind is comma-joined ("+" separator) for multi-field edits. Body equality uses strings.TrimRight(s, "\n") so trailing-newline-only edits are no-ops and do NOT append a revision.
Returns ErrNotFound if id does not exist.
func UpdateIssue ¶
UpdateIssue updates an existing issue. Only keys present in the updates map are modified. The updated_at timestamp is always set to the current time. Activity is recorded for each changed field within the same transaction.
Field names are validated against validUpdateFields, but callers are responsible for validating field values (e.g. ensuring status/priority/kind are valid enums) before calling this function.
func UpdateIssueCAS ¶
func UpdateIssueCAS(db *sql.DB, id int, updates map[string]interface{}, changedBy string, ifVersion *int) error
UpdateIssueCAS is UpdateIssue with an optional optimistic-concurrency precondition. When ifVersion is non-nil the update applies only if the issue's current version matches, and returns ErrVersionConflict otherwise. The version is bumped either way, so concurrent CAS writers are detected even when this caller did not supply a precondition.
func UpdateIssueCASLease ¶
func UpdateIssueCASLease(db *sql.DB, id int, updates map[string]interface{}, changedBy string, ifVersion *int, token string) error
UpdateIssueCASLease is UpdateIssueCAS for a verb that ends a lease: when the issue carries a LIVE lease, the caller must hold it (token) and the lease is cleared as part of the same transaction — the issue-level analog of a step's token retiring when its artifact records.
When no live lease exists the token is ignored entirely and behavior is exactly UpdateIssueCAS's. That is the dormancy guarantee: an unclaimed issue is outside the lease mechanism, so a repo that never claims sees no change on any verb.
func UpdateIssueCASNote ¶
func UpdateIssueCASNote(db *sql.DB, id int, updates map[string]interface{}, changedBy string, ifVersion *int, note string) error
UpdateIssueCASNote is UpdateIssueCAS with a note recorded as an issue comment in the SAME transaction as the update (DKT-480), so moving an issue and saying why is one verb: the two either both land or neither does, where the two-verb `issue comment add` + `issue move` form can leave a comment narrating a move that was refused.
An empty note is exactly UpdateIssueCAS, down to the writes it makes. A note with no field to update still records — a move to the status an issue already holds must not swallow the reason it was given — and does NOT bump the issue's version, since a comment never has.
func UsageBudgetUnitTx ¶
UsageBudgetUnitTx reads `budget.usage.unit` — the unit the MEASURED cap counts (DKT-238). Empty leaves that whole dimension dormant.
func ValidateConfigValue ¶
func ValidateConfigValue(spec ConfigSpec, value string) error
ValidateConfigValue checks a value against its key's kind.
func ValidateNameList ¶
ValidateNameList checks a comma-separated list of opaque names: each entry's shape, no empty entry, and no duplicate.
`noun` names one entry in the refusal, for validateName's reason.
func ValidateUnitName ¶
ValidateUnitName is the shape rule for a unit name, shared by `--usage`'s parser and `docket config set budget.unit`.
`tokens`, `pages`, and `sheets` are equally valid: core has no list.
func VoteRuleCriticalityKey ¶
func VoteRuleExists ¶
VoteRuleExists reports whether a rule is registered.
A rule EXISTS IFF its threshold is set. Criticality has a default, so it cannot be the existence test: a rule with only a criticality set would tally at no threshold at all.
func VoteRuleExistsTx ¶
VoteRuleExistsTx and RegisteredVoteRulesTx are the two above inside a CALLER'S transaction, for auto-registration: the scan validates a workflow's `vote_rule` references with the SAME rules `workflow register` applies, and it does so inside activation's fat transaction, where a pool read would deadlock against the one-connection pool.
They read `meta` directly rather than going through GetConfig, because a vote rule's key is DYNAMIC (`vote.rule.<name>.threshold`) and has no spec row to look up — which is exactly what VoteRuleExists' own `Source == "set"` check is testing for. "Set" here means "a row exists", and that is one query.
func VoteRuleSealedKey ¶
func VoteRuleSetElsewhere ¶
func VoteRuleSetElsewhere( db *sql.DB, projectID int, rule string, ) (projects int, value string, err error)
VoteRuleSetElsewhere counts how many OTHER projects configure a rule, and returns one of the values they use (DKT-264).
It exists because "vote_rule X is not registered" was true and useless in the one case that actually happens: a corpus workflow is shared across every project and its thresholds are not, so a freshly-registered project refuses a definition that works everywhere else in the store. The remedy is one store-wide set — the fallback for it has existed all along — and nothing said so, which is how thirteen projects came to hold thirteen per-project copies of the same three thresholds while `config.vote.rule.*` sat empty.
The VALUE is returned so the refusal can quote a real number instead of `<0-1>`. It is one of the values in use rather than a consensus: if two projects disagree, the operator is the one who decides which is right, and inventing an average would be core forming an opinion about a threshold.
The project's OWN row is excluded, so this answers "elsewhere" literally. A caller reaching this has already established the rule is unset here.
func VoteRuleSetElsewhereTx ¶
func VoteRuleSetElsewhereTx( tx *sql.Tx, projectID int, rule string, ) (projects int, value string, err error)
VoteRuleSetElsewhereTx is VoteRuleSetElsewhere inside a caller's transaction, for activation's auto-registration — where a pool read would deadlock against the one-connection pool rather than fail.
func VoteRuleThresholdKey ¶
VoteRuleThresholdKey, VoteRuleCriticalityKey, VoteRuleSealedKey and VoteRuleHoldOnDissentKey build a rule's four keys, so the string concatenation lives in one place rather than at every reader.
Types ¶
type AckReapResult ¶
type AckReapResult int
AckReapResult is what an acknowledgment did, so the caller can tell an idempotent success from a forgery refusal without re-querying.
const ( // AckRecorded is the first acknowledgment of a real reap. AckRecorded AckReapResult = iota // AckAlreadyDone: a second ack of the same seq succeeds and changes // nothing, so a relay retrying its hook does not fail. AckAlreadyDone // AckNoSuchReap: the seq names no unacknowledged-or-acknowledged reap // of this run. The caller raises VALIDATION_ERROR — an ack must name a real // reap, and this is the forgery point. AckNoSuchReap )
func AckReapTx ¶
func AckReapTx(tx *sql.Tx, runID int, seq int64, ackedBy string, nowMS int64) (AckReapResult, error)
AckReapTx is a CAS on `acked_at_ms IS NULL` for the row whose `reaped_seq` matches, SCOPED TO THE RUN.
The run scope is the forgery refusal's other half and it is not incidental: an ack naming another run's reap is a forgery in exactly the way naming a non-reap is, and a CAS without the scope would let a relay clear a hold on a run it is not driving.
The three-way return is what makes already-done and no-such-reap distinguishable. A CAS that matched zero rows means EITHER already-acknowledged OR no-such-reap, and those need opposite answers — one is a success, the other is a refusal — so the existence probe runs on the zero-rows path rather than being inferred.
type ActionResultRow ¶
type ActionResultRow struct {
ID int
RunID int
StepID int
// Action is the `action` value — the builtin's name, or the trust entry
// name a non-builtin looked up. Core carries it and never interprets it.
Action string
Ordinal int
Argv []string
Exit *int
DurationMS int64
Output string
Truncated bool
Verdict string
// Builtin marks a result core computed itself. It is the field that lets an
// operator tell `aggregate` — which consults no trust store — from a
// user-trusted command that happened to succeed.
Builtin bool
// Reason explains an `unmatched` verdict, a refusal, or a failure, for the
// same four-causes-one-verdict reason the gate row carries one.
Reason string
CreatedAtMS int64
}
ActionResultRow is one recorded action execution — the `action result` shape.
IT IS `GateResultRow` FOR ACTIONS, deliberately and field for field. The alternative — recording nothing and leaving the routing reason as the only trace — makes an `unmatched` action invisible to an audit of the run, which it must not be, and makes a failed computation indistinguishable from a failed threshold in a run report. Same shape, same ordinal semantics, same reason discipline, so `run report` reads one pattern twice rather than two patterns once.
Argv and Exit are POINTERS for the reason they are on a gate result: a builtin spawns nothing and an unmatched action never ran, so NULL is the honest encoding of "no process existed". A zero exit on something that did not execute reads as success.
func ActionResultsForStep ¶
func ActionResultsForStep(conn *sql.DB, stepID int) ([]ActionResultRow, error)
ActionResultsForStep returns every recorded result for a step, in insertion order.
type Artifact ¶
type Artifact struct {
ID int
RunID int
StepID int
Kind string
Body string
// Payload is the structured half, validated for SHAPE at stage 0; schema
// validation is the schema register's. It is JSON text or "".
Payload string
SHA256 string
// Stub is 1 ONLY on artifacts the pre-v9 stub action runner produced, and
// it is `omitempty` for the reason gate_results' own marker is: a result
// THIS stage produces serializes with no `stub` key at all.
//
// NOTHING WRITTEN FROM NOW ON SETS IT. The column exists so a migrated
// artifact stays distinguishable forever — the migration marks the rows
// whose payload carries the old `{"stub":true,…}` wrapper and leaves their
// BYTES alone, because rewriting them would destroy the evidence that a
// computation did not run.
Stub bool `json:"stub,omitempty"`
// Supersedes names the artifact this one REVISES, or nil for an original
// (v15, DKT-70).
//
// A held cluster's resolution records a new artifact rather than annotating
// the old one. It shares the original's `kind`; its body is
// regenerated from the resolved payload and its `sha256` covers body and
// payload both (DKT-112), so two links of the chain never share a content
// address while their payloads differ.
//
// The pointer says which of the two is the record of a computation and
// which is the record of a decision, WITHOUT rewriting either. A consumer
// counting work counts `Supersedes == nil`.
Supersedes *int `json:"supersedes,omitempty"`
CreatedAtMS int64
}
Artifact is one `artifacts` row: what a step produced.
func GetArtifactTx ¶
GetArtifactTx reads ONE artifact by id, inside a transaction — the reader for an id pinned at activation (DKT-547's `issue.linked` form), where the row may belong to another run entirely and the run-scoped listing above cannot reach it. ErrNotFound when no such row exists.
func ListRunArtifacts ¶
ListRunArtifacts reads a run's artifacts ordered by id — the tie-break the input resolution order ends on, so the read order and the resolution order agree without a second sort.
func ListRunArtifactsTx ¶
ListRunArtifactsTx is ListRunArtifacts inside a transaction — context assembly's reader, which must see the artifact set as of the claim it is part of.
func ListStepArtifacts ¶
ListStepArtifacts reads the artifacts ONE step produced, ordered by id.
The run-scoped reader above answers "what does this run hold", which is the question context assembly and the run report ask. This answers "what did this step produce", which is the question an operator reading a finished step asks — and which previously had no answer short of opening .docket/issues.db with sqlite.
`step_id` is nullable in the schema (a run-scoped artifact has none), so this deliberately matches on equality and never returns those: an artifact with no producing step is not this step's output.
func ListStepInputArtifactsTx ¶
ListStepInputArtifactsTx reads the artifacts a step's claim RECORDED as its inputs (InsertStepInputTx), ordered by id like every other artifact reader — the snapshot `step context` replays for a step that has been handed out (DKT-1054).
It is the artifact rows themselves, not the (position, artifact) pairs, because the reader re-runs the input resolution over exactly this set: the declared-position order, the engine-produced forms that bind no artifact (`issue.body`, an empty `issue.diff`, `gate-results`), and the within-input sort all come from the same code the claim ran, so a replayed bundle has the claim-time bundle's shape by construction rather than by a second rendering of it. Empty for a step whose claim bound no artifact at all.
type CastVoteResult ¶
type CastVoteResult struct {
Vote *model.Vote
ProposalStatus model.ProposalStatus
VotesCast int
VotesRequired int
QuorumReached bool
WeightedScore *float64
}
CastVoteResult holds the outcome of a CastVote operation, including whether quorum was reached and the proposal's updated status.
func CastVote ¶
CastVote inserts a vote and auto-finalizes the proposal when quorum is reached. Returns ErrNotFound if the proposal does not exist. Returns ErrConflict if the voter already voted or the proposal is already finalized.
The tally is the DECLARED one — each cast weighted by its own confidence times its own domain relevance — which is what every caller before DKT-2512 got and what an ad-hoc proposal still gets. A vote step that declares `weighting = "equal"` reaches the same function through CastVoteWeighted.
func CastVoteWeighted ¶
CastVoteWeighted is CastVote with the tally's weighting chosen by the caller (DKT-2512). With `equal` set, every cast weighs 1.0 × 1.0 in the score — the vote ROW still keeps the confidence and domain relevance the caster declared, in their existing columns, so what a seat claimed about its own testimony is on record even where it does not price the tally.
The flag is passed per cast rather than stored on the proposal because the tally happens inside the quorum-reaching cast, and the value comes from the vote step's PINNED definition, which cannot change under a live ballot.
type ClassMaxOverride ¶
ClassMaxOverride is one resolved limits.max.<class> value: the cap, and whether it came from ClassMaxAuto rather than a number.
type ConfigEntry ¶
type ConfigEntry struct {
Key string `json:"key"`
Value string `json:"value"`
Source string `json:"source"` // "set" or "default"
}
ConfigEntry is one key's effective value and where it came from.
func GetConfig ¶
GetConfig returns a key's effective value for one project, and whether it was explicitly set.
Resolution (v12): the project's own override, then the store-wide value, then the builtin default. A zero projectID reads the store-wide value directly. An unset key returns its default with source "default", so a caller can tell "nobody configured this" from "somebody configured this to the same value".
func GetConfigTx ¶
GetConfigTx is GetConfig inside a CALLER'S transaction, for readers that must resolve a key while holding the single pooled connection — the scheduler's snapshot, which is loaded entirely inside one transaction and would deadlock against a pool read rather than fail (the same constraint VoteRuleExistsTx was written for).
It resolves in GetConfig's order — the project's override, the store-wide value, the builtin default — by delegating the shape decisions to the SAME spec lookup and the same key builders. What it does not share is the per-class TTL fallback: nothing reads a lease TTL from inside a transaction, and a second implementation of a fallback is how two readers of one key start disagreeing. A caller that needs one reaches for LeaseTTL outside the transaction, as every current caller already does.
func ListConfig ¶
func ListConfig(db *sql.DB, projectID int) ([]ConfigEntry, error)
ListConfig returns every fixed key's effective value for one project, plus every per-class TTL and class cap that has been explicitly set — store-wide or as a project override, the override winning. Sorted for deterministic output.
type ConfigSpec ¶
type ConfigSpec struct {
Key string
Kind ConfigValueKind
Default string
Doc string
}
ConfigSpec describes one engine-configuration key.
func LookupConfigSpec ¶
func LookupConfigSpec(key string) (ConfigSpec, error)
LookupConfigSpec resolves a key to its spec. Per-class lease TTLs (lease.ttl.<class>) are matched dynamically: the class is an opaque string, so the set of valid keys is open by design.
type ConfigValueKind ¶
type ConfigValueKind int
ConfigValueKind classifies a config key for validation at `set` time — so a bad value fails where the user can see it, not later at read time.
const ( // KindDuration is a Go duration string ("15m", "2h30m"). KindDuration ConfigValueKind = iota // KindPositiveInt is an integer >= 1. KindPositiveInt // KindNonNegativeNumber is a number >= 0. KindNonNegativeNumber // KindNonNegativeInt is an integer >= 0. KindNonNegativeInt // KindUnitFraction is a float in (0, 1] — a vote rule's approval // threshold, which the existing `vote create --threshold` already takes in // exactly that range. KindUnitFraction // KindCriticality is one of low|medium|high|critical, the criticality the // existing proposal machinery already understands. KindCriticality // KindRetentionWindow is a duration that may also be ZERO, where zero means // "no retention window": prune holds back no event by age. // // It is a separate kind from KindDuration because that one rejects zero — // correctly, since a zero lease TTL would expire a claim the instant it was // made. Here zero is the DEFAULT: a window nobody set adds no age boundary, // and prune's explicit target and live-run refusal still bound what it // deletes. KindRetentionWindow // KindUnitName is an OPAQUE unit name, or empty. Core never enumerates // units and never has a default one, so the only validation possible is the // shape a name must have to be usable as a ledger key — the same caps // `--usage` applies to the names it records: at most 64 bytes, // printable ASCII, no whitespace. Anything beyond that would be core // deciding which units exist. KindUnitName // KindName is a single OPAQUE name, or empty — KindUnitName's rule without // the unit vocabulary, for a key whose value names something other than a // recorded unit. KindName // KindNameList is a comma-separated list of OPAQUE names, or empty. // // It validates each entry's SHAPE and nothing else, for the same reason // KindUnitName does: core never enumerates the members of such a list and // holds no opinion about what any of them denotes. What it does check is // that the list can be split back into the names that were put in — no // empty entry, and no duplicate, since a caller that counts the entries // would count a repeat twice and demand a decision from a name that can // only make one. KindNameList // KindBool is a boolean, in any of strconv.ParseBool's spellings // ("true"/"false", "1"/"0", "t"/"f"). Stored and read back verbatim, like // every other kind — a reader that needs the parsed bool calls ParseBool // itself, the same way a reader of KindDuration calls ParseDuration. KindBool // KindClassMax is a positive integer or ClassMaxAuto: a class's // concurrency cap, where zero or a negative number would admit nothing. KindClassMax )
type CycleError ¶
type CycleError struct {
Path []int
}
CycleError wraps ErrCycleDetected and carries the path of IDs forming the cycle.
func (*CycleError) Error ¶
func (e *CycleError) Error() string
func (*CycleError) Unwrap ¶
func (e *CycleError) Unwrap() error
type Dispatch ¶
type Dispatch struct {
ID int
RunID int
Status string
// OpenedSeq is the event seq at open time — the manifest's place in the log,
// and the boundary for "reaps this relay has not yet seen".
OpenedSeq int64
// ExtendedSeq is the event seq of the manifest's LATEST `dispatch extend`,
// beside OpenedSeq because the two are one manifest's place in the log:
// where it started, and where it last grew (DKT-2071). Zero means never
// extended — an event seq is 1-based, so no append can write one.
//
// The latest, not every one: the event log keeps the full history of
// appends, and a column accumulating them would be a second, worse copy.
ExtendedSeq int64
ExpiresMS int64
ClosedAtMS *int64
CloseReason string
CreatedAtMS int64
RowVersion int
}
Dispatch is one manifest's row.
func GetDispatchTx ¶
GetDispatchTx reads one manifest by id, whatever its status — the read a closing verb makes after its CAS to report what actually happened.
func OpenDispatchTx ¶
OpenDispatchTx reads the run's open manifest, or ErrNoOpenDispatch.
Every open-manifest probe goes through it, so it is ONE query with one definition of "open": the status the partial index keys on.
func (*Dispatch) Expired ¶
Expired reports whether the manifest has outlived its TTL.
It is a method on the row rather than a query so the ONE definition of expiry serves the lazy abandon, the refusal's message, and any read verb that renders a manifest — three callers that must not be able to disagree about whether a dispatch is still live.
type DispatchRow ¶
DispatchRow is one stored manifest row: the canonical bytes, their hash, and the identity they describe.
Both `RowJSON` and `RowSHA256` are stored. `verify` derives its stageless comparison from `RowJSON`, first asserting the bytes still hash to `RowSHA256` — the integrity check on the stored pair — and the spawn guard compares proposed rows against `RowSHA256` verbatim, stage included. The bytes are what a refusal SHOWS the operator: the differing row rather than a report that two rows differ.
func ListDispatchRowsTx ¶
func ListDispatchRowsTx(tx *sql.Tx, dispatchID int) ([]DispatchRow, error)
ListDispatchRowsTx reads a manifest's rows IN POSITION ORDER.
The order is the manifest (it records the resulting rows in order), so it is an ORDER BY rather than an insertion-order assumption: `verify` compares position by position, and a row set that came back in rowid order would make the comparison depend on how SQLite happened to store them.
type DocListOptions ¶
type DocListOptions struct {
ProjectID int // scope to one project (v12); 0 = every project
Types []string
Statuses []string
Author string
Sort string
SortDir string
Limit int
Offset int
}
DocListOptions holds filtering, sorting, and pagination options for ListDocs and ListDocsWithCounts. Mirrors ListOptions/issues but with the doc-table columns.
type DocSummary ¶
DocSummary is a row from ListDocsWithCounts: a Doc plus the JOIN-derived revision count and current revision number.
func ListDocsWithCounts ¶
func ListDocsWithCounts(db *sql.DB, opts DocListOptions) ([]*DocSummary, int, error)
ListDocsWithCounts returns DocSummary rows including JOIN-derived revisions_count and current_revision in a single query (no N+1). Returns total count (before limit) as the second value.
type DocUpdate ¶
DocUpdate is the set of fields UpdateDoc may change. Nil pointers mean "leave unchanged"; non-nil with the current value is detected as a no-op (body equality additionally applies trailing-newline trimming).
type GapIssue ¶
type GapIssue struct {
Title string
Description string
Priority model.Priority
Kind model.IssueKind
Labels []string
// Files and Scope come from the gap's `Files:` and `Scope:` header lines
// (DKT-3294). A nil Scope stores SQL NULL: no scope declared.
Files []string
Scope []string
}
GapIssue is the shape one recorded gap materializes into: the mechanically derived title and the verbatim body, plus whatever ranking the gap's own header block declared.
Priority and Kind carry the CALLER's decision, not a parse: an empty Priority means "the gap declared none" and lands `none`, an empty Kind lands `task`. Both defaults are applied here, once, so a caller that declares nothing gets exactly the row this insert always wrote (DKT-1082).
type GateOverrideGrant ¶
type GateOverrideGrant struct {
ID int
RunID int
OriginStepID int
Gate string
Exit *int
Reason string
// Fingerprint is the origin row's `gate_results.fingerprint`, COPIED at
// mint and never recomputed (DKT-1796): the ruling binds to the content the
// operator read, not to whatever a later re-run happens to print.
//
// EMPTY MATCHES NOTHING, deliberately. A grant minted before v30 recorded
// no content, so it can vouch for none; treating a blank as a wildcard
// would preserve the gate-wide waiver for exactly the runs that were in
// flight across the upgrade. Only a pre-v30 grant is ever blank — a row
// recorded at v30 or later always carries a fingerprint, the empty
// capture's included.
Fingerprint string
// Argv is the origin row's `gate_results.argv`, copied at mint. Nil means
// the origin row recorded none (an unmatched gate never resolved a
// command); it is a value, not a wildcard.
Argv []string
// ArgvLegacy reports a grant whose command is unknown: one recorded before
// v38, or by a binary that does not write the column. Argv is nil for such
// a grant, and the flag is what separates it from a nil-argv mint. The
// insert path never writes it.
ArgvLegacy bool
Note string
CoveredSteps int
CreatedAtMS int64
}
GateOverrideGrant is one operator ruling that a gate's failure signature is environmental for the remainder of ONE run (DKT-546): later steps of the same run whose SAME gate fails with the SAME exit, reason and content fingerprint auto-pass at routing instead of re-asking the operator.
Exit is a POINTER for gate_results' own reason: an `unmatched` gate never ran, so it has no exit code, and NULL must match only NULL — "no process existed" is not exit 0. Reason is compared verbatim; it is the engine's classification field (unmatched cause, timeout, network annotation), not free prose.
The grant is RUN-SCOPED BY CONSTRUCTION — the run_id foreign key is the whole scope rule. A new run re-asks, exactly as DKT-546 requires; nothing here consults project config, and there is deliberately no way to widen a grant past its run.
RUN-SCOPED INCLUDES STEPS THAT DO NOT EXIST YET (DKT-734). A fix round mints new steps INSIDE the same run, so a grant taken on `fix@7`'s park covers `fix@8` and `fix@9` when a later round's same gate fails the same way. That is the design and not a leak: RUN-51 read three authorizations there where one standing authorization was spent three times. Nothing narrower is available — the row has an `origin_step_id`, but it is provenance, never consulted by grantMatches — and the safety is elsewhere: the gates still run every round, a round failing DIFFERENTLY still parks, and each application records its own `step-batch-overridden` event naming this row's id, so the ledger distinguishes one ruling spent N times from N rulings.
func GateOverrideGrantsForRun ¶
func GateOverrideGrantsForRun(conn *sql.DB, runID int) ([]GateOverrideGrant, error)
GateOverrideGrantsForRun returns every grant recorded for one run, in insertion order.
type GateResultRow ¶
type GateResultRow struct {
ID int
RunID int
StepID int
Gate string
Ordinal int
Argv []string
Exit *int
DurationMS int64
Output string
Truncated bool
Verdict string
Pre bool
// Stub is 1 ONLY on rows migrated from the older `gate_trail`. Nothing this
// stage records sets it, which is what makes the migration window auditable
// after the fact.
Stub bool
// StubEntry is 1 when the trust entry that authorized this gate declared
// itself a PLACEHOLDER (DKT-265) — an echo, a `/usr/bin/true`, a script
// that exits 0 without looking at anything.
//
// IT IS A DIFFERENT FACT FROM `Stub` AND THE NAMES ARE NOT AN ACCIDENT.
// `Stub` is about WHICH ERA of this codebase produced the row; `StubEntry`
// is about WHAT RAN. A row can be either, both, or neither, and a reader
// asking "did a secret scan actually happen" is asking the second question
// only.
StubEntry bool
// Reason explains an `unmatched` verdict or a timeout.
// An unmatched verdict has four distinct causes needing four different
// remedies, and without this field they render identically to an operator.
Reason string
// Fingerprint is the SHA-256 of this row's capture with run-varying text
// removed (engine.GateFingerprint), computed at record time — the CONTENT
// half of the failure signature a batch override grant is keyed on
// (DKT-1796). A gate that printed nothing hashes the empty capture, so a
// row recorded at v30 or later always carries a value: EMPTY means the row
// predates v30 and nothing else.
Fingerprint string
// TargetSHA is the commit this PRE-gate row was measured against when the
// measurement ran OUTSIDE the claim — a detached pre-gate run started when
// the step's target was recorded, so a gate longer than the claim's budget
// can still hand the claim a complete result. The
// claim reuses a recorded row only for the same step and the same target,
// and this is the key it matches on. EMPTY on every row the claim itself
// records, on every completion-side row, and on every row recorded before
// v37: none of those was measured for reuse, and a blank is never a key.
TargetSHA string
CreatedAtMS int64
}
GateResultRow is one recorded gate execution — the `gate result` shape as it lives in the v8 table.
Argv and Exit are POINTERS because NULL is meaningful here and zero is not: an `unmatched` gate never ran, so it has no argv and no exit code. Recording `exit = 0` for a process that does not exist is exactly the confusion the gate-forgery threat exists to prevent — a zero exit reads as success. NULL is the honest encoding of "no process existed".
func GateResultsForStep ¶
func GateResultsForStep(conn *sql.DB, stepID int) ([]GateResultRow, error)
GateResultsForStep returns every recorded result for a step, in insertion order.
func GateResultsForStepTx ¶
func GateResultsForStepTx(tx *sql.Tx, stepID int) ([]GateResultRow, error)
GateResultsForStepTx is GateResultsForStep inside a transaction, for the readers that run within one.
type ListOptions ¶
type ListOptions struct {
ProjectID int // scope to one project (v12); 0 = every project
Statuses []string // filter by status (multiple = OR)
Priorities []string // filter by priority (multiple = OR)
Sizes []string // filter by size (multiple = OR); "" never matches a row
Labels []string // filter by label name (multiple = AND)
Types []string // filter by kind (multiple = OR)
Assignee string // filter by assignee
ParentID *int // filter by parent issue ID
RootsOnly bool // only issues with no parent
// RunID scopes the listing to one run's ROSTER — the issues bound to it in
// `run_issues` (DKT-405); 0 = every issue. It is the same membership
// ListRunIssues reads, expressed as a filter so it composes with the other
// filters, the sort, and the pre-limit COUNT rather than being intersected
// afterwards in the caller.
RunID int
IncludeDone bool // include done status (default: exclude)
Sort string // field name
SortDir string // "asc" or "desc"
Limit int // max results
Offset int // for pagination
}
ListOptions holds filtering, sorting, and pagination options for ListIssues.
type MetadataKeyRollup ¶
type MetadataKeyRollup struct {
Key string `json:"key"`
Values []MetadataValueCount `json:"values"`
}
MetadataKeyRollup is one metadata key and every distinct value recorded under it, with counts.
func MetadataRollup ¶
func MetadataRollup(db *sql.DB, runID int) ([]MetadataKeyRollup, error)
MetadataRollup rolls step `metadata` keys up to their distinct values with counts, VERBATIM AND UNINTERPRETED.
THIS IS THE GENERICITY LINE AT ITS THINNEST, so it is worth stating what the implementation does and does not do. It groups by key and by value, both as OPAQUE STRINGS, and reports counts. It does not know that any particular instance puts anything in particular there: it reports `{"tier": {"a": 3, "b": 1}}` for exactly the same reason it would report `{"desk": {"front": 3, "back": 1}}`.
TestMetadataRollupReadsNoKey asserts the implementation contains no key-name literal, which is the mechanical form of that promise — a rollup that special-cased one key would be core having an opinion about what a workflow author's bag of strings means.
The nesting is key -> value -> count, and both levels are returned SORTED so the report's determinism holds through a two-level structure that a naive implementation would emit in map order.
func VoteMetadataRollup ¶
func VoteMetadataRollup(db *sql.DB, scope, prefix string) ([]MetadataKeyRollup, error)
VoteMetadataRollup is MetadataRollup over CAST VOTES: every metadata bag the run's vote-step proposals collected, keys to distinct values with counts, verbatim and uninterpreted (DKT-71).
It exists because vote seats are the one spend the ledger cannot see: a vote step is never claimed, so nothing accrues usage rows for it, and until v13 nothing recorded what model a seat resolved to. The `--metadata` claim on `vote cast` closed the write half; this is the run-level read that makes routing drift measurable again — the same question the step rollup answers, asked of the casts.
The proposals are selected by the caller-supplied idempotency scope and prefix — the engine owns that spelling (voteIdempotencyPrefix) and this package must not restate it. The same genericity line holds here as in MetadataRollup: keys and values are opaque strings, counted, never interpreted, and TestMetadataRollupReadsNoKey covers both readers.
type MetadataValueCount ¶
MetadataValueCount is one value of one key, and how many steps carried it.
type ParkClass ¶
type ParkClass string
ParkClass is the closed vocabulary of WHY a step parked (DKT-1900), the routable half of the fact `park_reason` states in prose.
It is a named type rather than a bare string so a caller cannot hand SetStepRoutingTx an ad-hoc word, and so the compiler lists every producer when a value is added. Each value below names ONE routing decision; no value is derived by reading a reason string, and none is inferred at read time.
const ( // ParkClassGateFailed: a completion gate RAN and did not pass. ParkClassGateFailed ParkClass = "gate-failed" // ParkClassGateUnmatched: a gate named no trust entry, so it never ran. ParkClassGateUnmatched ParkClass = "gate-unmatched" // ParkClassGateSkipped: a gate was skipped because the judged commit could // not be bound, so it measured nothing. ParkClassGateSkipped ParkClass = "gate-skipped" // ParkClassThresholdRouted: a declared threshold evaluated to a park. ParkClassThresholdRouted ParkClass = "threshold-routed" // ParkClassLoopBound: a fix loop was refused because `max_fix_loops` is // spent — LoopOutcome.Entered is false, never the bound's wording. ParkClassLoopBound ParkClass = "loop-bound" // ParkClassLoopStalled: an unauthorized fix loop was refused for // non-convergence — the last round moved no scoped bytes, the routing step // repeated its verdict, or the routed volume held flat past // `max_stalled_rounds` — with budget still left. LoopOutcome.Stalled. ParkClassLoopStalled ParkClass = "loop-stalled" // ParkClassVoteRejected: a vote tallied to a rejection. ParkClassVoteRejected ParkClass = "vote-rejected" // ParkClassDissentHeld: a vote rule's `hold_on_dissent` decision parked an // approved tally because a seat cast `reject`. No threshold matched a park; // the operator's hold displaced the `pass` the tally would have routed. ParkClassDissentHeld ParkClass = "dissent-held" // ParkClassHeldRejected: an operator rejected a held cluster, and the // ROUTING step takes the consequence. ParkClassHeldRejected ParkClass = "held-rejected" // ParkClassGapOnly: a completion whose only artifacts were gaps. ParkClassGapOnly ParkClass = "gap-only" // ParkClassPartialWithGap: a completion that recorded a candidate beside a // gap its writer marked `AC-Blocking` (DKT-4279). ParkClassPartialWithGap ParkClass = "partial-with-gap" // ParkClassUnchangedHandBack: a loop round handed back the commit its // previous round recorded, so the review chain would re-read one tree. ParkClassUnchangedHandBack ParkClass = "unchanged-handback" // ParkClassPassFloor: a `pass` would have left declared-floor work standing. ParkClassPassFloor ParkClass = "pass-floor" // ParkClassActionFailed: an action step's computation could not run. ParkClassActionFailed ParkClass = "action-failed" // ParkClassAttemptsExhausted: the step spent its attempt budget. ParkClassAttemptsExhausted ParkClass = "attempts-exhausted" // ParkClassJoinMissed: a join completed below `min_siblings`. ParkClassJoinMissed ParkClass = "join-missed" // ParkClassTriageUndecided: a triage panel (DKT-1901) closed without a // usable verdict — declined, no quorum, or retired without a tally — so // its `on_fail` fell back to `waiting-human`, the same outcome a step // would have reached with no panel at all. ParkClassTriageUndecided ParkClass = "triage-undecided" // ParkClassHeld: the run's conductor parked one pending step for the // operator through `step hold` (DKT-3287), with its own stated reason. ParkClassHeld ParkClass = "held" )
The park classes. Each is produced by exactly one routing decision, named beside it.
`paused` is deliberately ABSENT. It is a RUN status and is never written to `steps.status` (see StepOffScheduler's note), so no routing transaction can assign it and a constant for it would be a value with no producer.
type Pin ¶
type Pin struct {
RunID int
Kind string
Ref string
SHA256 string
// Bytes is the pinned content's size, carried IN MEMORY ONLY during
// activation — it is not a column and is not persisted.
//
// It exists so the closure-size arithmetic can count a
// declared packet file without opening it a second time: the activation
// scan already read the bytes to hash them, and re-reading at expansion
// would both cost an extra pass and open a window where the two reads
// disagree.
Bytes int
}
Pin is one `pins` row.
type ProposalRunLink ¶
type ProposalRunLink struct {
ProposalID int
RunID int
// IssueID is the lowest run-bound issue the proposal is linked through.
IssueID int
CreatedAt time.Time
}
ProposalRunLink pairs a proposal with a run that binds an issue the proposal is linked to (proposal_issues to run_issues), for linked-panel attribution.
func ProposalRunLinks ¶
func ProposalRunLinks(db *sql.DB, filter ProposalRunLinkFilter) ([]ProposalRunLink, error)
ProposalRunLinks lists every (proposal, run) pair in which the run binds an issue the proposal is linked to, once each, ordered by proposal then run. A proposal whose created_at does not parse as RFC3339 is omitted: its creation cannot be placed in any run's window, so it is attributed to no run by inference.
type ProposalRunLinkFilter ¶
ProposalRunLinkFilter narrows ProposalRunLinks; a zero field matches any.
type ReapAck ¶
type ReapAck struct {
ID int
RunID int
StepID int
Class string
Instance string
// ReapedSeq is the `seq` of the `lease-reaped` event this ack is OF. The
// relay acknowledges "the `reaped` event", so the event's seq is the
// ack's identity — which makes the ack idempotent for free and a forged
// ack impossible to construct without naming a real reap.
ReapedSeq int64
AckedAtMS *int64
AckedBy string
CreatedAtMS int64
}
ReapAck is one unacknowledged-or-acknowledged reap.
func UnacknowledgedReapsTx ¶
UnacknowledgedReapsTx lists a run's outstanding reaps, oldest first.
It is the query behind the headroom predicate AND behind the human-readable reason `next` prints, so the two cannot name different rows: a headroom denial with nothing running is baffling unless the same rows that caused it are the ones reported.
The `instance` join is for the MESSAGE. The predicate needs only the class, but a refusal that named `STEP-7` rather than `implement@0` would make an operator look the step up to understand a sentence about their own run.
type ResultTrailRow ¶
type ResultTrailRow struct {
Instance string `json:"step"`
StepID string `json:"step_id"`
Issue string `json:"issue"`
Name string `json:"name"`
Ordinal int `json:"ordinal"`
Verdict string `json:"verdict"`
Reason string `json:"reason,omitempty"`
Output string `json:"output,omitempty"`
}
ResultTrailRow is one step's one result — the per-step trail the gate and action rollups carry beside their counts.
StepID and Issue joined on (DKT-77): instance names collide across issues in one run — two issues running the same workflow both have an `implement@0` — so a trail keyed on instance alone was unattributable. Output rides ONLY on rows that did not pass, as a bounded tail: a failing gate's diagnosis used to require re-running it out-of-band, which is at its most expensive exactly where a false failure blocks a security path.
func ActionTrail ¶
func ActionTrail(db *sql.DB, runID int) ([]ResultTrailRow, error)
type RunBudgetFacts ¶
RunBudgetFacts are the stored budget columns a read verb renders: the cached floor and the breach reason, if any.
func RunBudgetFactsFor ¶
func RunBudgetFactsFor(db *sql.DB, runID int) (RunBudgetFacts, error)
RunBudgetFactsFor reads the v10 columns for one run.
func RunBudgetFactsTx ¶
func RunBudgetFactsTx(tx *sql.Tx, runID int) (RunBudgetFacts, error)
RunBudgetFactsTx is RunBudgetFactsFor inside a caller's transaction, for the one writer that must read the breach record while deciding whether a cap change resolved it (DKT-80). The pool is capped at one connection, so a pool read from inside a transaction deadlocks rather than merely racing.
type RunContext ¶
type RunContext struct {
ExecRoot string
Branch string
CommitSHA string
Hostname string
// UsageBudget is the cap over MEASURED usage (DKT-238), resolved at
// `run start` from `--usage-budget` or `budget.usage.default` and pinned
// on the row for the same reason `budget` is: a config change must not
// re-cap a live run.
UsageBudget float64
}
RunContext is the execution context stamped on a run at creation: which checkout, on which branch and commit, on which machine. Empty fields record as empty — a run started outside a checkout has no branch, and inventing one would make the record an opinion.
type RunIssue ¶
type RunIssue struct {
RunID int
IssueID int
WorkflowID *int
BodySnapshot string
BodySHA256 string
IssueSnapshot string
ExpandedAtMS *int64
// LoopCount is the ISSUE's loop counter. It lives here rather than on
// `steps` because the counter belongs to the issue: one issue loops, and
// `max_fix_loops` bounds that one number.
LoopCount int
}
RunIssue is one `run_issues` row: an issue's membership in a run, its binding, and the activation-time snapshots the context bundle reads.
func GetRunIssueTx ¶
GetRunIssueTx reads one `run_issues` row — the issue's binding, snapshots, and loop counter — inside a transaction.
The loop routing needs the counter under the SAME lock that increments it, so this reads inside the caller's transaction rather than through a *sql.DB helper. (It also must: internal/db caps the pool at one connection, so a pool read from inside a transaction deadlocks — the failure TestNoPoolReadsInsideTransactions exists to prevent.)
func ListRunIssues ¶
ListRunIssues returns a run's issues in ascending issue order, so every caller walks them the same way and two activations of the same inputs agree.
func ListRunIssuesTx ¶
ListRunIssuesTx is ListRunIssues inside a transaction.
type RunListOptions ¶
type RunListOptions struct {
// ProjectID scopes the list to one project (v12); 0 = every project.
ProjectID int
// ActiveOnly restricts the list to runs that are not terminal — the
// `run status` default, lifted by `--all`. `planning` counts as active: a
// run that exists but has not been activated is still live work an
// operator is mid-way through.
ActiveOnly bool
Limit int
}
RunListOptions filters `run status` without an id.
type RunNote ¶
RunNote is one standing statement recorded against a run by whoever drives it (DKT-1079): a fact the run's workers need that no other packet source can carry — a gate known to fail on clean HEAD, the issue tracking it, and the disposition the operator already gave. Every packet the run renders after the note lands carries it, for every step of every issue in the run.
The note is RUN-SCOPED BY CONSTRUCTION — the run_id foreign key is the whole scope rule, exactly gate_override_grants' and stale_target_waivers' shape. It is also APPEND-ONLY: no edit, no delete. A packet is the record of what a worker was told, and a note that could be rewritten after the fact would make two renders of one step disagree about that with nothing in the ledger explaining the difference. A ruling that changes is recorded as a second note, which renders after the first.
Authority is the authority the writer asserted when recording the note, stored verbatim. A note recorded before the column existed reads "": its authority is unknown, and nothing infers one for it.
func ListRunNotes ¶
ListRunNotes is ListRunNotesTx on the pooled connection, for the read verb.
func ListRunNotesTx ¶
ListRunNotesTx returns every note recorded for one run, in insertion order — the order they render in, since a later note may qualify an earlier one.
It takes a transaction because its main consumer is context assembly, which reads every source inside the claim's own transaction so a claimant sees one consistent run state: one atomic mediation.
type SchemaListOptions ¶
type SchemaListOptions struct {
// ProjectID scopes the list to one project's visible schemas — its own
// plus builtins (v12); 0 = every project's.
ProjectID int
Name string
Limit int
// ExcludeDeprecated drops retired versions (`deprecated_at_ms` set) from
// both the rows and the pre-limit total. Zero value is false so every
// existing caller — the registry audit, which asks what the registry
// HOLDS — keeps seeing every version; only `schema list`'s default opts
// this in.
ExcludeDeprecated bool
}
SchemaListOptions filters `schema list`.
type SilentVoteSeatRow ¶
SilentVoteSeatRow is one cast VoteUsageCoverageFor counts as silent: which seat, on which proposal, reported no spend at all (DKT-733).
The coverage COUNT told an operator that 12 of 57 seats went silent and nothing anywhere said WHICH twelve — so the backfill verb that exists precisely to close the gap (`vote backfill-usage`, DKT-115) could not be aimed. This row is the identity behind the count.
The seating path is deliberately NOT here: this package selects by an opaque scope and prefix and cannot know which key family or run-naming rule minted a proposal. The engine, which owns those spellings, labels each row (engine.SilentVoteSeat).
func SilentVoteSeatsFor ¶
func SilentVoteSeatsFor(db *sql.DB, scope, prefix string, extraIDs ...int) ([]SilentVoteSeatRow, error)
SilentVoteSeatsFor enumerates the casts VoteUsageCoverageFor counts as silent, through the SAME membership builder — so the list and the count it explains cannot disagree about which casts belong to the run. Ordered by (proposal, voter): a total key, so the report stays deterministic.
type StaleTargetWaiver ¶
type StaleTargetWaiver struct {
ID int
RunID int
StepInstance string
TargetSHA string
Note string
CreatedAtMS int64
}
StaleTargetWaiver is one operator ruling that a specific stale-target warning — one (step instance, target sha) pair — has been adjudicated for the remainder of ONE run (DKT-742): dispatch open/verify stop re-firing the identical warning, and the standing precedent is engine-visible instead of session-memory-only.
The waiver is RUN-SCOPED BY CONSTRUCTION — the run_id foreign key is the whole scope rule, exactly gate_override_grants' shape (DKT-546). A new run re-warns, and there is deliberately no way to widen a waiver past its run.
THE SIGNATURE IS THE PAIR AND NOTHING ELSE. The warning's rendered reason names the shared HEAD, which moves at every integration — RUN-52's four firings of one adjudicated warning each carried a different HEAD — so the reason is not part of the match. A DIFFERENT target sha on the same row, or the same sha on a row this waiver does not name, is a different question and still warns.
TargetSHA may be a PREFIX of the recorded sha (>= 7 hex characters, enforced at the verb): the warning an operator copies it from renders the sha at 12 characters, and demanding the full 40 would make the verb unusable from its own advisory. Matching is case-insensitive prefix (engine: waiverCovers).
func StaleTargetWaiversForRun ¶
func StaleTargetWaiversForRun(conn *sql.DB, runID int) ([]StaleTargetWaiver, error)
StaleTargetWaiversForRun returns every waiver recorded for one run, in insertion order.
It reads on the pooled connection because its one consumer — staleTargets — runs OUTSIDE every transaction by design (no subprocess inside one), exactly as batchOverrideCover reads grants.
type Step ¶
type Step struct {
ID int
RunID int
IssueID int
WorkflowID int
StepName string
Ordinal int
SiblingIndex *int
Instance string
Kind string
Executor string
Class string
Status string
Attempt int
// AttemptBase is the attempt count the retry budget counts FROM (v16,
// DKT-86/DKT-90). `attempt` is monotonic for the step's whole life — it is
// the usage ledger's key half and the "claims made against this step,
// ever" — so `step resolve --as retry` refreshes the budget by moving this
// base to the current attempt instead of zeroing the counter. Exhaustion
// compares Attempt-AttemptBase against MaxAttempts.
AttemptBase int
// FailedAttempts and ReapedClaims are the OUTCOME breakdown of the claims
// Attempt counts (v23, DKT-490): how many ended in an explicit `step fail`,
// and how many were reaped without one (lease expiry, `max_step_duration`,
// `step reap`). Attempt alone cannot answer that — it spends one count per
// claim whatever the ending — and a consumer that read it as "attempts
// that failed" escalated on claims that never failed at all. A claim that
// RECORDED counts in neither, and `step resolve --as retry` touches
// neither. FailedAttempts+ReapedClaims never exceeds Attempt; the
// remainder is live claims, recorded completions, and pre-v23 history
// (the migration back-fills nothing — see migrateV22ToV23).
FailedAttempts int
ReapedClaims int
// LastClaimEnd is the END REASON of the most recent claim to leave this
// step (v27, DKT-1279): ClaimEndFailed or ClaimEndReaped, overwritten by
// whichever of MarkStepAttemptFailedTx / MarkStepClaimReapedTx ran last.
// FailedAttempts and ReapedClaims answer "how many of each has this step
// EVER had"; a router deciding how to treat THIS re-offer needs the LAST
// one, not a tally — the two disagree the moment a step has both a
// failure and a reap in its history, in either order. "" means no claim
// has ended in either way (never claimed, every claim recorded, or
// pre-v27 history — see migrateV26ToV27).
LastClaimEnd string
MaxAttempts *int
ExpectedCost float64
Owner string
TokenHash string
ExpiresMS int64
StartedMS *int64
ActivityMS *int64
SagaStage string
GateTrail string
Routing string
// ParkReason is the ENGINE's text for why this step could not be decided,
// written only where the step routes `waiting-human` and never rewritten by
// the resolution that answers it. Routing holds the LATEST decision; this
// holds the question.
ParkReason string
// ParkClass is the CLOSED-ENUM form of the same question ParkReason answers
// in prose (DKT-1900), written by the same statement and under the same
// condition. A conductor routing a parked row reads this; ParkReason is what
// it shows the person it escalates to. "" on any row that is not parked, and
// on parked rows that predate the column.
ParkClass ParkClass
// LoopRoundsRun, LoopTriggerStep, and LoopLatestVerdict are the loop
// history of a fix loop that exhausted its `max_fix_loops` budget (v35):
// the rounds that actually ran against the cap, the instance whose verdict
// opened the loop, and the verdict the last round ended on. ParkClass tells
// a router THAT the loop bound stopped this step; these tell it what the
// loop did before it stopped, which was otherwise reconstructible only from
// the prunable event log. LoopCap is the bound those rounds were measured
// against (v44), 0 when the history was written without one. Zero and "" on
// every row that never exhausted a loop, and on rows that predate the columns.
LoopRoundsRun int
LoopTriggerStep string
LoopLatestVerdict string
LoopCap int
Metadata string
ContextBytes int
// Materialized reports a step the ENGINE minted rather than one the pinned
// definition declares — the `<step>-held` human step a tripped `hold_spread`
// creates. Its spec is synthesized from the
// pinned bytes, so nothing unpinned enters a run; the column exists so a
// reader can tell a declared question from a computed one.
Materialized bool
// UsageRecorded is the v10 column group 1 writes on every `--usage` and
// group 2's discrepancy probe reads. It is the LEDGER'S OWN
// answer to "did this step report", written in the recording transaction, so
// the probe needs no join per step.
UsageRecorded bool
// WorkRoot is the checkout the step's work happened in — recorded at
// complete/record via --worktree (v12), read by the diff stage so a
// resumed saga diffs the tree the work touched, not the resumer's cwd.
WorkRoot string
CreatedAtMS int64
UpdatedAtMS int64
RowVersion int
// RecordedAtMS is the instant the step last moved into a terminal status
// (v39), written only by that transition. UpdatedAtMS moves on every row
// write after it, so it cannot say when the step recorded. Zero means the
// step has not recorded a terminal result.
RecordedAtMS int64
// Authority is the authority the step's latest ruling was made under (v40):
// operator, standing-grant, or conductor, the same word its ruling event
// carries. AuthorityRef names the standing authorization and is set only
// with standing-grant. Both are "" on a step no ruling has resolved, and on
// rows resolved before the columns existed.
Authority string
AuthorityRef string
// ClaimPhase is ClaimPhasePreGates from the commit of a pre-gated claim's
// first transaction until its context transaction commits (v41), and ""
// otherwise. It describes the claim that last wrote it, so it is only
// meaningful while that claim's lease is live.
ClaimPhase string
}
Step is one `steps` row, read whole. It is a superset of StepRow — which activation writes — because phase 3 reads the lease, saga, and routing columns activation never fills.
func GetStepTx ¶
GetStepTx is GetStep inside a transaction — every saga stage's reader, so the stage's guard and its mutation see one consistent row.
func ListActiveRunSteps ¶
ListActiveRunSteps reads the steps of every non-terminal run — `guard stop`'s reader and the scope-conflict check's, since a claimed step in ANOTHER active run excludes just as surely as one in this run.
func ListIssueStepsTx ¶
ListIssueStepsTx reads every step of ONE issue in a run, inside a transaction — the `after_fired` cascade's reader (DKT-1085), which must see the rows the open routing transaction just terminalized and needs no other issue's steps to answer its question.
func ListRunSteps ¶
ListRunSteps reads every step of a run, ordered by (issue, id) — creation order within an issue, which is declaration order, which is what the topology goldens compare against.
func ListRunStepsTx ¶
ListRunStepsTx is ListRunSteps inside a transaction — the reader for the transaction-side questions asked about a whole run, which must see one consistent snapshot of it. The readiness predicate asks whether predecessors are done and whether scopes overlap, which are questions about the same set of rows at the same instant; repinQuiescenceGuard asks whether any step could straddle a moving agreement, which is the same instant's question about the same rows.
func (*Step) InSaga ¶
InSaga reports whether the step is mid-saga — recorded but not yet routed. Such a step needs NO lease to advance: the token retired at stage 1, so any later engine invocation may resume it.
type StepRow ¶
type StepRow struct {
ID int
RunID int
IssueID int
WorkflowID int
StepName string
Ordinal int
SiblingIndex *int
Instance string
Kind string
Executor string
Class string
Status string
MaxAttempts *int
ExpectedCost float64
Metadata string
ContextBytes int
// Materialized marks a step the engine minted rather than one expansion
// read out of the pinned definition. Ordinary
// expansion never sets it, so every row activation writes reads 0.
Materialized bool
CreatedAtMS int64
}
StepRow is one `steps` row as activation writes it. Phase 3 adds the lease, saga, and routing readers; expansion fills only the identity, the shape, and the initial status.
type StepUsageRow ¶
type StepUsageRow struct {
Step string `json:"step"`
Instance string `json:"instance"`
Attempt int `json:"attempt"`
Unit string `json:"unit"`
Quantity float64 `json:"quantity"`
Source string `json:"source"`
}
StepUsageRow is one row of the per-step ledger view: which step, which attempt, which unit, how much, and who measured it.
func UsageByStep ¶
func UsageByStep(db *sql.DB, runID int) ([]StepUsageRow, error)
UsageByStep lists the run's ledger row by row, joined to each step's instance, ordered by (step, attempt, unit).
It exists because nothing exposed per-step usage. `run report` rolled the ledger up per UNIT only, so the one question a back-fill's refusal sends you to answer — WHICH steps already have usage recorded — could not be answered from any read verb, and conductors hand-filtered batches by trial and error (DKT-241). The rollup is still the headline; this is the detail behind it.
ORDERED BY A TOTAL KEY, like UsageByUnit and for the same reason: two reports of the same rows must be byte-identical.
type UnitTotal ¶
type UnitTotal struct {
Unit string `json:"unit"`
Quantity float64 `json:"quantity"`
Rows int `json:"rows"`
}
UnitTotal is one unit's rollup for the report's reported-per-unit line.
func UsageByUnit ¶
UsageByUnit rolls the run's ledger up per unit, ordered by unit name.
ORDERED BY A TOTAL KEY, never by map iteration: the report is deterministic given the same rows, for the same golden-stability reason `referencedSchemas` is ordered.
func VoteUsageRollup ¶
VoteUsageRollup sums the vote_usage ledger per unit over the run's vote-step proposals (DKT-95) — UsageByUnit's question, asked of the seats. The proposals are selected by the same caller-supplied idempotency scope and prefix VoteMetadataRollup reads by, and units stay opaque: summed and counted, never interpreted.
extraIDs widens the selection to proposals the caller attributed to the run some OTHER way than the vote-step key family (DKT-584): reap-ack ballots and conversational-gate proposals that name the run in their text. Membership is a set test per vote row, so an id also matched by the prefix counts once.
type UsageRow ¶
type UsageRow struct {
RunID int
StepID int
Attempt int
Unit string
Quantity float64
// Source records where the usage came from. Core writes
// UsageSourceReported and nothing else at this stage; the column exists so a
// harness back-filling from its own journal can record its own source later
// without a migration.
Source string
}
UsageRow is one `usage_ledger` row: what a step reported, in one unit, on one attempt.
func ParseUsage ¶
ParseUsage parses `--usage '{"unit": n, …}'` as a JSON object of string -> number, capped, with every refusal NAMING what it refused. It is the ONE implementation of the usage-report contract: the step ledger's writer (`step complete --usage`, via the engine) and the vote ledger's (`vote cast --usage`, DKT-95) both parse through it, so the two flags cannot drift on what a valid report is.
It returns the units in SORTED ORDER so a call's ledger rows land in a deterministic order — the same total-key discipline the report's rendering uses, applied at the writer so two identical calls produce identical row ids.
Refusals are plain errors naming only the offending key or value, never a flag: each caller prefixes its own surface's name and shape (the engine as a VALIDATION_ERROR on `--usage`, the CLI as a `--usage` flag refusal), so this package never authors wording for a surface it cannot see.
type VerdictCount ¶
type VerdictCount struct {
Name string `json:"name"`
Pass int `json:"pass"`
Fail int `json:"fail"`
Unmatched int `json:"unmatched"`
// Skipped counts rows that MEASURED NOTHING (DKT-254). Before this column
// a skipped row landed in none of the three above and vanished from the
// report entirely — an absence that reads as green, which is the exact
// inversion of what the verdict means.
//
// It partitions with pass/fail/unmatched: every row lands in exactly one
// of the four. (`Stub` below does not — see its own note.)
Skipped int `json:"skipped"`
// Stub counts rows whose authorizing trust entry declared itself a
// PLACEHOLDER (DKT-265). It is a count of ROWS, not of passes, so it
// overlaps the three columns above rather than partitioning with them —
// `pass 3, stub 3` means every one of those passes was hollow.
//
// It is zero for actions, which have no trust-entry stub declaration. That
// is an honest zero and not a missing feature: an action's `builtin` column
// already says whether core computed it.
Stub int `json:"stub"`
// Pre counts rows recorded by the PRE-GATE phase (DKT-862). Like Stub it
// counts ROWS and overlaps the four verdict columns rather than
// partitioning with them, because it describes WHEN the row was produced,
// not what it decided.
//
// It matters because a pre-gate NEVER ROUTES: it runs at claim, with
// its results carried into the step's context bundle, and is excluded
// from the saga's verdict. Without this count `ac-commands: pass 0, fail 1`
// read identically whether the failure blocked the step or was an advisory
// input to it — RUN-61 had three such rows and a conductor nearly reported
// a fix round as burned on one of them.
//
// It reads the SAME `pre` column `step show`'s `[pre]` marker reads, so the
// two surfaces cannot disagree about which gates were advisory. It is zero
// for actions, which have no pre phase.
Pre int `json:"pre"`
}
VerdictCount is one subject's pass/fail/unmatched tally.
func ActionRollup ¶
func ActionRollup(db *sql.DB, runID int) ([]VerdictCount, error)
ActionRollup is the same rollup over `action_results`.
The same shape, deliberately: gates and actions are the two execution seams and their results carry the same verdict vocabulary, so a reader who understands one section understands the other. Reusing the query rather than writing a second is what keeps the two from drifting into different definitions of "pass".
`skipped` is counted for actions too, and is always zero there today: no action path produces that verdict. That is an honest zero and the right shape — the alternative, omitting the column for one of the two seams, is how the two definitions of "what outcomes exist" start to drift.
func GateRollup ¶
func GateRollup(db *sql.DB, runID int) ([]VerdictCount, error)
GateRollup counts a run's gate results per gate name.
type VoteUsageCoverage ¶
type VoteUsageCoverage struct {
// Casts is every vote cast on this run's vote-step proposals.
Casts int `json:"casts"`
// Reported is the subset that recorded at least one vote_usage row.
Reported int `json:"reported"`
}
VoteUsageCoverage is how many of a run's seat-casts reported their spend, and how many did not (DKT-257).
func VoteUsageCoverageFor ¶
func VoteUsageCoverageFor(db *sql.DB, scope, prefix string, extraIDs ...int) (VoteUsageCoverage, error)
VoteUsageCoverageFor counts a run's seat-casts and how many reported spend.
It exists because a MISSING report and a FREE panel were the same number (DKT-257). The vote_usage ledger has existed since v14 and held zero rows for an entire store epoch while 21+ seat-votes did real verification work: the table, the writer, and the rollup were all wired, and nothing supplied a number, so `vote_usage: 0` read as "this panel cost nothing".
In-wave panels ARE counted, because their seats run as steps — RUN-22 STEP-379/381 journaled ~39.5k output tokens to step_usage. Conductor-side panels of identical shape recorded 287/0. Roughly 40k tokens per panel that the run's budget never saw, and the only difference was WHERE the ballot executed, not how much work it did.
Core cannot observe a conductor-side seat's spend, so it cannot fix the number. What it can do is stop the silence from looking like a zero, which is this: a run whose seats all reported reads `12/12`, and one whose seats reported nothing reads `0/12` instead of an absent section. extraIDs widens the count to explicitly attributed proposals exactly as VoteUsageRollup's does (DKT-584), through the same membership builder — so a cast counted in the rollup is always counted in its coverage line.
func (VoteUsageCoverage) Silent ¶
func (c VoteUsageCoverage) Silent() int
Silent is the gap: seats that deliberated and reported nothing.
type WorkflowListOptions ¶
type WorkflowListOptions struct {
// ProjectID scopes the list to one project (v12); 0 = every project.
ProjectID int
Name string
Limit int
// ExcludeDeprecated drops retired versions (`deprecated_at_ms` set) from
// both the rows and the pre-limit total. Zero value is false so every
// existing caller — binding readers that need the full lineage to judge
// staleness — keeps seeing every version; only `workflow list`'s default
// opts this in.
ExcludeDeprecated bool
}
WorkflowListOptions filters `workflow list`.
type WorkflowVersion ¶
type WorkflowVersion struct {
Name string
Version int
// Binds is false for a version RETIRED from binding (`deprecated_at_ms` set).
//
// The distinction is the same one bindableDefinitions makes: retirement is a
// binding-time filter, so the version a fresh run would pin is the highest
// one that still binds, and a staleness count computed over retired rows
// would tell an operator to chase a version nothing can bind.
Binds bool
}
WorkflowVersion is one registered version's IDENTITY, without its bytes (DKT-594).
It exists because the staleness question — "how many versions has this name advanced since the run pinned it" — is answered by the version column alone, and the rows that answer it carry a `body` and a `parsed` each. A corpus with 41 commits in four days has names registered a dozen deep; loading every one of their bodies to subtract two integers would make a read verb's cost scale with the size of the definitions it is not reading.
func WorkflowVersionsFor ¶
WorkflowVersionsFor returns every registered version of the NAMED workflows within one project, ordered by (name, version) so a caller's reduction is deterministic.
An empty `names` returns no rows and makes no query: the caller has nothing to ask about, and an unfiltered scan of the whole registry is never what "these names" means.
Source Files
¶
- action_results.go
- activity.go
- cas.go
- comments.go
- db.go
- dispatch.go
- doc_comments.go
- doc_links.go
- docs.go
- engineconfig.go
- files.go
- gate_override_grants.go
- gate_results.go
- idempotency.go
- issues.go
- labels.go
- leases.go
- projects.go
- proposals.go
- relations.go
- rollups.go
- rows.go
- run_notes.go
- runs.go
- schema.go
- schemas.go
- stale_target_waivers.go
- steps.go
- usage.go
- workflows.go