render

package
v0.8.1 Latest Latest
Warning

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

Go to latest
Published: May 21, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package render formats check findings for stdout — either as human-readable text (default) or as a structured JSON envelope (--format=json).

JSON envelope contract

Every aiwf invocation that emits JSON writes a single object with these slots:

tool      // always "aiwf"
version   // the binary's reported version
status    // "ok" | "findings" | "error" — overall outcome
findings  // []Finding — validation outcomes, cross-cutting; may
          // be present on any verb that runs the validators.
          // Empty when the run produced none.
result    // verb-specific payload. Different shape per verb:
          //   - check    → omitted (findings is the result)
          //   - history  → { id, events: [...] }
          //   - future verbs → their own shape
metadata  // counts, timing, root path, correlation_id when
          // present — auxiliary data, not load-bearing

findings vs result is the load-bearing distinction: findings is always the same shape across verbs (so a CI script can grep one thing), result is whatever the verb returns (so each verb can model its own output without compromise). Downstream tooling that touches both reads findings the same way everywhere and switches on the verb name to interpret result.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bold added in v0.8.1

func Bold(s string, enabled bool) string

Bold wraps s in the ANSI bold-on / reset-all escape sequence when enabled is true; otherwise returns s unchanged. Callers resolve `enabled` via ColorEnabled at the call site — Bold itself is a pure string operation with no IO side effects.

An empty string returns "" unchanged: a bold-on/reset-all wrapper around no content is wasted bytes that some pagers render as an empty 1-char attribute region.

func ColorEnabled added in v0.8.1

func ColorEnabled(f *os.File) bool

ColorEnabled returns true when f is a terminal and the operator has not opted out of color via the NO_COLOR environment variable. Per https://no-color.org, NO_COLOR is honored when set to any non-empty value regardless of content; an empty NO_COLOR is treated as unset.

Callers gate ANSI escape sequences on this predicate. Plain glyphs (✓ → ○ ✗) are content, not color, and stay enabled everywhere — they render in any UTF-8-capable consumer including CI logs and pipes.

The TTY check rides on TerminalWidth's non-zero return, so the same "no styling under `go test`, pipes, and redirected output" guarantee applies — golden tests stay byte-identical to the un-styled rendering without any test-time override.

func Dim added in v0.8.1

func Dim(s string, enabled bool) string

Dim wraps s in the ANSI dim-on / reset-all escape sequence when enabled is true; otherwise returns s unchanged. Used for secondary context lines (branch, age, etc.) so the eye lands on primary content first. G-0122.

func JSON

func JSON(w io.Writer, env Envelope, pretty bool) error

JSON writes the envelope to w as a single JSON object. Pretty enables indented output for human reading; the default (non-indented) is what CI and downstream tooling consume.

func StatusColor added in v0.8.1

func StatusColor(s, status string, enabled bool) string

StatusColor wraps s in the ANSI color appropriate for the entity / AC status when enabled is true; otherwise returns s unchanged. The color mapping mirrors StatusGlyph's grouping:

  • green: terminal positive (done, met, addressed, accepted, active)
  • yellow: in flight (in_progress)
  • cyan: pending (open, draft, proposed)
  • red: terminal negative (cancelled, wontfix, rejected, deprecated, retired, superseded)
  • uncolored: status not in the closed-set vocabulary

G-0122 color hierarchy.

func StatusFor

func StatusFor(findings []check.Finding) string

StatusFor returns the canonical envelope status string for a given findings list. "ok" if empty, "findings" if anything was reported. Internal-error envelopes are constructed directly by callers.

func StatusGlyph added in v0.8.1

func StatusGlyph(status string) string

StatusGlyph returns the canonical glyph for a kernel status string, or "" when the status is unrecognised (e.g. a value loaded from a pre-canonical commit, or a typo). The palette is per G-0080's resolution sketch:

✓  met / done / addressed / accepted    — "the work is finished"
→  in_progress / active                 — "the work is moving"
○  open / draft / proposed              — "the work hasn't started"
✗  cancelled / wontfix / rejected /
   retired / superseded                 — "the work is closed off"

Every glyph is 1-cell BMP so it works under text/tabwriter's rune-counting (the kernel does not pull github.com/mattn/go-runewidth per G-0080's *Out of scope*). The mapping is per-status-value, not per-kind: every kind's status set is a subset of the four buckets above, so one map covers epic/milestone/ADR/gap/decision/contract.

Unknown statuses return "" — callers render the status text without a glyph rather than guess. The set is intentionally exhaustive over the kernel's current status vocabulary; ADR-0008 keeps that vocabulary stable.

func TerminalWidth added in v0.8.1

func TerminalWidth(f *os.File) int

TerminalWidth returns the column width of f when f is a terminal, or 0 when it is not (piped, redirected to a file, run under `go test`, etc.). Callers gate truncation on the non-zero result so the same rendering code stays byte-identical in non-TTY contexts — golden tests, CI logs, and pipelines all see untruncated output.

The "not a terminal" path is the silent default: any error from the underlying syscall (closed fd, unsupported platform, etc.) collapses to the same zero return. A separate IsTerminal predicate would let callers distinguish "no TTY" from "TTY but width-detection failed", but no current call site cares.

func Text

func Text(w io.Writer, findings []check.Finding) error

Text writes one finding per line in linter-style format:

{path}:{line}: {severity} {code}[/{subcode}]: {message} — hint: {hint}

followed by a one-line summary. The `:line` is omitted when the finding has no line (load-errors that fail before parsing). The `— hint: ...` suffix is omitted when the finding has no hint. Findings without a path are still rendered (the path:line prefix is dropped but the rest of the line is unchanged).

func TextSummary added in v0.8.0

func TextSummary(w io.Writer, findings []check.Finding) error

TextSummary writes the default-mode text rendering of findings used by `aiwf check` (without --verbose). Errors are rendered per instance — identical to the full per-instance shape produced by Text — because each error is per-instance-actionable. Warnings are collapsed into a per-code summary:

<code> (warning) × N — <representative message>

where N is the count of findings sharing that Code and the representative message is the Message of the first finding in the input slice with that code (per M-0089 *Constraints*: "the first finding's Message field, verbatim").

Summary lines are sorted by count descending, with ties broken alphabetically by code (also pinned in *Constraints* — pinned here so the golden files in the test suite don't drift).

The footer line ("N findings (E errors, W warnings)") is unchanged and reflects raw instance counts, not summary-line counts.

func Truncate added in v0.8.1

func Truncate(s string, maxRunes int) string

Truncate returns s capped to maxRunes runes, replacing the tail with the single-rune ellipsis "…" when truncation occurred. When maxRunes is non-positive or s already fits, returns s unchanged. When maxRunes is 1, returns "…" (the ellipsis itself is the only output that fits).

Operates on runes, not bytes, so multibyte characters in titles are handled correctly. Display-cell width is not modeled — every rune counts as one cell. The aiwf glyph palette (`✓ → ○ ✗`) is all 1-cell BMP, so this matches reality for current output; emoji or CJK would need go-runewidth (deferred per G-0080's out-of-scope).

Types

type Envelope

type Envelope struct {
	Tool     string          `json:"tool"`
	Version  string          `json:"version"`
	Status   string          `json:"status"`
	Findings []check.Finding `json:"findings"`
	Result   any             `json:"result,omitempty"`
	Metadata map[string]any  `json:"metadata,omitempty"`
}

Envelope is the JSON shape every aiwf invocation writes when invoked with --format=json. Status is one of "ok", "findings", "error". Result carries the verb's payload (nil for `aiwf check`). Metadata carries timing, counts, correlation_id, etc.

Jump to

Keyboard shortcuts

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