obsout

package
v1.0.4 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 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