Documentation
¶
Overview ¶
Package tasks provides the small wiring layer between pasture's CLI and the Provenance task tracker library. It resolves the default database path, derives the default namespace, and centralises Tracker open semantics so every subcommand uses the same conventions.
Index ¶
- Constants
- func DefaultDBPath() string
- func DefaultNamespace() (string, error)
- func OpenAuditDBForFreeFloating(dbPath string) (*sql.DB, error)
- func OpenTaskTracker(dbPath string) (protocol.TaskTracker, error)
- func OpenTracker(dbPath string) (provenance.Tracker, error)
- func RecordGitEvent(ctx context.Context, tracker protocol.TaskTracker, auditDB *sql.DB, sha string, ...) (int64, error)
- func RecordSessionEvent(ctx context.Context, tracker protocol.TaskTracker, auditDB *sql.DB, ...) (int64, error)
- func RecordSkillEvent(ctx context.Context, tracker protocol.TaskTracker, auditDB *sql.DB, ...) (int64, error)
- func RegisterWellKnownAgents(ctx context.Context, tracker protocol.TaskTracker, cache *WellKnownAgentCache) error
- func ResolveNamespace(explicit string) (string, error)
- type WellKnownAgentCache
- type WellKnownAgentSpec
Constants ¶
const ( // EventGitCommit fires after a git commit completes (e.g., from a // Claude Code Stop hook firing after `git agent-commit`). Payload should // contain at least {"sha": "<commit-sha>"}. EventGitCommit protocol.EventType = "GitCommit" // EventGitPush fires after a git push completes. Payload should contain // at least {"refs": ["<branch>"], "remote": "<remote>"}. EventGitPush protocol.EventType = "GitPush" // EventGitRebase fires after a git rebase completes. Payload should // contain at least {"onto": "<base-ref>", "head": "<head-ref>"}. EventGitRebase protocol.EventType = "GitRebase" // EventSkillInvoked fires when a /pasture:* skill is invoked (per Pasture // URD R9). Payload should contain at least {"skill": "pasture:<name>"}. EventSkillInvoked protocol.EventType = "SkillInvoked" // EventSessionRecorded fires when a Claude Code session is recorded. // Payload should contain at least {"sessionId": "<id>"}. EventSessionRecorded protocol.EventType = "SessionRecorded" )
Free-floating event types are open-string by design (§7.3 / §7.5). These constants are the canonical spellings used by the in-tree hook handlers and skill entry points; external plugins MAY define their own event_type values.
const ( // DefaultGitRole is the canonical Role string written into // audit_events.role for git events recorded via RecordGitEvent. Matches // PROPOSAL-2 §7.7.2's pattern "pasture/automaton/hook/<hook-name>". DefaultGitRole = "pasture/automaton/hook/git-recorder" // DefaultSkillRole is the canonical Role string written into // audit_events.role for skill-invocation events recorded via // RecordSkillEvent. DefaultSkillRole = "pasture/automaton/hook/skill-recorder" // DefaultSessionRole is the canonical Role string written into // audit_events.role for Claude Code session events recorded via // RecordSessionEvent. DefaultSessionRole = "pasture/automaton/hook/session-recorder" )
─── Default role attribution for free-floating events ──────────────────────
PROPOSAL-2 §7.7.2 enumerates well-known automaton names. The in-tree recorders in this file use the corresponding hook-handler names so that once S3's audit-side legacy-role compatibility shim resolves Role to an agent_id (and post-S8 when the workflow boundary supplies agent_id directly), free-floating events flow into the right pasture_agent_categories row. The role is overridable per call so plugins can attribute their own hook handlers without re-entering this file.
const DBPathEnv = "PASTURE_DB_PATH"
DBPathEnv is the environment variable users can set to override the default unified pasture database location (see DefaultDBPath).
const DefaultDBFilename = "pasture.db"
DefaultDBFilename is the filename portion of the unified pasture database. PROPOSAL-2 §7.1 binds this to `pasture.db` so both the Provenance subsystem (task CRUD, agents, edges, labels, comments, activities) and the audit subsystem (audit_events, context_edges, sessions) open the same file.
Pre-PROPOSAL-2 the `pasture` CLI defaulted to `provenance.db` and `pastured` defaulted to `audit.db`; SLICE-10 collapses both to `pasture.db`. See PROPOSAL-2 §7.1 + §11 Scenario 9 for the unification rationale and the hjsdt-CLI byte-identical-output requirement that drives the migration.
const WellKnownAgentCount = 15
WellKnownAgentCount is the total number of canonical well-known agents registered at daemon startup (PROPOSAL-2 §7.7.2). It is exported so tests can assert the table size without re-counting; a divergence between this constant and len(WellKnownAgents()) is a programming error caught by init-time assertion in well_known_registry_init.go (or, equivalently, by the unit tests in well_known_test.go).
The breakdown is documented in the package-level comment above:
1 (ConstraintChecker) + 3 (TransitionGate) + 9 (HookHandler) + 1 (ConsensusReached) + 1 (CreateFollowup) = 15
const WellKnownAgentNamespace = "pasture"
WellKnownAgentNamespace is the namespace string passed to `provenance.Tracker.RegisterSoftwareAgent`. All well-known agents live under the `pasture` namespace; this distinguishes them from human and ML agents (which use other namespaces) and from external software agents registered by other tools.
const WellKnownAgentSource = "github.com/dayvidpham/pasture"
WellKnownAgentSource is the `source` column written to `agents_software` when minting a well-known agent's Provenance row. It is set to the pasture repo URL so downstream PROV-O queries can discover the agent's provenance.
const WellKnownAgentVersion = "v1"
WellKnownAgentVersion is the `version` column written to `agents_software` when minting a well-known agent's Provenance row. It moves in lockstep with the daemon binary: bumping the daemon version does NOT re-register agents (idempotency is keyed on logical name, not version), so this string is effectively a stamp recording which binary version first observed the agent's absence.
Variables ¶
This section is empty.
Functions ¶
func DefaultDBPath ¶
func DefaultDBPath() string
DefaultDBPath returns the conventional path for the unified pasture database, used by both the `pasture` CLI and the `pastured` daemon (PROPOSAL-2 §7.1).
Resolution order:
- $PASTURE_DB_PATH
- $XDG_DATA_HOME/pasture/pasture.db
- $HOME/.local/share/pasture/pasture.db
- .pasture/pasture.db (last-resort relative fallback when $HOME is unset)
The directory is NOT created here; the caller (typically OpenTaskTracker or OpenTracker) calls os.MkdirAll on filepath.Dir of the result before the file is opened for writing.
func DefaultNamespace ¶
DefaultNamespace returns the namespace URI that pasture uses when the user has not specified --namespace explicitly. It delegates to the Provenance namespace package so the same derivation rules apply across the toolkit.
func OpenAuditDBForFreeFloating ¶
OpenAuditDBForFreeFloating opens an auxiliary *sql.DB handle on the same pasture.db file that protocol.TaskTracker writes to, with the same WAL + busy_timeout=5000 pragmas openTaskTrackerImpl applies. It exists so callers outside internal/tasks (e.g. cmd/pastured wiring code, test code) can obtain a direct SQL handle for ad-hoc queries or pasture-specific table writes without depending on internal trackerImpl details.
Note: the free-floating recording helpers (RecordGitEvent / RecordSkillEvent / RecordSessionEvent) no longer require this handle internally — they now use tracker.RecordEventReturningId which bundles the write + id recovery. The handle is still accepted as a parameter by those helpers for API compatibility.
The returned handle MUST be closed by the caller; typically callers cache it for the lifetime of the daemon and Close it at shutdown.
dbPath: the SAME path used to open the TaskTracker. Empty string resolves to DefaultDBPath() (matching openTaskTrackerImpl's behaviour).
Errors are *pasterrors.StructuredError with CategoryConnection (file open failure) or CategoryStorage (PRAGMA failure).
This helper is a thin re-export of openAuditHandle so the rest of the tree (cmd/pastured, hooks/git_recorder.go) doesn't need access to package-private names.
func OpenTaskTracker ¶
func OpenTaskTracker(dbPath string) (protocol.TaskTracker, error)
OpenTaskTracker opens the unified pasture.db SQLite file at dbPath, runs the audit migrator, opens the Provenance tracker on the same file, and returns a wrapped protocol.TaskTracker.
dbPath: filesystem path to the unified database. Empty string resolves to DefaultDBPath (honours $PASTURE_DB_PATH and $XDG_DATA_HOME). Parent directories are created if they do not exist.
Errors are *pasterrors.StructuredError with category:
- CategoryConnection (exit 2): file/dir cannot be opened.
- CategoryStorage (exit 5): migration or DDL failure.
- CategoryValidation (exit 1): newer-schema rejection.
Callers MUST call Close on the returned tracker to release file handles.
This is the in-package entry point. External callers should use protocol.OpenTaskTracker, which is wired through init() above.
func OpenTracker ¶
func OpenTracker(dbPath string) (provenance.Tracker, error)
OpenTracker opens the Provenance tracker at dbPath, creating any missing parent directories along the way. An empty dbPath resolves to DefaultDBPath.
On failure the returned error is a *pasterrors.StructuredError with CategoryConnection, so callers can map it to exit code 2 via pasterrors.ExitCode.
func RecordGitEvent ¶
func RecordGitEvent( ctx context.Context, tracker protocol.TaskTracker, auditDB *sql.DB, sha string, eventType protocol.EventType, payload map[string]any, ) (int64, error)
RecordGitEvent records a free-floating git event (commit, push, rebase) to audit_events and attaches a ContextGit edge keyed by the supplied SHA / ref.
Parameters:
- ctx: context for cancellation; propagates to RecordEventReturningId and AttachContext.
- tracker: the unified TaskTracker (typically obtained from OpenTaskTracker). MUST be non-nil.
- auditDB: retained for API compatibility with existing callers; no longer used by the implementation (RecordEventReturningId bundles write + id recovery atomically, removing the need for a separate SELECT MAX round-trip). MAY be nil without causing a validation error.
- sha: the git commit SHA (or remote ref for pushes / base-ref for rebases). Used as the context_id of the ContextGit edge. MUST be non-empty; an empty sha would create a row no Timeline lookup can match.
- eventType: usually one of EventGitCommit / EventGitPush / EventGitRebase above; callers MAY pass any non-empty string per §7.3 / §7.5 open-string convention.
- payload: free-form key/value pairs. Should include {"sha": sha} for symmetry with the context edge; not enforced.
The recorded AuditEvent's Role field is set to DefaultGitRole — the canonical hook-handler name from PROPOSAL-2 §7.7.2's "pasture/automaton/hook/<name>" pattern. S3's audit-side legacy-role shim resolves this to an agent_id; post-S8 the workflow boundary will supply agent_id directly.
On success returns the int64 audit_events.id of the newly-inserted row so callers can attach further contexts (e.g., a post-epoch commit citing epoch X also gets ContextEpoch — see §11 Scenario 7 multi-context attachment).
Errors are *pasterrors.StructuredError with one of:
- CategoryValidation: nil tracker, empty sha, or an empty eventType (the underlying audit store would silently accept these but they're programming errors here).
- CategoryStorage: RecordEventReturningId / AttachContext write failure (file unwritable, schema drift, etc.). Underlying error wrapped via pasterrors.StructuredError.Why.
func RecordSessionEvent ¶
func RecordSessionEvent( ctx context.Context, tracker protocol.TaskTracker, auditDB *sql.DB, sessionId string, eventType protocol.EventType, payload map[string]any, ) (int64, error)
RecordSessionEvent records a free-floating Claude Code session event to audit_events and attaches a ContextSession edge keyed by the supplied session id.
See RecordGitEvent for the parameter / error contract; only the context kind (ContextSession) and the suggested EventType (EventSessionRecorded) differ.
Note: pasture also has a separate session_entries table (R6 — sub-PROV-O granularity for ACP SessionUpdate streams) reached via tracker.RecordSessionEntries; that path is for storing the streamed session content. RecordSessionEvent here records the higher-level "a session happened" event so it shows up in the audit timeline alongside epoch transitions and git events.
func RecordSkillEvent ¶
func RecordSkillEvent( ctx context.Context, tracker protocol.TaskTracker, auditDB *sql.DB, skillRunId string, eventType protocol.EventType, payload map[string]any, ) (int64, error)
RecordSkillEvent records a free-floating skill-invocation event to audit_events and attaches a ContextSkill edge keyed by the supplied skill run id (e.g., "pasture:user-elicit-<run-id>").
See RecordGitEvent for the parameter / error contract; only the context kind (ContextSkill) and the suggested EventType (EventSkillInvoked) differ.
`skillRunId` is the canonical id of the skill invocation. Per PROPOSAL-2 §7.3 example: "pasture:user-elicit-<run-id>" — the wire string is whatever the caller wants to use for Timeline lookups via `pasture task events --context-kind=SkillContext --context-id=<run-id>`.
func RegisterWellKnownAgents ¶
func RegisterWellKnownAgents(ctx context.Context, tracker protocol.TaskTracker, cache *WellKnownAgentCache) error
RegisterWellKnownAgents mints (or recovers) every entry in WellKnownAgents() against the supplied tracker and populates cache.
Idempotency contract (Scenario 14): two consecutive calls against the same underlying database produce identical row counts in `agents`, `agents_software`, `pasture_well_known_agents`, and `pasture_agent_categories`, AND identical AgentIDs in `pasture_well_known_agents` (pointwise across startups).
tracker MUST implement auditDBHolder (i.e. it must expose an audit *sql.DB handle via auditDBHandle). Both the production *trackerImpl and any test fake that wires up a real *sql.DB satisfy this. Callers that pass a tracker without the method get a CategoryConfig *StructuredError pointing at the wiring error.
cache MUST be non-nil. After successful return, cache contains exactly WellKnownAgentCount entries; on any error, cache may contain a strict subset (entries up to and including the failure point are populated; later entries are not). Partial population is safe for restart recovery — the missing entries get filled in on the next call because of the fast-path on lookup hit.
Errors are *pasterrors.StructuredError categorised as:
- CategoryConfig (exit 4): tracker is nil, does not implement auditDBHolder, or its auditDBHandle() returns nil; cache is nil.
- CategoryStorage (exit 5): SQLite read/write or transaction failure.
- CategoryWorkflow (exit 3): Provenance RegisterSoftwareAgent failure.
The function takes ctx for cancellation; long-running registrations (15 entries × {1 SELECT + maybe 1 RegisterSoftwareAgent + 2 INSERTs}) take well under a second on local SQLite, so context-deadline rejection is primarily about clean shutdown if the daemon is killed during startup.
func ResolveNamespace ¶
ResolveNamespace returns the namespace to use for a `create` operation. Explicit takes precedence; an empty value falls back to DefaultNamespace.
Errors from DefaultNamespace are wrapped as CategoryValidation so that the user is prompted to supply --namespace explicitly.
Types ¶
type WellKnownAgentCache ¶
type WellKnownAgentCache struct {
// contains filtered or unexported fields
}
WellKnownAgentCache holds the well-known-name → AgentId mapping for use by `temporal.Activities` (S8). Construct via NewWellKnownAgentCache; populate via RegisterWellKnownAgents; inject into Activities. Cache lookups (Get / MustGet / Names) are safe for concurrent use after population.
A nil *WellKnownAgentCache is NOT a valid cache: all methods other than Len would panic. RegisterWellKnownAgents asserts the pointer is non-nil before populating.
func NewWellKnownAgentCache ¶
func NewWellKnownAgentCache() *WellKnownAgentCache
NewWellKnownAgentCache returns an empty cache ready for population by RegisterWellKnownAgents. The initial capacity is sized to fit the canonical 15-entry registry without a rehash.
func (*WellKnownAgentCache) Get ¶
func (c *WellKnownAgentCache) Get(name string) (provenance.AgentID, bool)
Get returns the AgentId for name. The boolean is true when name is a known entry, false otherwise. Callers that have a static guarantee that name is in the registry (e.g. a constant from well_known_registry.go) may prefer MustGet; callers reading user-provided strings should prefer this method.
func (*WellKnownAgentCache) Len ¶
func (c *WellKnownAgentCache) Len() int
Len returns the number of entries in the cache. A nil receiver returns 0 so the daemon can safely log `cache.Len()` before construction completes (defensive for failure paths in main.go that may have a nil cache pointer).
func (*WellKnownAgentCache) MustGet ¶
func (c *WellKnownAgentCache) MustGet(name string) (provenance.AgentID, error)
MustGet returns the AgentId for name or returns a *StructuredError of CategoryValidation if name is not in the cache. Use from activity hot paths where the caller statically knows which well-known name it wants (e.g. CheckConstraints always wants check-constraints) — a missing entry then indicates a wiring bug (the cache was not populated, or the registry was edited without re-registering).
Returns the actionable error rather than panicking so the activity can surface it through Temporal's error channel without crashing the worker.
func (*WellKnownAgentCache) Names ¶
func (c *WellKnownAgentCache) Names() []string
Names returns the sorted slice of all well-known names currently in the cache. Sorted output makes test assertions stable (no map iteration nondeterminism) and supports diff-friendly debug logging. The returned slice is a fresh copy; callers may mutate it without affecting the cache.
type WellKnownAgentSpec ¶
type WellKnownAgentSpec struct {
// Name is the stable logical name. It survives across restarts and is
// minted exactly once per database (idempotency anchor on the UNIQUE
// constraint over `pasture_well_known_agents.name`). Convention:
// `pasture/automaton/<role-kebab>` (or `pasture/automaton/hook/<HookName>`).
Name string
// Role is the AutomatonRole this agent fulfils. MUST be one of
// `protocol.AllAutomatonRoles` minus `AutomatonRoleNone` (every
// concrete automaton has a non-None role). Validated at registration
// time by ensureWellKnownAgent via Role.IsValid().
Role protocol.AutomatonRole
}
WellKnownAgentSpec pairs a stable logical name (the PK in `pasture_well_known_agents`) with its `protocol.AutomatonRole` (the value stored in `pasture_agent_categories.automaton_role`). The PastureRole for every well-known automaton is `None` (per §7.7.2 — these are software automata, not workflow agents).
func WellKnownAgents ¶
func WellKnownAgents() []WellKnownAgentSpec
WellKnownAgents returns the canonical 15-entry slice of well-known agents in PROPOSAL-2 §7.7.2 row order. The returned slice is a fresh copy; callers may not assume it shares storage with package internals (this keeps the canonical list immutable from outside the package).
Order:
0: ConstraintChecker — pasture/automaton/check-constraints
1-3: TransitionGate — pasture/automaton/transition-gate/{consensus,vote-threshold,exit-condition}
4-12: HookHandler — pasture/automaton/hook/<HookName> × 9
13: ConsensusReached — pasture/automaton/consensus-reached
14: CreateFollowup — pasture/automaton/create-followup
Total: 15. Length-checked by the WellKnownAgentCount constant below.