Documentation
¶
Overview ¶
Package agentslock is the single shared writer for .agentsrc.lock — the resolved-state companion to .agentsrc.json (config-distribution-model §7).
It is schema-agnostic: it owns the whole JSON document and treats top-level sections (config, packages, adapters, …) as opaque values, so the config/ package resolver and the graph-adapter lifecycle share one file without either importing the other's schema (§7.4). A writer stages only its own section and flushes; sibling sections are preserved verbatim. Flush is atomic (temp file + rename, via fsops.WriteFileAtomic). A single Lockfile is safe for concurrent SetSection from parallel resolver goroutines. Flush also takes a portable sidecar-directory lock, rereads the latest on-disk document, and reapplies only this process's staged top-level keys before the atomic write. That keeps sibling sections written by another process from being lost while preserving the §7.4 "parallel resolution, serialized write" contract.
Index ¶
Constants ¶
const LockVersion = 1
LockVersion is the current .agentsrc.lock schema version.
Variables ¶
This section is empty.
Functions ¶
func AcquireFileLock ¶ added in v0.4.2
AcquireFileLock takes the package's advisory, inter-process lock guarding the file at path and returns a release function the caller MUST invoke to free it. It is the reusable form of the primitive that serializes .agentsrc.lock writes (see Flush); other cooperating da processes (e.g. an append-only NDJSON writer) call it to serialize their own writes to a shared file.
The lock is a sidecar directory at "<path>.lock". Acquisition is a single atomic os.Mkdir: success is the mutual-exclusion signal, and EEXIST means another holder currently owns it. The lock is therefore ADVISORY — it excludes only other callers of this function that name the same path, not arbitrary writers of the underlying file. The parent directory of path is created if it does not yet exist.
Acquisition blocks up to lockAcquireTimeout, retrying every lockRetryInterval, and returns a timeout error if a live holder never releases. Because the mkdir lock has no kernel-backed auto-release, a holder that crashed without releasing (SIGKILL/OOM/power loss) is detected as stale once its recorded age exceeds lockStaleTTL and is reclaimed at most once per call — so a slow but live holder is never torn down out from under itself. The returned release removes the sidecar directory exactly once and reports any removal error; it is once-guarded (a second call returns the first call's cached error without touching the filesystem).
The once-guard is a correctness requirement, not a convenience: an unguarded RemoveAll on every call would, after this caller released and another caller re-acquired the same path, delete the new holder's live lock dir on a stray second release — silently breaking its mutual exclusion. Removing at most once guarantees a duplicate release can never delete a dir this caller no longer owns.
Types ¶
type Lockfile ¶
type Lockfile struct {
// contains filtered or unexported fields
}
Lockfile is the in-memory view of a .agentsrc.lock document: open it, read or stage sections, then Flush. Safe for concurrent use.
func Open ¶
Open loads the lockfile at path. A missing file yields a fresh document (lock_version only); a present file is parsed, preserving every top-level key — including sections this process does not know about.
func (*Lockfile) Flush ¶
Flush writes the whole document to path atomically, preserving every section. It is callable more than once (e.g. persist config before a slow adapter activation, then flush adapters after). The parent directory must exist.
func (*Lockfile) InputsDigest ¶
InputsDigest returns the top-level inputs_digest and whether it was present. An absent or empty field reports ("", false).
func (*Lockfile) Section ¶
Section decodes the named section into v and reports whether it was present. An absent section returns (false, nil) so callers can treat "no section yet" and "section exists" uniformly.
func (*Lockfile) SetInputsDigest ¶
SetInputsDigest stages the top-level inputs_digest field (§7A.3): the whole-normalized hash of all local config scopes that drives staleness. An empty digest clears the field. Safe for concurrent use.