Documentation
¶
Overview ¶
Package settings reads and edits the focused Seamark settings in two documents. .seamark/config.yaml holds the inference preset, the pin-write gate, the review window, and the indexing options. Three packages read that one file, each its own section. .seamark/lessons.yaml holds the lesson delivery settings and the mutes; the pins in it belong to the pin editors (internal/pinfile). This package therefore never re-encodes a document. It splices the text of one field group, verifies the result with every reader, and writes it through the guarded setup engine of internal/integration.
Inspect reads only. PlanPatch computes a guarded diff and writes nothing. Save writes exactly the planned bytes, or refuses with ErrStale when the file changed after it was inspected. After a failed write, Save reads the file again and reports the outcome.
A save changes only the lines of one field group in one document. It keeps comments, unknown keys, other sections and the file mode, and it replaces the file atomically. A missing file is created with the planned keys only. A file that the splice cannot change safely returns a BlockedError that names the line to fix by hand: malformed or ambiguous YAML (for example duplicate keys or aliases), a symbolic link, a read-only file, or a file that this user cannot write. The two documents are independent: a problem in one never blocks a group of the other. The activation texts of the plan say when each field takes effect; docs/configuration.md lists them for the user.
Index ¶
- Constants
- Variables
- func CheckNumber(f Field, n int) error
- func MuteText(rule, region string) string
- func OverlapNote(e MuteEntry) string
- type Activation
- type BlockedError
- type Edit
- type Field
- type FieldState
- type Group
- type LessonsPart
- func (lp *LessonsPart) Changeable(ref MuteRef) error
- func (lp *LessonsPart) Duplicate(rule, region string, skip int) (MuteEntry, bool)
- func (lp *LessonsPart) Number(f Field) (int, bool)
- func (lp *LessonsPart) Overlaps(rule, region string, skip int) []MuteEntry
- func (lp *LessonsPart) Target(ref MuteRef) (MuteEntry, error)
- type MuteEdit
- type MuteEntry
- type MuteInput
- type MuteOp
- type MuteRef
- type Outcome
- type Plan
- type PlanBytes
- type Result
- type Snapshot
- type Source
- type Stored
- type Writer
Constants ¶
const ( ReaderAgent = "agent" ReaderDistill = "distill" ReaderIndex = "index" ReaderLessons = "lessons" )
Reader names, the keys of Snapshot.ReaderErrs. The index reader also owns reviews.window_days. The lessons reader (reviews.ParseConfig) reads lessons.yaml; the other three read config.yaml.
const File = ".seamark/config.yaml"
File is the shared configuration document, relative to the root.
const PinWriteScope = "apply, retarget, prune, and new pins, pin edits, and pin removals in Lessons › Pins"
PinWriteScope names every write that distill.write governs (spec R-5). The proposal apply, the Pins retarget and prune, and the pin authoring of Lessons › Pins read the same gate. Every text that says what the gate governs uses this phrase, so no text names a part of the scope.
Variables ¶
var ErrBlocked = errors.New("configuration cannot be edited safely")
ErrBlocked reports a file that cannot be edited safely. Every BlockedError matches it with errors.Is.
var ErrEmptyMute = errors.New("a mute needs a rule, a region, or both")
ErrEmptyMute reports a mute without a rule and without a region. The matcher reads such an entry as matching nothing, so the editor never writes one.
var ErrInvalidEdit = errors.New("invalid settings edit")
ErrInvalidEdit reports an edit that the settings form must not send: an unknown preset, a negative window, a bad exclude pattern, or edits from two groups.
var ErrStale = errors.New("configuration changed since it was inspected")
ErrStale reports a file that changed after it was inspected or planned. The caller inspects again; a silent re-plan could save an intent the user never saw.
Functions ¶
func CheckNumber ¶
CheckNumber checks a typed value of threshold, pin_budget, or change_budget. The planner and the number editor of the console both call it, so the editor shows the verdict of the save. The value must be from 1 to math.MaxInt32. The upper bound keeps the value an int on every platform and in every reader. A positive value is never replaced by a default, so the editor writes no value that the reader ignores.
func MuteText ¶
MuteText describes a mute for display, as "rule F541 · region api". The values are file text, so the description is sanitized.
func OverlapNote ¶
OverlapNote is the review note about an entry e that hides a lesson that the planned mute also hides. A form shows the same note before the review, so the form and the review use the same words.
Types ¶
type Activation ¶
type Activation struct {
Field Field
// When names the action that first uses the new value.
When string
// Notes explain the effect and its limits.
Notes []string
}
Activation says when a saved field takes effect, and what else the user must know about it.
type BlockedError ¶
type BlockedError struct {
Reason string
// Line is the 1-based line in the file, or 0 when unknown.
Line int
// File is the document to edit by hand, relative to the root. Empty
// means config.yaml, the only document of the callers before the
// lessons document.
File string
}
BlockedError explains why an edit is unsafe and names the line to open, so the UI can offer a manual route.
func (*BlockedError) Error ¶
func (e *BlockedError) Error() string
Error names the reason, the line, and the file.
func (*BlockedError) Is ¶
func (e *BlockedError) Is(target error) bool
Is makes every BlockedError match ErrBlocked.
type Edit ¶
type Edit struct {
Field Field
// Reset removes the key from the file.
Reset bool
// String is the agent.cli preset (a registered invocation ID) or the
// hook_delivery mode.
String string
// Bool is the value of distill.write or index.generated.
Bool bool
// Days is a positive reviews.window_days value.
Days int
// Unlimited sets reviews.window_days to 0, the reader's unlimited.
Unlimited bool
// List is the index.exclude patterns. An empty list is a reset.
List []string
// Int is the threshold, pin_budget, or change_budget value, from 1
// to math.MaxInt32. The editor writes no value that the reader
// replaces with its default.
Int int
// Mute is the one mute operation of a mute field edit.
Mute *MuteEdit
}
Edit is one field change of a draft. Reset removes the key, so the reader default applies; it never writes today's default value. The value fields that apply depend on Field.
type Field ¶
type Field string
Field names one supported setting by its YAML path. A config.yaml field is a "section.key" path. A lessons.yaml field is a top-level key.
const ( AgentCLI Field = "agent.cli" AgentArgv Field = "agent.argv" DistillWrite Field = "distill.write" ReviewWindow Field = "reviews.window_days" IndexGenerated Field = "index.generated" IndexExclude Field = "index.exclude" )
The supported fields of config.yaml. agent.argv is read-only: the only edit is its removal, because authoring a custom command is out of scope.
const ( Threshold Field = "threshold" PinBudget Field = "pin_budget" ChangeBudget Field = "change_budget" HookDelivery Field = "hook_delivery" Mutes Field = "mute" )
The supported fields of lessons.yaml. Each is a top-level key of reviews.Config. Mutes is the mute list; a plan changes one entry of it. The pin list is not a field: the pin editors own it.
func (Field) Activation ¶
func (f Field) Activation() Activation
Activation returns when a saved value of the field takes effect and what else the user must know, without the notes that depend on one snapshot. The Settings detail shows it before a draft exists, and every plan starts from it.
func (Field) Document ¶
Document returns the file that holds the field, relative to the root: File or pinfile.File. An unknown field returns "".
type FieldState ¶
type FieldState struct {
Field Field
Stored Stored
// Default and Effective are display text. They come from the owning
// reader, so the UI never computes a default itself.
Default string
Effective string
Source Source
// Notes explain the effective value, for example an override.
Notes []string
// Err is set when the owning reader or the invocation resolver
// fails. The value is then unknown, not the default.
Err error
}
FieldState is the stored, default, and effective value of one field.
type Group ¶
type Group int
Group is one settings form. A plan edits exactly one group, so enabling pin writes is always a save of its own (spec R-12).
const ( // GroupInference edits agent.cli and removes agent.argv. GroupInference Group = iota // GroupPinWrite edits distill.write only. GroupPinWrite // GroupEvidence edits reviews.window_days. GroupEvidence // GroupIndexing edits index.generated and index.exclude. GroupIndexing // GroupDelivery edits the four delivery keys of lessons.yaml. GroupDelivery // GroupMutes adds, changes, or removes one mute entry of lessons.yaml. GroupMutes )
The settings groups.
func (Group) Document ¶
Document returns the file that the group edits, relative to the root. The lessons groups edit pinfile.File, the one name of lessons.yaml. An unknown group returns "".
type LessonsPart ¶
type LessonsPart struct {
// Path is the absolute file under the inspected root. The caller
// passes the effective root, so this is the file that Pins lists.
Path string
// Guard is the observation that every lessons plan binds to.
Guard integration.FileGuard
// Exists is true when the file exists.
Exists bool
// Blocked holds the reason that stops a save of every lessons group:
// the file is unreadable, linked, hard-linked, or not writable for
// this user. Line breaks other than LF (CRLF, bare CR, mixed) and a
// failed structural gate also block every lessons group. A save of
// either group replaces the whole file, so a file problem blocks both.
// BlockedAt is the 1-based line to open, or 0.
Blocked error
BlockedAt int
// ReaderErr is the failure of reviews.ParseConfig on the file bytes,
// the reader of every lesson consumer. The editor never repairs a
// file that the reader rejects, so it blocks both lessons groups.
ReaderErr error
// Config is the decoded file; nil when the reader fails.
Config *reviews.Config
// Mutes lists every item of the mute list in file order, null items
// included. An item of a flow list has no line span, so its RawSHA is
// zero; every mute operation refuses a flow list.
Mutes []MuteEntry
// contains filtered or unexported fields
}
LessonsPart is one read-only observation of lessons.yaml.
func (*LessonsPart) Changeable ¶
func (lp *LessonsPart) Changeable(ref MuteRef) error
Changeable returns nil when the planner can change the entry of ref, or its refusal. A form asks it before it opens. So a form never offers a change that the planner refuses.
func (*LessonsPart) Duplicate ¶
func (lp *LessonsPart) Duplicate(rule, region string, skip int) (MuteEntry, bool)
Duplicate returns the entry, other than the entry at index skip, that holds the same mute as rule and region, as the planner refuses it. A form calls it to show the refusal before the review. Use -1 as skip for a new entry.
func (*LessonsPart) Number ¶
func (lp *LessonsPart) Number(f Field) (int, bool)
Number returns the stored number of threshold, pin_budget, or change_budget that the reader uses. It is false when the key is absent or null, or when the node is not a number. It is also false when the reader uses its default in place of a stored 0 or a negative value. The console marks its number choice with it, because Config.Threshold already holds the default in place of a nonpositive stored value.
func (*LessonsPart) Overlaps ¶
func (lp *LessonsPart) Overlaps(rule, region string, skip int) []MuteEntry
Overlaps returns the entries, other than the entry at index skip, that hide a lesson that a mute of rule and region also hides. For example, "api" and "api/" overlap, and so do "W1 in api" and "api/". A rule-only mute and a region-only mute are not reported. Almost every such pair hides a common lesson, so a note about it would say nothing.
func (*LessonsPart) Target ¶
func (lp *LessonsPart) Target(ref MuteRef) (MuteEntry, error)
Target returns the entry that ref names, or the refusal of the planner. The plan bytes are the snapshot bytes, so a hash that differs means that the draft started on another version of the file. A form asks it too, so it names a stale draft before the review.
type MuteEdit ¶
type MuteEdit struct {
Op MuteOp
Ref MuteRef
Rule string
// Region is the typed region; CheckMute normalizes it.
Region string
}
MuteEdit is one mute operation. Rule and Region are the typed values of an add or a change; an empty value has no key. A removal takes only Ref.
type MuteEntry ¶
type MuteEntry struct {
// Index is the position of the item in the YAML sequence. A null
// item counts, so Index can differ from the index in
// reviews.Config.Mute, which drops null items.
Index int
// Line is the 1-based dash line of the item.
Line int
// Rule and Region are the decoded values; "" when absent.
Rule, Region string
// RawSHA is the SHA-256 of the item lines, from the dash line to the
// last line of the item. A MuteRef binds an edit to this text.
RawSHA [32]byte
// NoOp is true when the entry matches nothing: it has neither a rule
// nor a region, as a null item or "- {}".
NoOp bool
// DuplicateOf is the Index of an earlier entry with the same rule
// (any case) and exactly the same region, or -1.
DuplicateOf int
// Flow is true for an item in flow style, such as "- {rule: a}", and
// for each item of a flow list. A change refuses a flow item; a
// removal works in a block list.
Flow bool
// Extra lists the keys of the item that the reader does not know, in
// file order. A change keeps them.
Extra []string
}
MuteEntry is one item of the mute list. Rule and Region hold the decoded values as the reader decodes them. They are file text: a view sanitizes them before display.
type MuteInput ¶
MuteInput is a mute that a person typed, after CheckMute. Region is the region to store. Notes name each change from the typed region.
func CheckMute ¶
CheckMute checks a mute that a person typed. The record form and the planner both call it, so the form shows the verdict of the save. The rule must pass reviews.CheckAuthoredText; the region, when typed, must pass reviews.CheckMuteRegion; one of the two must be set. The errors wrap reviews.ErrAuthoredText, reviews.ErrRegionInput, or ErrEmptyMute, so a form can show each next to its field.
type MuteOp ¶
type MuteOp int
MuteOp names one mute operation.
const ( // MuteAdd appends one entry after the last entry. MuteAdd MuteOp = iota + 1 // MuteChange changes the rule and the region of one entry. Unknown // keys and comments of the entry stay. MuteChange // MuteRemove removes one entry. MuteRemove )
The mute operations. The zero value is no operation, so a mute edit without an operation is invalid.
type MuteRef ¶
MuteRef names one entry of the mute list: its YAML item index and the hash of its text when the draft started (MuteEntry.RawSHA). A plan refuses a reference whose entry text changed.
type Outcome ¶
type Outcome int
Outcome says what the file holds after a save.
const ( // NotWritten means that the file does not hold the planned bytes: // the save refused before the write, or the write failed and the // file holds other bytes. NotWritten Outcome = iota // Written means that the file holds the planned bytes. Written // Unknown means that the write failed and the file cannot be read // again. The caller must not report "written" or "not written". Unknown )
The save outcomes (P4-18). The zero value is NotWritten, so a result of a refused save reads as "nothing written".
type Plan ¶
type Plan struct {
// Snapshot is the observation that the plan starts from.
Snapshot *Snapshot
Group Group
Edits []Edit
// File is the edited document relative to the root: File or
// pinfile.File, as Group.Document returns it.
File string
// Bytes holds the reviewed and the planned file bytes of a lessons
// plan, a no-op included; zero for a config.yaml plan. After equals
// the bytes of the guarded write. A selection sample reads them.
Bytes PlanBytes
// Setup is the guarded write plan; nil for a no-op.
Setup *integration.SetupPlan
// Preview is the redacted file diff; zero for a no-op.
Preview integration.WritePreview
// Before and After are the states of the group's fields, from the
// owning readers.
Before, After []FieldState
// Activation holds one entry per edited field.
Activation []Activation
// Notes apply to the save as a whole.
Notes []string
// NoOp is true when the edits leave the file bytes unchanged.
NoOp bool
}
Plan is a validated, guarded patch of one settings group. It holds the exact bytes to write; nothing is written yet.
type PlanBytes ¶
type PlanBytes struct {
Before, After []byte
}
PlanBytes are the file bytes before and after a plan.
type Result ¶
type Result struct {
// Outcome says what the file holds after the save.
Outcome Outcome
// Written is true when the file holds the planned bytes: Outcome is
// Written.
Written bool
// Created is true when the file did not exist before the save.
Created bool
// Path is the absolute path of the file.
Path string
// Err is the cause of a failed save. Outcome says what the file
// holds then.
Err error
}
Result reports one save.
func SaveWith ¶
SaveWith writes exactly the planned bytes through write, or returns ErrStale without a write. The setup engine checks the guard again before the write. It refuses a read-only file and keeps the file mode. It replaces the file by rename, so a failed write leaves the old content complete. A no-op plan writes nothing, but it also returns ErrStale when the file changed after the plan.
A refusal before the write is NotWritten. After an error of the write step, the save reads the file again (integration.Landed). It then reports Written, NotWritten, or Unknown, because the error alone does not prove the file state. The error stays in Result.Err in every case.
type Snapshot ¶
type Snapshot struct {
// Root is the absolute workspace root; Path is the absolute
// config.yaml file.
Root, Path string
// Guard is the observation that every config.yaml plan binds to.
Guard integration.FileGuard
// Blocked is set when no config.yaml group can be saved: the file is
// unreadable, linked, read-only, or structurally unsafe to edit.
// BlockedAt is the 1-based line to open, or 0. It never blocks a
// lessons group.
Blocked error
BlockedAt int
// ReadErr is the failure of reading config.yaml itself, before any
// reader runs; nil when the read worked or the file is absent. A
// reader that rejects the text sets ReaderErrs, not ReadErr. The
// console checks its reload after a save with it, because a reader
// error of another section does not mean that the read failed.
ReadErr error
// Fields holds every supported field of both documents in display
// order.
Fields []FieldState
// Presets lists the valid agent.cli values in registry order.
Presets []string
// Invocation is the resolved agent command; zero with InvocationErr.
Invocation agent.CommandSpec
InvocationErr error
// ReaderErrs holds each reader's failure, keyed by ReaderAgent,
// ReaderDistill, ReaderIndex, or ReaderLessons. A missing key means
// success. The ReaderLessons entry is Lessons.ReaderErr, so a lookup
// by Field.Reader works for every field.
ReaderErrs map[string]error
// Lessons is the observation of lessons.yaml. Inspect always sets it.
Lessons *LessonsPart
// Agent, Distill, and Index are the decoded sections; nil when the
// reader failed. The UI seeds its drafts from them.
Agent *agent.Config
Distill *distill.Config
Index *index.Config
// InspectedAt is the time of the observation.
InspectedAt time.Time
// contains filtered or unexported fields
}
Snapshot is one read-only observation of the two settings files. The fields without a document name describe config.yaml; Lessons describes lessons.yaml.
func Inspect ¶
func Inspect(root string, reg *integration.Registry) (*Snapshot, error)
Inspect reads the settings file under root. It runs no agent, no git, and no network call, and it writes nothing. A reader failure or an unsafe file is part of the snapshot, not an error: the UI shows every field it can. The error is for invalid arguments only.
func (*Snapshot) Editable ¶
Editable returns nil when the group can be planned, or the reason it cannot. The reason is the block of the group's document, a problem in the group's section, or a failing owning reader. The UI shows the reason before a draft starts. A group checks only its own document, so a problem in one file never blocks a group of the other.
type Stored ¶
type Stored struct {
// Present is true when the key exists in the file.
Present bool
// Raw is the sanitized source text of the value, for display.
Raw string
// Line is the 1-based line of the key; 0 when absent.
Line int
}
Stored is the raw state of one key in the file.
type Writer ¶
type Writer func(*integration.SetupPlan, integration.ApplyOptions) (integration.SetupResult, error)
Writer applies a guarded setup plan. integration.ApplySetup is the writer of Save; a test injects a writer that fails at a chosen step.