Help us shape the future of Go! Take the 2026 Go Developer Survey

obsout

package
v1.0.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

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

View Source
const MaxSuggested = 3

MaxSuggested is the most next commands a document offers.

Variables

This section is empty.

Functions

func Cap

func Cap[T any](items []T, max int) (kept []T, omitted int)

Cap keeps at most max items and reports how many it dropped. max <= 0 keeps everything.

func Command

func Command(argv ...string) string

Command joins argv into one shell-runnable command line, single-quoting any word that needs it.

func NewSanitizingWriter

func NewSanitizingWriter(w io.Writer) io.Writer

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

func SanitizeText(s string) string

SanitizeText is NewSanitizingWriter over one string, for a renderer that builds a cell before it writes.

func Suggest

func Suggest(cmds ...string) []string

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.

func Table

func Table(w io.Writer, header []string, rows [][]string, maxCell int)

Table writes an aligned plain-text table: the human form of a list verb. Cells are single-line (newlines and tabs become spaces) and a cell wider than maxCell is cut with an ellipsis, so one long log body cannot wrap the table. maxCell <= 0 means no cut.

Types

type Failure

type Failure struct {
	Head
	Suggested []string `json:"suggested,omitempty"`
}

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.

func Failed

func Failed(schema string, err error, suggested ...string) Failure

Failed builds the failure document for err.

type Flags

type Flags struct {
	JSON   bool
	Fields string
	Limit  int
}

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

func (f Flags) Emit(w io.Writer, schema Schema, doc any, fields []string) (bool, error)

Emit writes doc as JSON (honouring --fields) when --json is set and reports true; a false return means the caller renders the human form.

func (Flags) Parsed

func (f Flags) Parsed(schema Schema) ([]string, error)

Parsed validates --fields against schema.

func (*Flags) Register

func (f *Flags) Register(fs *pflag.FlagSet, defaultLimit int)

Register adds the flags to fs. defaultLimit is the verb's own page size.

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

func (h *Head) Stamp(schema string, err error)

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.

func Bound

func Bound[T any](items []T, max int, nextToken string) ([]T, Page)

Bound caps items and fills the page: truncated when anything was cut or the backend has a next page. A backend that paged for the verb passes its token.

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) Help

func (s Schema) Help() string

Help is the "Fields:" block for a verb's --help.

func (Schema) ParseFields

func (s Schema) ParseFields(csv string) ([]string, error)

ParseFields splits a --fields value ("a,b.c") and checks every path against the schema.

func (Schema) Project

func (s Schema) Project(doc any, fields []string) ([]byte, error)

Project returns the compact JSON of doc restricted to fields (plus the always-kept keys). It is Write without the indentation, for one NDJSON line.

func (Schema) Write

func (s Schema) Write(w io.Writer, doc any, fields []string) error

Write encodes doc to w: the whole document, or only the selected paths (plus the schema's always-kept keys) when fields is non-empty. Output is indented, the house --json convention.

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

func NewStream(w io.Writer, schema Schema, fields []string) *Stream

NewStream streams records of schema to w, projected to fields (empty = whole records).

func (*Stream) End

func (s *Stream) End(err error) error

End writes the closing envelope line for err (nil = success). It is never projected: the verdict is always whole.

func (*Stream) Record

func (s *Stream) Record(rec any) error

Record writes one record.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL