report

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package report holds the structured end-of-turn report: its shape, the checks it must pass before the agent accepts it, and the rendering the agent and the interactive front end both draw it with.

A report is what a turn ends with instead of free prose. The model calls the Report tool with JSON in the shape below; the tool refuses JSON that does not fit and says what is wrong, and the turn cannot end until an acceptable report exists. The front end then draws the accepted report as a card at the prompt, where one keypress answers a decision it raises.

This package depends on nothing else in kvit-coder, so the tool that produces a report, the session reader that finds one and the composer that draws it all work from one definition of it.

docs/structured-reports.md is the design.

Index

Constants

View Source
const (
	VerifyPassed = "passed"
	VerifyFailed = "failed"
	// VerifyPartial and VerifyNotRun both require the block to say what was
	// not verified and why.
	VerifyPartial = "partial"
	VerifyNotRun  = "not_run"
)

Verification statuses.

View Source
const (
	// EffectDispatch sends the option's instruction as the next turn's prompt.
	EffectDispatch = "dispatch"
	// EffectCollect puts an answer in the composer to edit and send, rather
	// than starting a turn at once.
	EffectCollect = "collect"
	// EffectResolve records that the user is stopping here. No turn starts.
	EffectResolve = "resolve"
)

Option effects: what picking an option does.

View Source
const (
	// HeadlineMax is the longest headline, in characters.
	HeadlineMax = 160
	// SummaryMax is the longest block summary, in characters.
	SummaryMax = 240
	// DetailsMax is the largest details field, in bytes.
	DetailsMax = 32 * 1024
	// LabelMax is the longest option label, in characters.
	LabelMax = 60
	// TextMax is the longest option description or consequence.
	TextMax = 200
	// PreviewMax is the largest option preview, in bytes.
	PreviewMax = 4 * 1024
	// InstructionMax is the largest dispatch instruction, in bytes.
	InstructionMax = 1024
	// IDMax is the longest block or option id, in characters.
	IDMax = 64
	// MinOptions and MaxOptions bound an interactive block's option list.
	MinOptions = 2
	MaxOptions = 6
	// DefaultMaxBlocks is the block limit when configuration sets none.
	DefaultMaxBlocks = 20
)

Limits. They are the schema's documented bounds and the validator's checks, so a model reading the schema and the tool checking the result agree.

View Source
const MaxChoices = 9

MaxChoices is how many options one card can offer to a keypress, which is how many digits there are to press.

View Source
const ResponseSingle = "single"

ResponseSingle is the only response_type there is: one option, with free text always allowed alongside it.

Variables

BlockTypes lists every block type a model may send.

Effects lists the allowed values of an option's effect.

View Source
var Levels = []string{"low", "medium", "high", "critical"}

Levels are the values importance and severity take, least to most urgent.

Statuses lists every status a model may send, in the order they are documented. The list is the schema's enum and the validator's allowed set, so the two cannot drift.

VerifyStatuses lists the allowed values of a verification block's status.

Functions

func BuriedProposalHint

func BuriedProposalHint(r *Report) bool

BuriedProposalHint reports whether a rejected report proposes a follow-up in prose while offering no interactive block for it. The caller appends a one-line hint telling the model to expose each proposal as its own next_step block instead.

func FormatProblems

func FormatProblems(ps []Problem) string

FormatProblems is the rejection the model reads. It leads with the marker so the problem is unmistakable in a wall of tool output, then lists every problem as a path and a sentence.

func JSONSchema

func JSONSchema(maxBlocks int) map[string]any

JSONSchema is the tool's parameter schema as the model sees it.

The nine block types share one object rather than being expressed as a discriminated union: several endpoints handle oneOf poorly, and a model that cannot decode the schema sends nothing usable at all. Each field's description says which types require it, and Validate is what actually enforces that, reporting every violation at once so a wrong guess costs one round trip rather than nine.

func Normalize

func Normalize(r *Report)

Normalize cleans up what a model sent before it is checked: surrounding whitespace everywhere, and a code fence wrapped around a whole Markdown value. It never changes meaning, so a report that only needed normalizing is accepted rather than bounced for a formatting habit.

func Render

func Render(r *Report, opts Options) string

Render is the report as plain text, which is what a headless run prints and what the clipboard gets.

Types

type Block

type Block struct {
	Type    BlockType `json:"type"`
	ID      string    `json:"id"`
	Summary string    `json:"summary"`

	// Shared optional fields.
	Impact         string   `json:"impact,omitempty"`
	Recommendation string   `json:"recommendation,omitempty"`
	Details        string   `json:"details,omitempty"`
	RelatedFiles   []string `json:"related_files,omitempty"`

	// finding
	Importance        string `json:"importance,omitempty"`
	BlocksCurrentTask *bool  `json:"blocks_current_task,omitempty"`

	// verification
	Status           string `json:"status,omitempty"`
	Evidence         string `json:"evidence,omitempty"`
	Limitation       string `json:"limitation,omitempty"`
	RequiredToVerify string `json:"required_to_verify,omitempty"`

	// warning
	Severity string `json:"severity,omitempty"`

	// question
	Question string `json:"question,omitempty"`

	// blocked
	Blocker        string `json:"blocker,omitempty"`
	RequiredAction string `json:"required_action,omitempty"`

	// decision, question, blocked, next_step
	Options              []Option `json:"options,omitempty"`
	ResponseType         string   `json:"response_type,omitempty"`
	RecommendationReason string   `json:"recommendation_reason,omitempty"`

	// unclassified
	ReasonUnclassified string `json:"reason_unclassified,omitempty"`
	SuggestedType      string `json:"suggested_type,omitempty"`
}

Block is one typed item of a report. Every block type shares id and summary; the rest of the fields apply to the types that document them, and the validator rejects a block that omits one its type requires.

One flat struct rather than nine is deliberate: it keeps decoding to a plain json.Unmarshal, and it lets the validator report every problem in a report at once instead of stopping at the first type it cannot decode.

func (*Block) Glyph

func (b *Block) Glyph() string

Glyph is the one-character marker at the start of a block's line. A verification's glyph says how the check went, so the outcome is legible without reading the line.

func (*Block) Interactive

func (b *Block) Interactive() bool

Interactive reports whether this block asks the user for something, which is what makes it answerable from the card and what holds back an inbox-only turn while it waits.

func (*Block) IsNotRunVerification

func (b *Block) IsNotRunVerification() bool

IsNotRunVerification reports whether this block is a verification that ran nothing. Those are the rows the live card collapses: required by the validator, noise on the card.

func (*Block) Option

func (b *Block) Option(id string) *Option

Option returns the option with the given id from the given block, or nil.

type BlockType

type BlockType string

BlockType selects which shape a block has.

const (
	// BlockChange is file edits and other work output.
	BlockChange BlockType = "change"
	// BlockFinding is something learned that matters to the task.
	BlockFinding BlockType = "finding"
	// BlockVerification is how the work was checked.
	BlockVerification BlockType = "verification"
	// BlockWarning is a risk or caveat.
	BlockWarning BlockType = "warning"
	// BlockDecision is a choice only the user can make.
	BlockDecision BlockType = "decision"
	// BlockQuestion is information the user has to supply.
	BlockQuestion BlockType = "question"
	// BlockBlocked is work that cannot continue.
	BlockBlocked BlockType = "blocked"
	// BlockNextStep is a proposed continuation the user accepts or redirects.
	BlockNextStep BlockType = "next_step"
	// BlockUnclassified is the last resort when nothing else fits.
	BlockUnclassified BlockType = "unclassified"
)

func (BlockType) Label

func (t BlockType) Label() string

Label is the block type as it appears at the head of its line.

type Choice

type Choice struct {
	// Block indexes Report.Blocks; Option indexes that block's Options.
	Block, Option int
	// Number is what the user presses, 1-based. Options past the ninth get 0:
	// there is no key for them, so the card shows them without a number.
	Number int
}

Choice is one option the report offers, located by the block and option it sits in and numbered as the card shows it.

type Line

type Line struct {
	Kind LineKind
	Text string
	// Block is the index into Report.Blocks, or -1 for the header.
	Block int
	// BlockID is that block's id, for callers that track selection by id.
	BlockID string
	// Option is the 1-based number of the option this line offers, or 0.
	// A digit key on the card acts on the option with this number.
	Option int
}

Line is one rendered line with enough structure to style or act on it.

func Lines

func Lines(r *Report, opts Options) []Line

Lines renders a report as structured lines in display order.

type LineKind

type LineKind int

LineKind says what a rendered line is, so a caller that paints the report — the composer's card — can style each kind without parsing the text back apart. The plain renderer ignores it.

const (
	// LineHeader is the status chip and the headline.
	LineHeader LineKind = iota
	// LineBlock is a block's own line: glyph, kind, id, summary.
	LineBlock
	// LineField is one of a block's typed fields, indented under it.
	LineField
	// LineOption is one numbered option of an interactive block.
	LineOption
	// LineInstruction is the instruction a dispatch option would run.
	LineInstruction
	// LineDetails is one line of a block's Markdown details.
	LineDetails
)

type Option

type Option struct {
	ID          string `json:"id"`
	Label       string `json:"label"`
	Description string `json:"description,omitempty"`
	Consequence string `json:"consequence,omitempty"`
	Preview     string `json:"preview,omitempty"`
	Effect      string `json:"effect"`
	// Instruction is what a dispatch option sends as the next prompt. It is
	// shown in full on the card, so the user sees what picking it will do.
	Instruction string `json:"instruction,omitempty"`
}

Option is one way forward offered by an interactive block.

type Options

type Options struct {
	// Expanded holds the ids of blocks whose details are shown.
	Expanded map[string]bool
	// ExpandAll shows every block's details, which is what a headless run and
	// the full viewer do.
	ExpandAll bool
	// HideNotRun collapses verification blocks that ran nothing into a
	// one-line footnote, which is what the live card does: the check stays
	// mandatory, but a docs-only edit no longer spends two lines saying so.
	// ExpandAll or an entry in Expanded still shows the block in full, and
	// the scrollback transcript (which expands everything) is unaffected.
	HideNotRun bool
}

Options controls how much of a report is drawn.

type Problem

type Problem struct {
	Path    string `json:"path"`
	Code    string `json:"code"`
	Message string `json:"message"`
}

Problem is one thing wrong with a report. Path is a JSON Pointer into the submitted arguments, so the model is told which field to fix rather than which rule it broke. Code is stable across wordings, for counting what models get wrong.

func Validate

func Validate(r *Report, rules Rules) []Problem

Validate checks a normalized report and returns everything wrong with it, so the model can fix a whole report in one pass rather than one problem per round trip. An empty result means the report is acceptable.

type Report

type Report struct {
	TaskStatus Status  `json:"task_status"`
	Headline   string  `json:"headline"`
	Blocks     []Block `json:"blocks"`
}

Report is the whole thing: what the turn amounted to, one sentence about it, and the typed items behind that sentence.

func (*Report) Block

func (r *Report) Block(id string) *Block

Block returns the block with the given id, or nil.

func (*Report) Choice

func (r *Report) Choice(number int) *Choice

Choice returns what the given key number picks, or nil.

func (*Report) Choices

func (r *Report) Choices() []Choice

Choices lists every option in the report, in display order, numbered continuously across blocks. Numbering across blocks rather than within one selected block is what lets a digit answer any question the report asks without a selection to move first.

func (*Report) FirstInteractive

func (r *Report) FirstInteractive() int

FirstInteractive returns the index of the first block, in display order, that asks the user something, or -1. It is what a digit key answers.

func (*Report) HasInteractive

func (r *Report) HasInteractive() bool

HasInteractive reports whether any block asks the user for something.

func (*Report) HiddenNotRunCount

func (r *Report) HiddenNotRunCount(opts Options) int

HiddenNotRunCount is how many not_run verifications opts would collapse. The card uses it for its footnote and for offering the expand key even when there are no details to show.

func (*Report) Order

func (r *Report) Order() []int

Order returns the block indices in display order: decision, question, blocked, next_step, warning, change, finding, verification, unclassified — regardless of the order the model sent them in. Blocks that tie keep the order they were sent in, so a report renders the same way every time.

func (*Report) PlainText

func (r *Report) PlainText() string

PlainText is the whole report with every detail shown.

type Rules

type Rules struct {
	// MaxBlocks bounds the block list. Zero selects DefaultMaxBlocks.
	MaxBlocks int
	// Mutated says whether the turn changed anything. It decides what
	// "completed" has to show: a turn that edited files says how it checked
	// them, while a turn that only read and explained is finished without a
	// verification block. Demanding one of a question-answering turn is how
	// the explanation ends up inside a block that verified nothing.
	Mutated bool
}

Rules are the parts of validation that depend on the turn rather than on the report, so the same report can be right for one turn and wrong for another.

type Status

type Status string

Status is the task_status field: what the turn amounted to.

const (
	// StatusCompleted means the work is done and checked.
	StatusCompleted Status = "completed"
	// StatusCompletedWithNotes means done, with a caveat stated in the report.
	StatusCompletedWithNotes Status = "completed_with_notes"
	// StatusNeedsAction means the turn stopped where the user must choose or
	// answer something.
	StatusNeedsAction Status = "needs_action"
	// StatusBlocked means the work cannot continue without the user or
	// something outside the session.
	StatusBlocked Status = "blocked"
	// StatusFailed means the work did not succeed, with the reason recorded.
	StatusFailed Status = "failed"
)

func (Status) Chip

func (s Status) Chip() string

Chip is the status as it appears at the head of the card.

Jump to

Keyboard shortcuts

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