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
- func DefaultConcepts() []string
- func DefaultOriginWitnesses() map[string]Witness
- func HandoffIssueBody(h Handoff, step HandoffNextStep) string
- func HandoffMarkerKey(body string) string
- func IsDefaultConcept(concept string) bool
- func NormalizeConcept(concept string) string
- func RedactCommandOutput(s string) string
- func RenderQueue(w io.Writer, q Queue, drilldown bool)
- func ValidateSnapshot(s Snapshot) error
- type ArtifactCommand
- type ArtifactResult
- type Attempt
- type Claim
- type ConceptUsage
- type EvidenceRef
- type Handoff
- type HandoffDraftOptions
- type HandoffEvidenceInputs
- type HandoffIssue
- type HandoffIssuePlanRow
- type HandoffNextStep
- type HandoffReview
- type HandoffReviewOptions
- type HandoffTask
- type IssueLabel
- type IssueMilestone
- type KindWitness
- type LivenessClass
- type Manager
- func (m *Manager) BeatStep(taskID, stepID string) error
- func (m *Manager) BeatStepWithEvidence(taskID, stepID string, output []byte) (WitnessRecord, error)
- func (m *Manager) BeatTask(taskID string) error
- func (m *Manager) BeatTaskWithEvidence(taskID string, output []byte) (WitnessRecord, error)
- func (m *Manager) CancelTask(taskID, reason string) error
- func (m *Manager) FailStep(taskID, stepID, reason string) error
- func (m *Manager) FailTask(taskID, reason string) error
- func (m *Manager) FinishStep(taskID, stepID string) error
- func (m *Manager) FinishTask(taskID string) error
- func (m *Manager) SetStepProgress(taskID, stepID string, done, total float64, unit string) error
- func (m *Manager) SetStepWitness(taskID, stepID string, rec WitnessRecord) error
- func (m *Manager) SetTaskProgress(taskID string, done, total float64, unit string) error
- func (m *Manager) SetTaskWitness(taskID string, rec WitnessRecord) error
- func (m *Manager) Snapshot() Snapshot
- func (m *Manager) StartStep(taskID string, spec StepSpec) (*Step, error)
- func (m *Manager) StartTask(spec TaskSpec) (*Task, error)
- func (m *Manager) Task(id string) (*Task, bool)
- func (m *Manager) WitnessStep(taskID, stepID string, w Witness, refs []EvidenceRef) (WitnessRecord, error)
- func (m *Manager) WitnessTask(taskID string, w Witness, refs []EvidenceRef) (WitnessRecord, error)
- type Option
- type OutputShapeSLO
- type PathWitness
- type Progress
- type QualitySLO
- type QualitySLOStatus
- type Queue
- type QueueIssue
- type QueueLeaf
- type RepeatSignal
- type RepeatStallMonitor
- type RepeatStallOption
- type ResourceDelta
- type ResourceSample
- type ResourceWindow
- type Sampler
- type ShapeWitness
- type Snapshot
- type State
- type Step
- type StepSnapshot
- type StepSpec
- type Task
- func (t *Task) Beat() error
- func (t *Task) BeatWithEvidence(output []byte) (WitnessRecord, error)
- func (t *Task) Cancel(reason string) error
- func (t *Task) Fail(reason string) error
- func (t *Task) Finish() error
- func (t *Task) SetProgress(done, total float64, unit string) error
- func (t *Task) StartConceptStep(spec StepSpec) (*Step, error)
- func (t *Task) StartModelStep(stepID, title string) (*Step, error)
- func (t *Task) StartObserveStep(stepID, title string) (*Step, error)
- func (t *Task) StartStep(spec StepSpec) (*Step, error)
- func (t *Task) StartToolStep(stepID, title string) (*Step, error)
- func (t *Task) StartVerifyStep(stepID, title string) (*Step, error)
- type TaskSnapshot
- type TaskSpec
- type VerifiedState
- type Witness
- type WitnessFunc
- type WitnessRecord
Constants ¶
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.
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" )
const DefaultLivenessTimeout = 30 * time.Second
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.
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.
const (
// PathRefKind is the EvidenceRef.Kind PathWitness reads back.
PathRefKind = "path"
)
const SchemaQueue = "fak-task-queue/1"
const SchemaSnapshot = "fak.task-manager-snapshot.v1"
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
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
HandoffMarkerKey extracts the stable marker key from an issue body.
func IsDefaultConcept ¶
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 ¶
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
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 ValidateSnapshot ¶
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
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 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
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 (*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) 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 (*Manager) FinishStep ¶
func (*Manager) FinishTask ¶
func (*Manager) SetStepProgress ¶
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 (*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) 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 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 WithOriginWitness ¶ added in v0.37.0
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
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 ¶
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 ¶
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 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
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 Step ¶
type Step struct {
// contains filtered or unexported fields
}
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.
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) 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) StartConceptStep ¶
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) StartObserveStep ¶
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.
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" // 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".