taskmgr

package
v0.44.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package taskmgr is fak's process-local task manager concept.

It tracks the work a running process says it is doing as task and step records, then projects a point-in-time snapshot with wall time, process resource samples, per-step/concept runtime, progress, and ETA when enough progress data exists.

Tier: foundation (1) - see internal/architest. This package is stdlib-only and deliberately off the request path: it is a reference fold a front door can embed, not a hosted scheduler, durable store, or fleet coordinator.

Index

Constants

View Source
const (
	ConceptObserve    = "observe"
	ConceptAdjudicate = "adjudicate"
	ConceptTool       = "tool"
	ConceptModel      = "model"
	ConceptCache      = "cache"
	ConceptIO         = "io"
	ConceptVerify     = "verify"
	ConceptWait       = "wait"
	ConceptOther      = "other"
)

The default concept vocabulary. A step's Concept is free-form, but these are the canonical buckets so process snapshots compare cleanly across serve, guard, benches, and demos. Each maps to an existing fak concept:

observe    - admission / session decision, status sampling
adjudicate - guard adjudication of a tool call or request
tool       - tool-call execution and result admission
model      - an upstream model / provider request
cache      - context-cache reuse and prefix accounting
io         - disk, network, or other blocking I/O
verify     - verification of a claim or effect (a trust rung)
wait       - intentional waiting on an external signal
other      - anything that does not fit a named bucket

The vocabulary is runtime accounting only. It carries no security trust and no completion truth: a step tagged verify has not been verified, it merely spent time in verification. Callers may still pass a custom concept for local experiments; NormalizeConcept lets such values pass through unchanged.

View Source
const (
	// SchemaHandoff is the input contract for a task completion handoff.
	SchemaHandoff = "fak.task-handoff.v1"
	// SchemaHandoffReview is the output contract for the pure handoff gate.
	SchemaHandoffReview = "fak.task-handoff-review.v1"
)
View Source
const DefaultLivenessTimeout = 30 * time.Second
View Source
const DefaultRepeatStallThreshold = 3

DefaultRepeatStallThreshold is the repeat count at which a within-turn tool-input hash flips a task to RepeatStalled. The issue's "> 2x in one turn" maps to: the FIRST call is work, the SECOND is a retry, the THIRD identical call inside the same turn is the signal that the loop is wedged. So the third occurrence (count == 3) trips it.

View Source
const OutputRefKind = "output"

OutputRefKind is the EvidenceRef.Kind a ShapeWitness reads back: an EvidenceRef whose Kind is "output" carries, in its Ref field, the OUTPUT TEXT of a beat to be graded for shape. It is the semantic-failure analogue of PathWitness's "path" refs — but where a "path" ref POINTS at proof on disk, an "output" ref carries the proof inline (the bytes the process actually emitted), because the failure mode being witnessed is in the bytes themselves, not in some external artifact.

View Source
const (
	// PathRefKind is the EvidenceRef.Kind PathWitness reads back.
	PathRefKind = "path"
)
View Source
const SchemaQueue = "fak-task-queue/1"
View Source
const SchemaSnapshot = "fak.task-manager-snapshot.v1"
View Source
const TestRefKind = "test"

TestRefKind is the EvidenceRef.Kind for a targeted command that witnessed the task, usually a focused go test invocation.

Variables

This section is empty.

Functions

func DefaultConcepts

func DefaultConcepts() []string

DefaultConcepts returns a copy of the canonical concept vocabulary in stable order. The returned slice is the caller's to mutate; it does not alias package state.

func DefaultOriginWitnesses added in v0.37.0

func DefaultOriginWitnesses() map[string]Witness

DefaultOriginWitnesses returns the built-in kind registry. The returned map is a fresh copy so callers can add or replace witnesses without mutating package state.

func HandoffIssueBody added in v0.35.0

func HandoffIssueBody(h Handoff, step HandoffNextStep) string

HandoffIssueBody renders the dedupe marker plus enough state for a future agent to understand where the item stands before picking it up.

func HandoffMarkerKey added in v0.35.0

func HandoffMarkerKey(body string) string

HandoffMarkerKey extracts the stable marker key from an issue body.

func IsDefaultConcept

func IsDefaultConcept(concept string) bool

IsDefaultConcept reports whether concept is a member of the canonical vocabulary. The check is exact: pass a value already lowercased and trimmed, or run it through NormalizeConcept first.

func NormalizeConcept

func NormalizeConcept(concept string) string

NormalizeConcept maps a caller-supplied concept onto the value that should be recorded so snapshots aggregate consistently:

  • whitespace is trimmed;
  • an empty concept becomes "other" rather than a blank bucket;
  • a value that matches the default vocabulary case-insensitively is folded to its canonical lowercase form (so "Verify" and "verify" aggregate together);
  • any other non-empty value passes through trimmed, preserving its case, so custom concepts remain usable for local experiments.

func RedactCommandOutput added in v0.44.0

func RedactCommandOutput(s string) string

RedactCommandOutput removes common credential forms before evidence leaves the local command boundary. It intentionally preserves the field name so the resulting artifact remains diagnostically useful.

func RenderQueue added in v0.44.0

func RenderQueue(w io.Writer, q Queue, drilldown bool)

func ValidateSnapshot

func ValidateSnapshot(s Snapshot) error

ValidateSnapshot reports whether s satisfies the task-manager snapshot contract: the schema tag, unique non-empty task/step IDs, the closed state vocabulary, non-negative runtimes and resource counters, consistent progress fields, and the ETA presence/absence rule (an ETA may appear only on a running record with measurable progress, and the two ETA fields are present or absent together).

ValidateSnapshot is read-only: it takes the snapshot by value and never repairs or mutates it. A nil return means valid; any defect is reported as the first error encountered, naming the offending task or step.

Progress overrun (done > total) is allowed on purpose: it is an honest over-budget signal and yields a percent above 100. Validation therefore does not reject overrun; it only requires that the reported percent agree with the done/total it was derived from.

Types

type ArtifactCommand added in v0.44.0

type ArtifactCommand struct {
	Argv         []string
	Dir          string
	ArtifactPath string
	MaxBytes     int
}

ArtifactCommand describes a command whose bounded, redacted output should be retained as issue evidence. ArtifactPath is required so the caller chooses a durable, linkable location rather than silently writing session scratch.

type ArtifactResult added in v0.44.0

type ArtifactResult struct {
	Evidence  EvidenceRef
	ExitCode  int
	Truncated bool
}

ArtifactResult records the command outcome and the independently readable path EvidenceRef. A non-zero command exit is evidence, not a helper error; Err is reserved for setup, execution, redaction, or artifact-write failures.

func CaptureCommandArtifact added in v0.44.0

func CaptureCommandArtifact(ctx context.Context, spec ArtifactCommand) (ArtifactResult, error)

CaptureCommandArtifact runs a command, retains only bounded output, redacts common credential forms, and atomically stores the result with owner-only permissions. It returns a path EvidenceRef suitable for a dogfood issue.

type Attempt added in v0.44.0

type Attempt struct {
	Holder      string `json:"holder"`
	PID         int    `json:"pid,omitempty"`
	Account     string `json:"account,omitempty"`
	Token       string `json:"token,omitempty"`
	HeartbeatAt string `json:"heartbeat_at,omitempty"`
	AcquiredAt  string `json:"acquired_at,omitempty"`
	Lane        string `json:"lane,omitempty"`
}

type Claim

type Claim struct {
	TaskID string
	StepID string // empty for a task-level claim
	State  State
	Refs   []EvidenceRef
}

Claim is what a Witness is asked to corroborate: the identity of the task/step, the state the process claims, and the artifacts it points at as proof.

type ConceptUsage

type ConceptUsage struct {
	Concept        string  `json:"concept"`
	Steps          int     `json:"steps"`
	RunningSteps   int     `json:"running_steps,omitempty"`
	RuntimeSeconds float64 `json:"runtime_s"`
	CPUSeconds     float64 `json:"cpu_s,omitempty"`
}

type EvidenceRef

type EvidenceRef struct {
	Kind string `json:"kind"`          // e.g. "path", "commit", "plan-phase"
	Ref  string `json:"ref,omitempty"` // e.g. a path, a sha, "PLAN/PHASE"
	Note string `json:"note,omitempty"`
}

EvidenceRef points a witness at an artifact it can read back: a file path, a git ref, a plan/phase. It is the claim's pointer to proof, not the proof itself.

func DeriveHandoffEvidenceRefs added in v0.37.0

func DeriveHandoffEvidenceRefs(in HandoffEvidenceInputs) []EvidenceRef

DeriveHandoffEvidenceRefs turns raw producer signals into bounded, typed refs.

type Handoff added in v0.35.0

type Handoff struct {
	Schema       string      `json:"schema"`
	Task         HandoffTask `json:"task"`
	CurrentState string      `json:"current_state"`
	// AchievedMaturity names the completion standard the finished task actually
	// reached (production, integrated, staging, development, demo, prototype,
	// experiment, research). A handoff that claims "done" without naming its
	// achieved maturity can read as production-ready when only a demo exists;
	// the strict project-work gate refuses that ambiguity (#4640).
	AchievedMaturity    string            `json:"achieved_maturity,omitempty"`
	Summary             string            `json:"summary,omitempty"`
	CompletedBy         string            `json:"completed_by,omitempty"`
	Labels              map[string]string `json:"labels,omitempty"`
	NextSteps           []HandoffNextStep `json:"next_steps,omitempty"`
	NoNextStepReason    string            `json:"no_next_step_reason,omitempty"`
	CompletionEvidence  []EvidenceRef     `json:"completion_evidence,omitempty"`
	CompletionTimestamp int64             `json:"completion_unix_nano,omitempty"`
}

Handoff is the machine-readable record a finishing agent hands to the next loop. It turns "remember to follow up" into typed state: the task's claimed completion, the independent witness beside it, where the item currently stands, and either one or two concrete next steps or an explicit reason that no follow-up is reasonable.

func DraftHandoffFromTask added in v0.37.0

func DraftHandoffFromTask(task TaskSnapshot, opt HandoffDraftOptions) Handoff

DraftHandoffFromTask creates the editable task-handoff record from the current task snapshot and producer-side evidence signals. It is intentionally usable for a running task: ReviewHandoff may later refuse that draft as incomplete, but the changed-path/test/artifact refs are already present at the origin.

type HandoffDraftOptions added in v0.37.0

type HandoffDraftOptions struct {
	CurrentState        string
	Summary             string
	CompletedBy         string
	Labels              map[string]string
	CompletionEvidence  []EvidenceRef
	CompletionTimestamp int64
	Evidence            HandoffEvidenceInputs
}

HandoffDraftOptions controls the handoff draft generated from a live task snapshot.

type HandoffEvidenceInputs added in v0.37.0

type HandoffEvidenceInputs struct {
	ChangedPaths       []string `json:"changed_paths,omitempty"`
	TestCommands       []string `json:"test_commands,omitempty"`
	GeneratedArtifacts []string `json:"generated_artifacts,omitempty"`
}

HandoffEvidenceInputs are raw producer-side signals collected while a task is running. DraftHandoffFromTask folds them into typed EvidenceRefs before an operator edits or syncs the handoff.

type HandoffIssue added in v0.35.0

type HandoffIssue struct {
	Number int    `json:"number"`
	Title  string `json:"title"`
	Body   string `json:"body"`
	State  string `json:"state"`
	URL    string `json:"url,omitempty"`
}

HandoffIssue is the subset of a GitHub issue needed to dedupe handoff-created follow-ups by marker.

type HandoffIssuePlanRow added in v0.35.0

type HandoffIssuePlanRow struct {
	Action       string   `json:"action"`
	Key          string   `json:"key"`
	Number       *int     `json:"number,omitempty"`
	State        string   `json:"state,omitempty"`
	Title        string   `json:"title"`
	Body         string   `json:"-"`
	Labels       []string `json:"labels,omitempty"`
	Reason       string   `json:"reason"`
	Priority     string   `json:"priority,omitempty"`
	EvidenceRefs []string `json:"evidence_refs,omitempty"`
}

HandoffIssuePlanRow is a create/update decision for one next step.

func BuildHandoffIssuePlan added in v0.35.0

func BuildHandoffIssuePlan(h Handoff, existing []HandoffIssue) []HandoffIssuePlanRow

BuildHandoffIssuePlan decides create vs update for every next step.

type HandoffNextStep added in v0.35.0

type HandoffNextStep struct {
	Key                     string        `json:"key"`
	Title                   string        `json:"title"`
	Body                    string        `json:"body"`
	Reason                  string        `json:"reason"`
	Generation              string        `json:"generation,omitempty"`
	PromotionEvidence       []string      `json:"promotion_evidence,omitempty"`
	DemotionEvidence        []string      `json:"demotion_evidence,omitempty"`
	InvalidatingAssumptions []string      `json:"invalidating_assumptions,omitempty"`
	GenerationNonGoals      []string      `json:"generation_non_goals,omitempty"`
	WorkingSpine            string        `json:"working_spine,omitempty"`
	PriorityContext         string        `json:"priority_context,omitempty"`
	WorkUnit                string        `json:"work_unit,omitempty"`
	ExpectedSteps           int           `json:"expected_steps,omitempty"`
	Assumptions             []string      `json:"assumptions,omitempty"`
	ConfusionRisks          []string      `json:"confusion_risks,omitempty"`
	Coordination            []string      `json:"coordination,omitempty"`
	Trigger                 string        `json:"trigger,omitempty"`
	BatchPolicy             string        `json:"batch_policy,omitempty"`
	InScope                 string        `json:"in_scope,omitempty"`
	OutOfScope              string        `json:"out_of_scope,omitempty"`
	DoneCondition           string        `json:"done_condition,omitempty"`
	Witness                 string        `json:"witness,omitempty"`
	AcceptanceGate          string        `json:"acceptance_gate,omitempty"`
	Lane                    string        `json:"lane,omitempty"`
	Paths                   []string      `json:"paths,omitempty"`
	Priority                string        `json:"priority,omitempty"`
	Labels                  []string      `json:"labels,omitempty"`
	BoundaryNotes           []string      `json:"boundary_notes,omitempty"`
	ClosureBinding          string        `json:"closure_binding,omitempty"`
	WorkEstimate            string        `json:"work_estimate,omitempty"`
	ScopeContribution       string        `json:"scope_contribution,omitempty"`
	CompletionStandard      string        `json:"completion_standard,omitempty"`
	TargetEnvelope          string        `json:"target_operating_envelope,omitempty"`
	WitnessedEnvelope       string        `json:"witnessed_operating_envelope,omitempty"`
	EvidenceRefs            []EvidenceRef `json:"evidence_refs,omitempty"`
}

HandoffNextStep is one concrete follow-up the next agent can pick up. The CLI can sync each entry to one stable GitHub issue.

type HandoffReview added in v0.35.0

type HandoffReview struct {
	Schema       string               `json:"schema"`
	OK           bool                 `json:"ok"`
	Verdict      string               `json:"verdict"`
	Reasons      []string             `json:"reasons,omitempty"`
	TaskID       string               `json:"task_id,omitempty"`
	NextStepKeys []string             `json:"next_step_keys,omitempty"`
	IssueCount   int                  `json:"issue_count"`
	IssueReviews []issuepolicy.Review `json:"issue_reviews,omitempty"`
	// AchievedMaturity is the normalized maturity the handoff names for the
	// completed task — a stable JSON field for downstream status consumers
	// (#4640). Empty means the handoff did not declare one.
	AchievedMaturity string `json:"achieved_maturity,omitempty"`
}

HandoffReview is the pure verdict. OK means the handoff has enough witnessed completion evidence and next-step state for an automated loop to act on it.

func ReviewHandoff added in v0.35.0

func ReviewHandoff(h Handoff) HandoffReview

ReviewHandoff grades h without side effects. The gate deliberately requires VerifiedDone for StateDone handoffs: a task's own "done" string is not proof that it should fan out follow-up work.

func ReviewHandoffWithOptions added in v0.37.0

func ReviewHandoffWithOptions(h Handoff, opt HandoffReviewOptions) HandoffReview

ReviewHandoffWithOptions grades h with optional strict review of every next-step issue candidate. StrictScope is the guard used by the CLI before it can create GitHub follow-up issues.

type HandoffReviewOptions added in v0.37.0

type HandoffReviewOptions struct {
	StrictScope        bool
	StrictProjectWork  bool
	Live               bool
	DedupeChecked      bool
	DedupeCap          int
	ParentIssue        int
	ParentBaseline     float64
	CompletionStandard string
	TargetEnvelope     string
	WitnessedEnvelope  string
}

HandoffReviewOptions turns on the stricter GitHub-issue contract for callers that are about to plan or sync follow-up issues. The default ReviewHandoff path stays the basic task-completion gate for existing non-issue users.

Closure binding: StrictScope plus the typed next-step fields on HandoffNextStep (InScope, OutOfScope, DoneCondition, Witness, AcceptanceGate, Lane, Paths, BoundaryNotes, ClosureBinding, ...), the ReviewHandoffWithOptions gate below that refuses a vague next step via issuepolicy.ReviewCandidate before live issue sync, and HandoffIssueBody's stable-section rendering together satisfy #1460's ask in full, covered by handoff_test.go's TestReviewHandoffStrictScopeRejectsVagueNextStep, TestReviewHandoffStrictScopeAcceptsDispatchableNextStep, and TestHandoffIssueBodyIncludesStrictScopeSections, with cmd/fak/taskmgr.go's live sync path already wiring StrictScope: true. The work shipped citing #1639 and a generic worktree-sync subject, never #1460 itself; published history cannot be rewritten, so this comment restates the closure binding explicitly for the grep-based referee.

type HandoffTask added in v0.35.0

type HandoffTask struct {
	TaskID  string         `json:"task_id"`
	Title   string         `json:"title,omitempty"`
	State   State          `json:"state"`
	Witness *WitnessRecord `json:"witness,omitempty"`
}

HandoffTask is the compact task slice needed by the handoff gate. It mirrors TaskSnapshot's identity/state/witness fields without requiring a full runtime snapshot in hand-authored fixtures.

type IssueLabel added in v0.44.0

type IssueLabel struct {
	Name string `json:"name"`
}

type IssueMilestone added in v0.44.0

type IssueMilestone struct {
	Title string `json:"title"`
}

type KindWitness added in v0.37.0

type KindWitness struct {
	Witnesses map[string]Witness
}

KindWitness dispatches each EvidenceRef.Kind to the witness registered for that kind and folds the per-kind verdicts into one task/step witness record.

func (KindWitness) WitnessClaim added in v0.37.0

func (w KindWitness) WitnessClaim(c Claim) WitnessRecord

WitnessClaim verifies every ref whose kind is registered. Any refused kind refuses the whole claim. Unknown or unavailable kinds make the whole claim unavailable rather than silently passing as verified.

type LivenessClass added in v0.34.0

type LivenessClass string
const (
	LivenessUnknown LivenessClass = ""
	LivenessIdle    LivenessClass = "idle"
	LivenessLive    LivenessClass = "live"
	LivenessStalled LivenessClass = "stalled"
)

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

func NewManager

func NewManager(opts ...Option) *Manager

func (*Manager) BeatStep added in v0.34.0

func (m *Manager) BeatStep(taskID, stepID string) error

func (*Manager) BeatStepWithEvidence added in v0.37.0

func (m *Manager) BeatStepWithEvidence(taskID, stepID string, output []byte) (WitnessRecord, error)

BeatStepWithEvidence records a step heartbeat and immediately grades the output shape for that beat. The parent task heartbeat is updated by BeatStep, while the output verdict is stored on the step witness rung.

func (*Manager) BeatTask added in v0.34.0

func (m *Manager) BeatTask(taskID string) error

func (*Manager) BeatTaskWithEvidence added in v0.37.0

func (m *Manager) BeatTaskWithEvidence(taskID string, output []byte) (WitnessRecord, error)

BeatTaskWithEvidence records a task heartbeat and immediately grades the output shape for that beat. This is the at-origin form of "beat, then inspect the transcript later": the task can be live while its output witness refuses.

func (*Manager) CancelTask

func (m *Manager) CancelTask(taskID, reason string) error

func (*Manager) FailStep

func (m *Manager) FailStep(taskID, stepID, reason string) error

func (*Manager) FailTask

func (m *Manager) FailTask(taskID, reason string) error

func (*Manager) FinishStep

func (m *Manager) FinishStep(taskID, stepID string) error

func (*Manager) FinishTask

func (m *Manager) FinishTask(taskID string) error

func (*Manager) SetStepProgress

func (m *Manager) SetStepProgress(taskID, stepID string, done, total float64, unit string) error

func (*Manager) SetStepWitness

func (m *Manager) SetStepWitness(taskID, stepID string, rec WitnessRecord) error

SetStepWitness attaches a precomputed WitnessRecord to a step, leaving the step's claimed State untouched.

func (*Manager) SetTaskProgress

func (m *Manager) SetTaskProgress(taskID string, done, total float64, unit string) error

func (*Manager) SetTaskWitness

func (m *Manager) SetTaskWitness(taskID string, rec WitnessRecord) error

SetTaskWitness attaches a precomputed WitnessRecord to a task, leaving the task's claimed State untouched. It errors on an unknown task or a verified state outside the vocabulary.

func (*Manager) Snapshot

func (m *Manager) Snapshot() Snapshot

func (*Manager) StartStep

func (m *Manager) StartStep(taskID string, spec StepSpec) (*Step, error)

func (*Manager) StartTask

func (m *Manager) StartTask(spec TaskSpec) (*Task, error)

func (*Manager) Task

func (m *Manager) Task(id string) (*Task, bool)

func (*Manager) WitnessStep added in v0.37.0

func (m *Manager) WitnessStep(taskID, stepID string, w Witness, refs []EvidenceRef) (WitnessRecord, error)

WitnessStep builds a Claim from the step's current claimed state plus refs, runs w against it (outside the lock, so a witness may do I/O), stores the resulting record, and returns it. The claimed State is never overwritten.

func (*Manager) WitnessTask

func (m *Manager) WitnessTask(taskID string, w Witness, refs []EvidenceRef) (WitnessRecord, error)

WitnessTask builds a Claim from the task's current claimed state plus refs, runs w against it (outside the lock, so a witness may do I/O), stores the resulting record, and returns it. The claimed State is never overwritten: a refused or unavailable witness leaves the claim standing and visible alongside the verdict.

type Option

type Option func(*Manager)

func WithClock

func WithClock(clock func() time.Time) Option

func WithDefaultOriginWitnesses added in v0.37.0

func WithDefaultOriginWitnesses() Option

WithDefaultOriginWitnesses installs the built-in origin witnesses for the ref kinds taskmgr knows how to read locally: path existence and output shape.

func WithLivenessTimeout added in v0.34.0

func WithLivenessTimeout(timeout time.Duration) Option

func WithOriginWitness added in v0.37.0

func WithOriginWitness(w Witness) Option

WithOriginWitness runs w immediately when a task or step is started with EvidenceRefs. This moves the quality check to the origin record instead of relying on an after-the-fact scorecard pass to discover missing or bad evidence.

func WithOriginWitnessByKind added in v0.37.0

func WithOriginWitnessByKind(witnesses map[string]Witness) Option

WithOriginWitnessByKind installs a by-kind origin witness registry. It is the ergonomic version of WithOriginWitness for callers that already know the EvidenceRef.Kind but should not have to select PathWitness/ShapeWitness by hand.

func WithSampler

func WithSampler(sampler Sampler) Option

type OutputShapeSLO added in v0.37.0

type OutputShapeSLO struct {
	MaxRepeat float64 `json:"max_repeat,omitempty"`
	MaxChars  int     `json:"max_chars,omitempty"`
	NGram     int     `json:"ngram,omitempty"`
}

OutputShapeSLO carries the ShapeWitness limits a task or step expects for beat-time output. A nil OutputShape means the default ShapeWitness limits apply only when a caller explicitly runs a shape witness; a non-nil value makes the expectation visible in the spec and snapshot.

type PathWitness

type PathWitness struct {
	Exists func(string) bool
}

PathWitness corroborates a claim by checking that every "path" EvidenceRef exists on disk. It is a small, network-free example of an out-of-process witness: it reads the filesystem, not the process's own claim. Exists is injectable so tests need no real files; it defaults to an os.Stat probe.

func (PathWitness) WitnessClaim

func (w PathWitness) WitnessClaim(c Claim) WitnessRecord

WitnessClaim returns VerifiedDone when every referenced path exists, VerifiedRefused when one is missing, and VerifiedUnavailable when the claim carries no path evidence to read back.

type Progress

type Progress struct {
	Done    float64  `json:"done,omitempty"`
	Total   float64  `json:"total,omitempty"`
	Unit    string   `json:"unit,omitempty"`
	Percent *float64 `json:"percent,omitempty"`
}

type QualitySLO added in v0.37.0

type QualitySLO struct {
	OutputShape          *OutputShapeSLO `json:"output_shape,omitempty"`
	MaxStallCount        *int            `json:"max_stall_count,omitempty"`
	RequiredWitnessState VerifiedState   `json:"required_witness_state,omitempty"`
}

QualitySLO is the origin-declared quality contract for a task or step. It is copied into snapshots so readers can see the expectation next to the evidence that currently passes or fails it.

type QualitySLOStatus added in v0.37.0

type QualitySLOStatus struct {
	Passed       bool          `json:"passed"`
	Reasons      []string      `json:"reasons,omitempty"`
	WitnessState VerifiedState `json:"witness_state,omitempty"`
	StallCount   int           `json:"stall_count"`
}

QualitySLOStatus is the current evidence verdict against a QualitySLO.

type Queue added in v0.44.0

type Queue struct {
	Schema string      `json:"schema"`
	Leaves []QueueLeaf `json:"leaves"`
}

func BuildQueue added in v0.44.0

func BuildQueue(issues []QueueIssue, attempts []Attempt) Queue

BuildQueue folds durable issue contracts and ephemeral attempts without allowing attempt telemetry to mutate the durable leaf state.

type QueueIssue added in v0.44.0

type QueueIssue struct {
	Number    int             `json:"number"`
	Title     string          `json:"title"`
	State     string          `json:"state"`
	Body      string          `json:"body"`
	Labels    []IssueLabel    `json:"labels"`
	Milestone *IssueMilestone `json:"milestone,omitempty"`
}

type QueueLeaf added in v0.44.0

type QueueLeaf struct {
	Number       int       `json:"number"`
	State        string    `json:"state"`
	DurableState string    `json:"durable_state"`
	Priority     string    `json:"priority"`
	Generation   string    `json:"generation"`
	Lane         string    `json:"lane"`
	Title        string    `json:"title"`
	Outcome      string    `json:"outcome"`
	Requires     []int     `json:"requires"`
	Witness      string    `json:"witness"`
	Parent       int       `json:"parent,omitempty"`
	Attempts     []Attempt `json:"attempts,omitempty"`
}

type RepeatSignal added in v0.34.0

type RepeatSignal struct {
	// Stalled is true once Count has reached the monitor's threshold for Hash within
	// the current turn — the task is repeating the same tool input and not progressing.
	Stalled bool `json:"stalled"`
	// Hash is the tool-input hash this observation carried (echoed for the caller's log).
	Hash string `json:"hash,omitempty"`
	// Count is how many times Hash has been seen in the current turn, including this
	// observation. A fresh hash is 1; the threshold (default 3) is the trip point.
	Count int `json:"count"`
	// Turn is the turn index this observation landed in (0-based; advanced by NextTurn).
	Turn int `json:"turn"`
	// FirstSeenUnixNano is when Hash first appeared in the current turn — so a host can
	// report how long the loop has been wedged on it.
	FirstSeenUnixNano int64 `json:"first_seen_unix_nano,omitempty"`
}

RepeatSignal is the verdict ObserveToolCall returns for one observed tool call. It is a pure value: the caller decides what to do (surface it, refuse the beat, route the task to a replan). Stalled is the one-bit gate; the rest is evidence.

type RepeatStallMonitor added in v0.34.0

type RepeatStallMonitor struct {
	// contains filtered or unexported fields
}

RepeatStallMonitor tracks within-turn tool-input repetition per task. It is concurrency-safe and clock-injectable (mirroring Manager), so a test proves the trip math without sleeping. Construct it once per process alongside the Manager.

func NewRepeatStallMonitor added in v0.34.0

func NewRepeatStallMonitor(opts ...RepeatStallOption) *RepeatStallMonitor

NewRepeatStallMonitor builds a monitor with the default threshold and clock, plus any overriding options.

func (*RepeatStallMonitor) Forget added in v0.34.0

func (r *RepeatStallMonitor) Forget(taskID string)

Forget drops all repeat state for taskID — a host calls it when the task ends so the monitor does not retain per-task maps for the life of the process.

func (*RepeatStallMonitor) NextTurn added in v0.34.0

func (r *RepeatStallMonitor) NextTurn(taskID string) int

NextTurn advances taskID to the next turn and clears its within-turn repeat counts, returning the new turn index. A host calls it at each model-turn boundary so the "within ONE turn" scope is honored: the same tool call across DIFFERENT turns is normal agent work, only a within-turn repeat is the wedge this catches.

func (*RepeatStallMonitor) ObserveToolCall added in v0.34.0

func (r *RepeatStallMonitor) ObserveToolCall(taskID, hash string) RepeatSignal

ObserveToolCall records one tool-input hash for taskID in the current turn and returns the resulting signal. An empty hash is a no-op observation (Count 0, never stalled): a caller that cannot content-address a call should not have it counted as a repeat of "the empty call". The same hash seen `threshold` times within one turn returns Stalled=true; advancing the turn (NextTurn) clears the counts so a legitimate re-issue in a new turn starts fresh.

func (*RepeatStallMonitor) Threshold added in v0.34.0

func (r *RepeatStallMonitor) Threshold() int

Threshold reports the configured repeat count at which a hash trips the stall.

type RepeatStallOption added in v0.34.0

type RepeatStallOption func(*RepeatStallMonitor)

RepeatStallOption configures a RepeatStallMonitor at construction.

func WithRepeatClock added in v0.34.0

func WithRepeatClock(clock func() time.Time) RepeatStallOption

WithRepeatClock injects the monitor's clock (defaults to time.Now). A nil clock is ignored, mirroring WithClock on the Manager.

func WithRepeatThreshold added in v0.34.0

func WithRepeatThreshold(threshold int) RepeatStallOption

WithRepeatThreshold overrides DefaultRepeatStallThreshold. A value < 2 is ignored (a threshold of 1 would flag every single call as a stall, which is meaningless).

type ResourceDelta

type ResourceDelta struct {
	WallSeconds    float64 `json:"wall_s"`
	CPUSeconds     float64 `json:"cpu_s"`
	HeapAllocBytes int64   `json:"heap_alloc_bytes,omitempty"`
	HeapInuseBytes int64   `json:"heap_inuse_bytes,omitempty"`
	HeapSysBytes   int64   `json:"heap_sys_bytes,omitempty"`
	SysBytes       int64   `json:"sys_bytes,omitempty"`
	Goroutines     int     `json:"goroutines,omitempty"`
}

type ResourceSample

type ResourceSample struct {
	TSUnixNano     int64   `json:"ts_unix_nano"`
	WallSeconds    float64 `json:"wall_s"`
	CPUSeconds     float64 `json:"cpu_s"`
	HeapAllocBytes uint64  `json:"heap_alloc_bytes,omitempty"`
	HeapInuseBytes uint64  `json:"heap_inuse_bytes,omitempty"`
	HeapSysBytes   uint64  `json:"heap_sys_bytes,omitempty"`
	SysBytes       uint64  `json:"sys_bytes,omitempty"`
	Goroutines     int     `json:"goroutines,omitempty"`
}

func SampleRuntime

func SampleRuntime(processStart, now time.Time) ResourceSample

type ResourceWindow

type ResourceWindow struct {
	Start   ResourceSample `json:"start"`
	Current ResourceSample `json:"current"`
	Delta   ResourceDelta  `json:"delta"`
}

type Sampler

type Sampler func(processStart, now time.Time) ResourceSample

Sampler reads this process' resource state. The default sampler uses the Go runtime: memory stats, goroutine count, and runtime CPU-class seconds when the Go toolchain exposes that metric. The clock is injectable so tests can prove ETA and elapsed-time math without sleeping.

type ShapeWitness added in v0.35.0

type ShapeWitness struct {
	// Limits are the answershape thresholds this witness grades against. The zero
	// value is valid; effectiveLimits supplies sane defaults for any unset knob.
	Limits answershape.Limits
}

ShapeWitness grades the CLAIMED OUTPUT TEXT of a beat — carried on the Claim as one or more EvidenceRef{Kind:"output"} refs — through internal/answershape, and refuses a degenerate (looping / runaway) beat as a silent semantic failure. It is the "healthy process, degraded output" witness: a task can be alive and beating while emitting garbage, and a liveness rung that only watches heartbeats cannot tell the two apart. ShapeWitness reads the actual emitted bytes back — a source the reporting process's own "I'm fine" claim does not author — exactly as PathWitness reads the filesystem, and returns:

  • VerifiedRefused when a graded output is DEGENERATE (answershape Report.Degenerate true). The answershape reason(s) go into Verdict/Detail so the refusal explains WHY (which repetition/verbosity sub-signal tripped) without dumping the payload.
  • VerifiedDone when every graded output is present and in-shape.
  • VerifiedUnavailable when the Claim carries NO "output" ref to read back — there is nothing to grade, so the claim is neither confirmed nor refused. It must NEVER silently downgrade to VerifiedDone (the PathWitness "no path evidence" rule): "I saw no output" is not "I saw good output".

Limits are caller-tunable. A zero-value ShapeWitness is usable: effectiveLimits fills the repeat threshold and n-gram width from answershape's defaults so the witness grades meaningfully out of the box; MaxChars stays 0 (the verbosity check is opt-in, since a long-but-coherent answer is not a semantic failure).

Fence: ShapeWitness only RECORDS a graded verdict beside the claimed State; it never overwrites the claim and never gates admission. Manager.WitnessTask preserves that separation — a refused beat leaves the task's claimed State standing, visible next to the verified_refused rung.

func (ShapeWitness) WitnessClaim added in v0.35.0

func (w ShapeWitness) WitnessClaim(c Claim) WitnessRecord

WitnessClaim grades every EvidenceRef{Kind:"output"} on c through answershape. Mirrors PathWitness.WitnessClaim: it counts the refs it can actually read back, refuses on the first degenerate one (most-restrictive-wins — a single garbage beat taints the claim), confirms when all are in-shape, and reports unavailable when there is nothing of the right kind to grade.

type Snapshot

type Snapshot struct {
	Schema          string            `json:"schema"`
	ProcessID       int               `json:"process_id"`
	GoOS            string            `json:"goos"`
	GoArch          string            `json:"goarch"`
	GoVersion       string            `json:"go_version"`
	StartedUnixNano int64             `json:"started_unix_nano"`
	TSUnixNano      int64             `json:"ts_unix_nano"`
	UptimeSeconds   float64           `json:"uptime_s"`
	Resource        ResourceSample    `json:"resource"`
	ResourceDelta   ResourceDelta     `json:"resource_delta"`
	Tasks           []TaskSnapshot    `json:"tasks"`
	Concepts        []ConceptUsage    `json:"concepts,omitempty"`
	Labels          map[string]string `json:"labels,omitempty"`
}

type State

type State string
const (
	StateRunning  State = "running"
	StateDone     State = "done"
	StateFailed   State = "failed"
	StateCanceled State = "canceled"
)

type Step

type Step struct {
	// contains filtered or unexported fields
}

func (*Step) Beat added in v0.34.0

func (s *Step) Beat() error

func (*Step) BeatWithEvidence added in v0.37.0

func (s *Step) BeatWithEvidence(output []byte) (WitnessRecord, error)

BeatWithEvidence records a step heartbeat and grades the emitted output bytes in the same origin call. The returned WitnessRecord is also stored on the step snapshot, beside the claimed state.

func (*Step) Fail

func (s *Step) Fail(reason string) error

func (*Step) Finish

func (s *Step) Finish() error

func (*Step) SetProgress

func (s *Step) SetProgress(done, total float64, unit string) error

type StepSnapshot

type StepSnapshot struct {
	StepID             string            `json:"step_id"`
	Title              string            `json:"title,omitempty"`
	Concept            string            `json:"concept,omitempty"`
	State              State             `json:"state"`
	Reason             string            `json:"reason,omitempty"`
	LivenessClass      LivenessClass     `json:"liveness_class,omitempty"`
	BeatsSeen          int64             `json:"beats_seen,omitempty"`
	LastBeatUnixNano   int64             `json:"last_beat_unix_nano,omitempty"`
	LastBeatAgeSeconds *float64          `json:"last_beat_age_s,omitempty"`
	StartedUnixNano    int64             `json:"started_unix_nano"`
	EndedUnixNano      int64             `json:"ended_unix_nano,omitempty"`
	RuntimeSeconds     float64           `json:"runtime_s"`
	Progress           Progress          `json:"progress"`
	ETASeconds         *float64          `json:"eta_s,omitempty"`
	ETAUnixNano        *int64            `json:"estimated_completion_unix_nano,omitempty"`
	Resource           ResourceWindow    `json:"resource"`
	Labels             map[string]string `json:"labels,omitempty"`
	EvidenceRefs       []EvidenceRef     `json:"evidence_refs,omitempty"`
	QualitySLO         *QualitySLO       `json:"quality_slo,omitempty"`
	QualitySLOStatus   *QualitySLOStatus `json:"quality_slo_status,omitempty"`
	// Witness is the optional, independently-attested completion rung for this
	// step. Nil means claimed-only; the claimed State above is never overwritten.
	Witness *WitnessRecord `json:"witness,omitempty"`
}

func (StepSnapshot) VerifiedProgressing added in v0.37.0

func (s StepSnapshot) VerifiedProgressing() bool

VerifiedProgressing reports whether the step is making REAL progress by the same refused-witness rule TaskSnapshot uses.

type StepSpec

type StepSpec struct {
	StepID       string            `json:"step_id"`
	Title        string            `json:"title,omitempty"`
	Concept      string            `json:"concept,omitempty"`
	Total        float64           `json:"total,omitempty"`
	Unit         string            `json:"unit,omitempty"`
	Labels       map[string]string `json:"labels,omitempty"`
	EvidenceRefs []EvidenceRef     `json:"evidence_refs,omitempty"`
	QualitySLO   *QualitySLO       `json:"quality_slo,omitempty"`
}

type Task

type Task struct {
	// contains filtered or unexported fields
}

func (*Task) Beat added in v0.34.0

func (t *Task) Beat() error

func (*Task) BeatWithEvidence added in v0.37.0

func (t *Task) BeatWithEvidence(output []byte) (WitnessRecord, error)

BeatWithEvidence records a task heartbeat and grades the emitted output bytes in the same origin call. The returned WitnessRecord is also stored on the task snapshot, beside the claimed state.

func (*Task) Cancel

func (t *Task) Cancel(reason string) error

func (*Task) Fail

func (t *Task) Fail(reason string) error

func (*Task) Finish

func (t *Task) Finish() error

func (*Task) SetProgress

func (t *Task) SetProgress(done, total float64, unit string) error

func (*Task) StartConceptStep

func (t *Task) StartConceptStep(spec StepSpec) (*Step, error)

StartConceptStep starts a step after normalizing its Concept through NormalizeConcept, so callers get a consistent bucket without hand-typing the canonical string. All other StepSpec fields are passed through unchanged.

func (*Task) StartModelStep

func (t *Task) StartModelStep(stepID, title string) (*Step, error)

func (*Task) StartObserveStep

func (t *Task) StartObserveStep(stepID, title string) (*Step, error)

StartObserveStep, StartModelStep, StartToolStep, and StartVerifyStep are convenience constructors for the most common served-request phases. Each applies the matching default concept; reach for StartConceptStep when you need to set Total, Unit, or Labels as well.

func (*Task) StartStep

func (t *Task) StartStep(spec StepSpec) (*Step, error)

func (*Task) StartToolStep

func (t *Task) StartToolStep(stepID, title string) (*Step, error)

func (*Task) StartVerifyStep

func (t *Task) StartVerifyStep(stepID, title string) (*Step, error)

type TaskSnapshot

type TaskSnapshot struct {
	TaskID             string            `json:"task_id"`
	Title              string            `json:"title,omitempty"`
	State              State             `json:"state"`
	Reason             string            `json:"reason,omitempty"`
	LivenessClass      LivenessClass     `json:"liveness_class,omitempty"`
	BeatsSeen          int64             `json:"beats_seen,omitempty"`
	LastBeatUnixNano   int64             `json:"last_beat_unix_nano,omitempty"`
	LastBeatAgeSeconds *float64          `json:"last_beat_age_s,omitempty"`
	StartedUnixNano    int64             `json:"started_unix_nano"`
	EndedUnixNano      int64             `json:"ended_unix_nano,omitempty"`
	RuntimeSeconds     float64           `json:"runtime_s"`
	Progress           Progress          `json:"progress"`
	ETASeconds         *float64          `json:"eta_s,omitempty"`
	ETAUnixNano        *int64            `json:"estimated_completion_unix_nano,omitempty"`
	CurrentStep        string            `json:"current_step,omitempty"`
	Resource           ResourceWindow    `json:"resource"`
	Steps              []StepSnapshot    `json:"steps,omitempty"`
	Concepts           []ConceptUsage    `json:"concepts,omitempty"`
	Labels             map[string]string `json:"labels,omitempty"`
	EvidenceRefs       []EvidenceRef     `json:"evidence_refs,omitempty"`
	QualitySLO         *QualitySLO       `json:"quality_slo,omitempty"`
	QualitySLOStatus   *QualitySLOStatus `json:"quality_slo_status,omitempty"`
	// Witness is the optional, independently-attested completion rung. It is nil
	// for a claimed-only task; the claimed State above is never overwritten.
	Witness *WitnessRecord `json:"witness,omitempty"`
}

func (TaskSnapshot) VerifiedProgressing added in v0.35.0

func (t TaskSnapshot) VerifiedProgressing() bool

VerifiedProgressing reports whether the task is making REAL progress, as opposed to "advancing on garbage". It is a read-only DERIVED view over the claimed State and witness rungs beside it: it never mutates either. The answer is true UNLESS a task or child-step witness ran and refused.

That single refused case is the "alive but emitting garbage" signal: a task can be StateRunning and beating (liveness says live) while a ShapeWitness has graded its output degenerate. Heartbeat liveness alone cannot distinguish a healthy task from a looping one; pairing running-and-beating with VerifiedProgressing()==false is what surfaces the silent semantic failure. A claimed-only task (no witness) and a task whose witness confirmed or could-not-read (done / unavailable / unknown) are all reported as progressing — only an affirmative refusal flips the bit, so the derived view never invents a problem the witness did not attest.

type TaskSpec

type TaskSpec struct {
	TaskID       string            `json:"task_id"`
	Title        string            `json:"title,omitempty"`
	Total        float64           `json:"total,omitempty"`
	Unit         string            `json:"unit,omitempty"`
	Labels       map[string]string `json:"labels,omitempty"`
	EvidenceRefs []EvidenceRef     `json:"evidence_refs,omitempty"`
	QualitySLO   *QualitySLO       `json:"quality_slo,omitempty"`
}

type VerifiedState

type VerifiedState string

VerifiedState is the witnessed-completion rung, kept deliberately separate from the claimed State a process reports about itself. A process may claim StateDone; only a Witness that reads the effect back from a source the process did not author can raise the record to VerifiedDone. The task manager must never treat its own completion string as proof.

const (
	// VerifiedUnknown is the zero value: no witness has run, so the record carries
	// only the process's own claim. Every snapshot defaults to this, which is why
	// witness-free snapshots stay valid.
	VerifiedUnknown VerifiedState = ""
	// VerifiedDone means a witness confirmed the claimed effect from evidence.
	VerifiedDone VerifiedState = "verified_done"
	// VerifiedRefused means a witness ran and the evidence contradicted the claim.
	VerifiedRefused VerifiedState = "verified_refused"
	// VerifiedUnavailable means a witness was asked but could not read the effect
	// back (no network, a missing ref). The claim is neither confirmed nor refused;
	// it must not silently downgrade to claimed-done.
	VerifiedUnavailable VerifiedState = "verified_unavailable"
)

type Witness

type Witness interface {
	WitnessClaim(Claim) WitnessRecord
}

Witness reads an effect back from a source the reporting process did not author and returns an evidence-backed record. A Witness must never treat the claimed State as proof — that separation is the whole point of the rung. The interface is intentionally tiny so a host can bridge git/DOS evidence without this foundation-tier package importing DOS.

type WitnessFunc

type WitnessFunc func(Claim) WitnessRecord

WitnessFunc adapts an ordinary function to the Witness interface.

func (WitnessFunc) WitnessClaim

func (f WitnessFunc) WitnessClaim(c Claim) WitnessRecord

WitnessClaim calls the underlying function.

type WitnessRecord

type WitnessRecord struct {
	VerifiedState   VerifiedState `json:"verified_state"`
	Source          string        `json:"source,omitempty"`  // which witness produced this
	Verdict         string        `json:"verdict,omitempty"` // the witness's raw verdict text
	SHA             string        `json:"sha,omitempty"`
	Detail          string        `json:"detail,omitempty"`
	EvidenceRefs    []EvidenceRef `json:"evidence_refs,omitempty"`
	CheckedUnixNano int64         `json:"checked_unix_nano,omitempty"`
}

WitnessRecord is the evidence-backed verdict a Witness attaches to a task or step. It never replaces the claimed State; it sits beside it as a separate rung, so a snapshot can hold both "the process says done" and "a witness has/has not confirmed it".

Jump to

Keyboard shortcuts

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