Documentation
¶
Overview ¶
Package obsout is the agent output layer of the feedback-loop verbs (`forge obs …`, `forge env releases …`, `forge db query`): the one place that decides what a machine-readable document looks like, so every verb an agent calls answers in the same shape.
The contract (research/feedback-loop/DESIGN.md §8.2):
- ONE document on stdout. It starts with a Head: the schema name and the ok / exit_code / error triple, computed from the error the verb returns so the document and the process status cannot disagree. Progress goes to stderr. A streaming verb writes NDJSON (Stream) and ends with a line that carries the same head.
- Scope echoes what was resolved (env, backend, time range, services).
- Lists are bounded: Page carries truncated / omitted / next_page_token, and Cap is how a verb applies its cap.
- Suggest offers at most three next commands, each complete and runnable. Command builds one with correct shell quoting.
- --fields (Schema.ParseFields, [Write]) projects a document with dotted paths, validated against the fields the verb declares, so a typo is an error rather than a silently empty answer.
- Absent means unknown: nothing here emits null or empty placeholders.
The package imports nothing from forge's cli, the control plane or reliant. A verb embeds Head (and Page) in its own document struct.
Index ¶
- Constants
- func Cap[T any](items []T, max int) (kept []T, omitted int)
- func Command(argv ...string) string
- func NewSanitizingWriter(w io.Writer) io.Writer
- func SanitizeText(s string) string
- func Suggest(cmds ...string) []string
- func Table(w io.Writer, header []string, rows [][]string, maxCell int)
- type Failure
- type Flags
- type Head
- type Page
- type Schema
- type Scope
- type Stream
Constants ¶
const MaxSuggested = 3
MaxSuggested is the most next commands a document offers.
Variables ¶
This section is empty.
Functions ¶
func Command ¶
Command joins argv into one shell-runnable command line, single-quoting any word that needs it.
func NewSanitizingWriter ¶
NewSanitizingWriter returns a writer that passes printable text through to w and drops everything that can steer a terminal. An escape sequence never outlives the line it started on, so an unterminated one cannot swallow later output.
func SanitizeText ¶
SanitizeText is NewSanitizingWriter over one string, for a renderer that builds a cell before it writes.
func Suggest ¶
Suggest returns the first MaxSuggested distinct non-empty commands, in the order given: the caller ranks them. Each must be a complete, runnable command (use Command); a suggestion is a concrete call, never advice.
Types ¶
type Failure ¶
Failure is the document of a verb that failed before it had a result: the head and the commands that fix it. It is what --json prints on a refusal.
type Flags ¶
Flags are the output flags every agent verb registers the same way: --json, --fields and --limit. A verb that streams also honours --json as NDJSON.
func (Flags) Emit ¶
Emit writes doc as JSON (honouring --fields) when --json is set and reports true; a false return means the caller renders the human form.
type Head ¶
type Head struct {
// Schema names the document and its version, e.g. "forge.obs.logs/v1".
// Field names under a schema are stable; a breaking change is a new
// version.
Schema string `json:"schema"`
// OK is true exactly when the process will exit 0.
OK bool `json:"ok"`
// ExitCode is exactly the status the process will exit with.
ExitCode int `json:"exit_code"`
// Error is the failure message; absent on success.
Error string `json:"error,omitempty"`
}
Head is the common head of every agent document. Embed it first in a verb's document so the schema name and the verdict lead the output.
func (*Head) Stamp ¶
Stamp fills the head from the error the verb is about to return. It takes the error rather than a code so the two cannot diverge: an error that carries an ExitCode() int (forge's exitCodeError) reports that code, any other error is 1 ("we looked, and it is wrong"); internal/exitcode is the one resolution.
type Page ¶
type Page struct {
// Truncated is true when the list is not everything the query matched.
Truncated bool `json:"truncated"`
// Omitted counts items a cap cut from what the backend returned (not
// counting a further page the backend holds). Absent when nothing was cut.
Omitted int `json:"omitted,omitempty"`
// NextPageToken resumes the list; pass it back as --page-token. Absent when
// there is no further page.
NextPageToken string `json:"next_page_token,omitempty"`
}
Page is the bound every list document carries. Embed it in the document next to the list.
type Schema ¶
type Schema struct {
// Name is the schema name stamped in the head, e.g. "forge.obs.logs/v1".
Name string
// Fields are every selectable path, parents included ("logs",
// "logs.service").
Fields []string
}
Schema declares one document: its name and the dotted field paths a caller may select with --fields. A verb declares it once; --help prints it (Schema.Help) and --fields is validated against it, so a typo is an error that lists the real fields instead of an empty answer.
Paths are dotted. A list is transparent: with "logs" a list of objects, "logs.service" is the service of every element. A path ending ".*" declares an open map whose keys are data ("logs.attributes.*"), so --fields logs.attributes.http.route is valid.
func (Schema) ParseFields ¶
ParseFields splits a --fields value ("a,b.c") and checks every path against the schema.
type Scope ¶
type Scope struct {
Env string `json:"env"`
Backend string `json:"backend"`
// EnvID is the control plane's id for the env, on the cloud backend.
EnvID string `json:"env_id,omitempty"`
// From and To are the half-open range queried, in UTC.
From time.Time `json:"from"`
To time.Time `json:"to"`
Services []string `json:"services,omitempty"`
}
Scope echoes what a query was resolved against, so a document is self-describing: the same verb run against another env or backend is visibly a different question.
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
Stream writes NDJSON: one compact JSON record per line, for a verb that follows (`logs --follow --json`). A stream ends with Stream.End, a line carrying the same Head a one-shot document does, so a consumer learns the verdict from the last line rather than from a process it may not own.
func NewStream ¶
NewStream streams records of schema to w, projected to fields (empty = whole records).