Documentation
¶
Overview ¶
Package session implements Båge's FILE-LEG two-phase, region-anchored, concurrency-safe edit protocol (SPEC §8, ADR-0003).
Prepare is OPTIMISTIC: it holds no lock, reads each live file, resolves every region-anchored edit against those bytes (rejecting a Conflict/Ambiguous with a typed error), preview-splices, formats, lints, and reparses to prove the result is valid, then durably records a wal.Intent. Nothing is written to a source file during Prepare — its sole on-disk effect is the WAL record.
Commit is the ATOMIC, lossless point. Per file, UNDER A PER-FILE LOCK, it RE-READS the live bytes and RE-RESOLVES every edit (resolve-under-lock, so a concurrent commit that benignly shifted a region is picked up and the edit lands at the current offset, never the stale one), splices, atomic-writes, reparses, and computes a region.EditResult. A region whose region_hash no longer matches any live node is a *ConflictError and that file is not written. Same-file commits serialize on one lock; cross-file commits take different locks and run in parallel.
The cross-store graph+file coordinator lives in HYLLA per ADR-0001; this package owns only the FILE leg.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrConflict = errors.New("session: edit conflict")
ErrConflict is the sentinel wrapped by every ConflictError, so callers can match a region conflict with errors.Is without inspecting the path.
Functions ¶
This section is empty.
Types ¶
type ConflictError ¶
type ConflictError struct {
// Path is the file whose region could not be resolved.
Path string
// Reason is the resolve status that triggered the conflict ("conflict" or
// "ambiguous"), or a wrapped resolver error message.
Reason string
}
ConflictError reports that a region-anchored edit could not be resolved against the live file: the target's region_hash no longer matches any live node (a concurrent edit changed the same region), or it matches more than one (ambiguous twins). Either case is a hard reject — Båge never guesses and never misapplies (SPEC §8.3, §8.4, ADR-0003). The offending file is left untouched.
func (*ConflictError) Error ¶
func (e *ConflictError) Error() string
Error implements error with a path-and-reason-qualified message.
func (*ConflictError) Unwrap ¶
func (e *ConflictError) Unwrap() error
Unwrap returns ErrConflict so errors.Is(err, ErrConflict) matches a ConflictError.
type Plan ¶
type Plan struct {
// Intent is the WAL intent recorded by Prepare (already persisted): originals
// for restore-on-failure plus the expected per-file hashes.
Intent wal.Intent
// Edits are the region-anchored edits this plan will apply, in input order.
Edits []region.Edit
// Anchors maps each file path to the per-file drift gate it was prepared
// against (SPEC §8.1).
Anchors map[string]region.FileAnchor
}
Plan is the result of a successful Prepare. It records the region edits and the per-file anchors so Commit can re-validate under lock — Prepare is optimistic, Commit is the atomic point (SPEC §8).
type Session ¶
type Session struct {
// Parser reparses live and staged bytes (drift relocation + parse assertion).
Parser parser.ParserPort
// Hasher computes the raw and normalized hashes recorded into the WAL intent
// and the post-edit hashes returned in each EditResult.
Hasher hashing.Hasher
// Formatter, when non-nil, rewrites staged bytes before linting/parsing.
Formatter format.Formatter
// Linter, when non-nil, validates staged bytes; a lint failure blocks Prepare.
Linter format.Linter
// Lang, when set (non-LangUnknown), forces that language for every file;
// when LangUnknown (the zero value) each file's language is auto-detected
// from its path via parser.LangForPath. It is an optional per-session
// override, letting an agent IDE edit a mixed-language tree without a single
// session-wide language.
Lang parser.Lang
// WALDir is the directory holding this session's write-ahead log.
WALDir string
// contains filtered or unexported fields
}
Session is the configured FILE-LEG edit engine. Formatter and Linter may be nil to skip the corresponding step; Parser, Hasher, and WALDir are required. Lang is an OPTIONAL per-session override (see the field). A single Session is safe for concurrent Prepare/Commit calls: the per-file lock map serializes writers to the same file while letting different files proceed in parallel (SPEC §8.3).
func (*Session) Commit ¶
func (s *Session) Commit(plan *Plan) ([]region.EditResult, error)
Commit is the atomic, lossless point. Per file, under that file's lock, it RE-READS the live bytes, RE-RESOLVES every edit against the current content (resolve-under-lock — this picks up concurrent commits, so a benignly shifted region lands at its current offset and a same-region conflict is rejected), splices the resolved ranges, atomic-writes, reparses, and computes an EditResult. A *ConflictError on any file aborts the Commit: every file already written in this Commit is restored from its original, and the WAL is preserved for Recover. On full success the WAL is cleared. Different files take different locks, so cross-file commits run in parallel; same-file commits serialize.
func (*Session) Prepare ¶
func (s *Session) Prepare(ctx context.Context, edits []region.Edit, anchors []region.FileAnchor) (*Plan, error)
Prepare optimistically stages every region-anchored edit and records one WAL intent. It holds no lock. For each file it reads the live bytes, resolves every edit via region.Resolve (a Conflict/Ambiguous resolve yields a *ConflictError and nothing is committed), preview-splices the resolved ranges, runs the Formatter then the Linter (a lint failure rejects), and reparses to assert the result still parses (a parse failure rejects). The originals and expected hashes are recorded into a wal.Intent persisted via wal.Append. On success it returns a Plan; the only on-disk effect is the WAL record — no source file is written.
func (*Session) Recover ¶
Recover is the restart path: it replays the WAL in dir and, for each intent, restores every affected file from Intent.Originals via atomicwrite.Write, converging the files back to their pre-Prepare state (files-as-truth, SPEC §1.2 converge-on-crash). It then clears the WAL. A crash between Prepare and Commit leaves a WAL record whose Originals undo the half-done edit; a crash after Commit cleared the WAL leaves nothing to replay.