Documentation
¶
Overview ¶
Package services is the typed engine adapter of the seamark console (`seamark tui`). It binds the shared engines to one resolved workspace and returns plain values that a screen can show. It holds no screen, no drawing and no console state.
Surface ¶
All is the whole adapter, and it embeds one interface per area: Reader, Setup, Decisions, Learning, Maintenance and Settings. A worker of the console takes only the area that it needs, so its signature shows which engine calls it can make. New builds the production adapter. NewWithDeps builds it with other boundaries (Deps): the GitHub fetch, the agent invoker and the clock. The console tests that drive a screen over the real engine use it. IndexView, PolicySummary, SetupPreview, IndexEvent and SampleView are the view types that the areas return. SampleRequest and SampleSurface are the input of Sample. ErrBusy, ErrNoLedger and ErrForeignRoot are the three errors that a screen tells apart. The engine and its helpers stay unexported, so a screen cannot reach past the interfaces.
Roots ¶
The effective root R is the root that a readable index records, else the workspace root. The Pins read, the pin authoring, both settings documents, the ledger and the sample use R. So every editor of .seamark/lessons.yaml sees the file that Pins lists. When R is not the workspace root, ErrForeignRoot applies. A pin authoring plan names it as a blocker, and an authoring commit refuses it. The Settings snapshot blocks the lessons groups with it, and a lessons save refuses it. A sample is then unavailable. These Phase 4 rules have no --db exception. A maintenance commit refuses a foreign root only with the default database path, as in Phase 3.
Sample ¶
Sample runs the lesson selection of the edit hook or of change_set for explicit paths, under the saved lessons.yaml text and the candidate of one review. It reads the evidence once with report.ReadEvidence and selects with the production folds, so its current side equals the production selection. The view also names the threshold of each side, because an exclusion only says that a lesson is below it. It needs a ready index of this workspace; every other state is Unavailable in words, never an empty selection. It never calls delivery.Deliver, report.ChangeSet, report.CheckAdvisory, a firing record or a delivery lease. It never opens the index for a write, and it never builds the index.
Imports ¶
services imports the engines (workspace, index, store, distill, learning, maintain, proposals, settings, lessonstats, integration, doctor, status, scaffold, agent, reviews, gate, pinfile, report and delivery). report and delivery hold the selection and the hook path rule of the sample. It never imports Bubble Tea, Lip Gloss, Bubbles, internal/tui or internal/tui/ui. An adapter that knows the screen would let a screen rule leak into an engine call. TestServicesStayBelowTheConsole checks this.
Writes ¶
A read method never writes. A mutation takes the engine lock with TryLock, so a second mutation fails with ErrBusy and never waits behind a long run. Every write goes through an engine, and the adapter adds no write path of its own. A pin authoring commit and a lessons save write .seamark/lessons.yaml only, and never the ledger.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrBusy = errors.New("another operation is running")
ErrBusy reports a second mutation while one runs. The console runs one mutation at a time and never queues a duplicate submission.
var ErrForeignRoot = errors.New("the index records another repository root")
ErrForeignRoot reports an index that records another repository root than the workspace, as in a copied checkout or a --db of another repository. A pin authoring commit and a lessons save refuse it, and a selection sample is then unavailable. A maintenance commit refuses it only with the default database path.
var ErrNoLedger = errors.New("no readable index database: proposals live in the index")
ErrNoLedger reports a ledger read without a readable index database.
Functions ¶
This section is empty.
Types ¶
type All ¶
All is the console's view of the shared engines. Every method is synchronous; the shell calls them from background commands, never from Update or View. Read methods never write.
All embeds one interface per area, and each area matches the screens that use it. A worker that needs one area takes only that area, so its signature shows which engine calls it can make. The split stops at the workers. The shared state keeps the whole All, because the shell loads sources from several areas. Every screen holds that state, so the compiler does not limit a screen to its own area. A screen narrows the value itself before it starts a worker, for example var svc services.Learning = d.st.svc.
func New ¶
New binds the shared engines to one resolved workspace. binary is the seamark path that hook commands use.
func NewWithDeps ¶
NewWithDeps binds the shared engines to one resolved workspace with the boundaries of deps. The console tests that drive a screen over the real engine use it, so they replace only the GitHub fetch, the agent and the clock.
type Decisions ¶
type Decisions interface {
// PreviewDecision describes a proposal decision. It writes nothing.
PreviewDecision(action proposals.Action, ids []int64) (*proposals.Preview, error)
// CommitDecision revalidates and applies a reviewed decision.
CommitDecision(p *proposals.Preview) (proposals.Result, error)
}
Decisions covers the proposal decisions: a preview, then a commit of the reviewed preview. Lessons › Proposals uses it.
type Deps ¶
type Deps struct {
// Fetch reads the GitHub review comments; nil runs gh.
Fetch reviews.FetchFunc
// Invoke sends the distillation and the trigger extraction prompts;
// nil runs the command of the reviewed preview.
Invoke agent.Invoker
// Now reads the clock; nil reads the system clock.
Now func() time.Time
}
Deps are the three external boundaries of the engine: the GitHub fetch, the agent invoker and the clock. Tests replace them, so no engine test contacts GitHub or runs a paid agent, and every time is fixed. A zero field selects the real boundary: gh, the command of the reviewed preview, and time.Now. Deps holds nothing else, because every other input of the engine is a file or the store.
type IndexEvent ¶
type IndexEvent struct {
Phase string
Done int
Total int
// Log is a warning or note line; Phase is then empty.
Log string
}
IndexEvent is one progress event of an index build. Phase is one of scan, parse, history, write; Total 0 means the phase has no count.
type IndexView ¶
type IndexView struct {
Probe workspace.IndexProbe
// Status is nil unless Probe.State is IndexReady and Gather worked.
Status *status.Status
// StatusErr holds the semantic health read failure.
StatusErr error
// Policy summarizes the gate policy. It does not need the index, so
// it is filled in every index state.
Policy PolicySummary
}
IndexView is the index section of the Overview: the probe, and the semantic health when the index is ready.
type Learning ¶
type Learning interface {
// Learning reads the stored evidence, the review target, and the
// review window. It runs local git only; it never fetches or writes.
Learning() learning.Snapshot
// PlanEvidence previews an evidence refresh in mode. It fetches
// nothing and writes nothing.
PlanEvidence(mode index.SourceMode) (*learning.Plan, error)
// RunEvidence runs exactly the reviewed plan. A stop through ctx
// ends the GitHub fetch; progress runs on the worker goroutine.
RunEvidence(ctx context.Context, p *learning.Plan, progress func(index.RefreshEvent)) (learning.Outcome, error)
// PlanDistill previews a distillation. It never runs the agent and
// never writes.
PlanDistill(req learning.DistillRequest) (*learning.DistillPreview, error)
// RunDistill runs exactly the reviewed preview. A stop through ctx
// starts no new group; progress runs on the worker goroutine.
RunDistill(ctx context.Context, p *learning.DistillPreview, progress func(distill.Event)) (*distill.Execution, error)
// CleanupPending removes the stale pending proposals of a
// cleanup-only preview. It runs no agent.
CleanupPending(p *learning.DistillPreview) (int, error)
// PlanExtract previews a trigger extraction of the selected
// proposals. It never runs the agent and never writes.
PlanExtract(req learning.ExtractRequest) (*learning.ExtractPreview, error)
// RunExtract runs exactly the reviewed preview. A stop through ctx
// starts no new batch; progress runs on the worker goroutine.
RunExtract(ctx context.Context, p *learning.ExtractPreview,
progress func(distill.ExtractEvent)) (*distill.ExtractExecution, error)
}
Learning covers the learning workflow: the evidence snapshot, the evidence refresh, the distillation, the stale-pending cleanup and the trigger extraction. Lessons › Evidence and Lessons › Run use it. Its runs call GitHub or the agent, so each run takes a context that stops it.
type Maintenance ¶
type Maintenance interface {
// Pins reads the installed pins of .seamark/lessons.yaml and relates
// them to the ledger. It needs no index and never fails: a file or
// ledger problem is data in the inspection. It writes nothing.
Pins() *maintain.Inspection
// PlanMaintenance plans a retarget or a prune of the selected pins.
// It writes nothing.
PlanMaintenance(req maintain.Request) (*maintain.Plan, error)
// CommitMaintenance revalidates and commits exactly the reviewed plan.
CommitMaintenance(p *maintain.Plan) (maintain.Result, error)
// PlanRecovery plans the ledger-only completion of a partial
// maintenance operation of this session. It writes nothing.
PlanRecovery(r *maintain.Recovery) (*maintain.Plan, error)
// CommitRecovery revalidates the recovery proof and completes the
// ledger update. It never writes the pin file.
CommitRecovery(p *maintain.Plan) (maintain.Result, error)
// PlanAuthor plans the create, edit, or removal of one pin. It
// writes nothing. A missing ledger or a foreign root is a blocker
// of the plan, not an error.
PlanAuthor(req maintain.AuthorRequest) (*maintain.AuthorPlan, error)
// CommitAuthor revalidates and writes exactly the reviewed pin file
// bytes. It never writes the ledger, and it refuses a foreign root
// with ErrForeignRoot.
CommitAuthor(p *maintain.AuthorPlan) (maintain.AuthorResult, error)
}
Maintenance covers the installed pins of .seamark/lessons.yaml: the pin read, the pin authoring, the retarget and prune maintenance, and the recovery of a partial operation. Lessons › Pins, its authoring form, and its maintenance modal use it.
type PolicySummary ¶
PolicySummary is the typed rule count of the gate policy, for the Guard card. Err holds the policy load failure.
type Reader ¶
type Reader interface {
// Root returns the effective repository root.
Root() string
// DBPath returns the index database path.
DBPath() string
// Version returns the seamark version.
Version() string
// Fresh reports a workspace without any Seamark state: no index
// database, no .seamark directory, and no recognized integration
// artifact.
Fresh() (bool, error)
// Doctor runs the installation checks of `seamark doctor`.
Doctor() *doctor.Report
// Index probes the index and gathers semantic health when ready.
Index() IndexView
// Integrations inspects every registered client.
Integrations() []integration.Inspection
// Clients returns the registered client descriptors.
Clients() []integration.Client
// AgentsOnPath returns the IDs of the registered clients whose agent
// command is on PATH. It never runs an agent.
AgentsOnPath() []string
// Ledger reads the proposal decision ledger.
Ledger() (*proposals.Ledger, error)
// Stats reads the lesson delivery statistics.
Stats() lessonstats.Snapshot
// Sample runs the lesson selection of one surface for explicit
// paths under the two lessons.yaml texts of one review. It never
// emits, records, or leases delivery, and it never builds the index.
Sample(req SampleRequest) SampleView
}
Reader is the shared read side of the workspace: the paths, the diagnostics, the index, the integrations, the ledger, the statistics, and the sample. The shell and most screens use it. Its methods never write, so a helper that takes only a Reader cannot change the workspace.
type SampleRequest ¶
type SampleRequest struct {
Surface SampleSurface
// Paths are the paths to sample, in the order of the user. They are
// user text.
Paths []string
// Before and After are lessons.yaml texts. Before also decides Stale.
Before, After []byte
}
SampleRequest is one sample: a surface, the explicit paths, and the two lessons.yaml texts of one review. Before is the file text that the review starts from, and After is its candidate: AuthorPlan.Before and After, or settings.Plan.Bytes. No other text reaches a sample, so a sample never combines the unsaved drafts of two reviews.
type SampleSurface ¶
type SampleSurface int
SampleSurface names the surface whose selection a sample runs.
const ( // SampleHook is the selection of one edit-hook injection: the hook // names its paths with delivery.NormalizePaths and selects with the // hook budget. SampleHook SampleSurface = iota // SampleChangeSet is the selection of one change_set answer or one // check advisory: change_set names its paths with // report.ChangeSetNames and selects with the change budget. SampleChangeSet )
func (SampleSurface) String ¶
func (s SampleSurface) String() string
String names the surface for a label.
type SampleView ¶
type SampleView struct {
Surface SampleSurface
// Paths are the files that the selection uses, named by the rule of
// the surface. The hook gives its normalized files. change_set gives
// one name per requested path, repeats kept. Paths is nil when the
// sample is unavailable before the naming.
Paths []string
// Rejected are the paths that the hook rejects, as NormalizePaths
// keeps them: at most its first few. change_set rejects no path.
Rejected []delivery.RejectedPath
// Unindexed are the entries of Paths that the index does not know,
// by the rule of report.ChangeSetNames, for both surfaces. They are
// a label only: a new file still matches by path.
Unindexed []string
// NoFile are the entries of Paths that name no existing file or
// directory, a future path for example. A relative entry resolves
// against the effective root. They are a label only: a lesson can
// match a path that does not exist yet.
NoFile []string
// Current is the selection under Before, Candidate under After.
Current, Candidate report.Selection
// CurrentThreshold and CandidateThreshold are the thresholds of the
// two configurations, as the lessons reader decodes them. An
// exclusion says only that a lesson is below the threshold, so a
// view needs the number to name it. Both are zero until both texts
// parse.
CurrentThreshold, CandidateThreshold int
// text that starts with "Sample unavailable: ". It is empty when the
// sample computes both selections.
Unavailable string
// IndexedAt is the time of the last index run; zero when unknown.
// ReadAt is the start time of the evidence read; zero when no read
// succeeded.
IndexedAt, ReadAt time.Time
// Stale is true when lessons.yaml under the effective root no longer
// holds Before. The saved side of the sample is then not the disk.
Stale bool
}
SampleView is the result of one sample. Either Unavailable is set, or Current and Candidate both hold a computed selection. A view never shows an unavailable sample as two empty selections. Sample states the pairing rule of the two selections.
Paths, Rejected, Unindexed, NoFile, and Unavailable hold user, file, or index text: a screen sanitizes them before display.
type Settings ¶
type Settings interface {
// Settings reads the focused settings of both documents. The error
// reports invalid arguments only; a file problem or a foreign root
// is data in the snapshot.
Settings() (*settings.Snapshot, error)
// PlanSettings plans a guarded patch of one settings group. It
// writes nothing.
PlanSettings(snap *settings.Snapshot, edits []settings.Edit) (*settings.Plan, error)
// SaveSettings writes exactly the reviewed plan, or reports it stale.
// A lessons save refuses a foreign root with ErrForeignRoot.
SaveSettings(p *settings.Plan) (settings.Result, error)
}
Settings covers the focused settings of the two settings documents, config.yaml and lessons.yaml: the read, a guarded plan and the save. The Settings screen uses it.
type Setup ¶
type Setup interface {
// PlanSetup plans an integration change. It writes nothing.
PlanSetup(setups []integration.ClientSetup) (*SetupPreview, error)
// ApplySetup applies exactly the reviewed plan.
ApplySetup(p *SetupPreview) (integration.SetupResult, error)
// BuildIndex runs a local index build. It cannot be cancelled.
BuildIndex(force bool, progress func(IndexEvent)) (*index.Summary, error)
}
Setup covers the integration setup and the local index build. The first-run flow, the setup wizard and the Overview build use it. These flows install Seamark in the workspace, so they share one area.
type SetupPreview ¶
type SetupPreview struct {
Setups []integration.ClientSetup
Plan *integration.SetupPlan
Writes []integration.WritePreview
GateMode string
// PlannedAt is the plan time; the result screen reports it.
PlannedAt time.Time
}
SetupPreview is a reviewed integration plan: the shared setup plan plus a readable diff of every planned write.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package servicestest holds the fixtures of the tests that run the production engine of internal/tui/services.
|
Package servicestest holds the fixtures of the tests that run the production engine of internal/tui/services. |