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
- Variables
- func BuriedProposalHint(r *Report) bool
- func FormatProblems(ps []Problem) string
- func JSONSchema(maxBlocks int) map[string]any
- func Normalize(r *Report)
- func Render(r *Report, opts Options) string
- type Block
- type BlockType
- type Choice
- type Line
- type LineKind
- type Option
- type Options
- type Problem
- type Report
- func (r *Report) Block(id string) *Block
- func (r *Report) Choice(number int) *Choice
- func (r *Report) Choices() []Choice
- func (r *Report) FirstInteractive() int
- func (r *Report) HasInteractive() bool
- func (r *Report) HiddenNotRunCount(opts Options) int
- func (r *Report) Order() []int
- func (r *Report) PlainText() string
- type Rules
- type Status
Constants ¶
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.
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.
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.
const MaxChoices = 9
MaxChoices is how many options one card can offer to a keypress, which is how many digits there are to press.
const ResponseSingle = "single"
ResponseSingle is the only response_type there is: one option, with free text always allowed alongside it.
Variables ¶
var BlockTypes = []BlockType{ BlockChange, BlockFinding, BlockVerification, BlockWarning, BlockDecision, BlockQuestion, BlockBlocked, BlockNextStep, BlockUnclassified, }
BlockTypes lists every block type a model may send.
var Effects = []string{EffectDispatch, EffectCollect, EffectResolve}
Effects lists the allowed values of an option's effect.
var Levels = []string{"low", "medium", "high", "critical"}
Levels are the values importance and severity take, least to most urgent.
var Statuses = []Status{ StatusCompleted, StatusCompletedWithNotes, StatusNeedsAction, StatusBlocked, StatusFailed, }
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.
var VerifyStatuses = []string{VerifyPassed, VerifyFailed, VerifyPartial, VerifyNotRun}
VerifyStatuses lists the allowed values of a verification block's status.
Functions ¶
func BuriedProposalHint ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
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 ¶
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.
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" )
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.
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.
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) Choices ¶
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 ¶
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 ¶
HasInteractive reports whether any block asks the user for something.
func (*Report) HiddenNotRunCount ¶
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 ¶
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.
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" )