Documentation
¶
Overview ¶
Package telemetry records how jade's tools are actually used.
Why this exists. jade's design bet is that giving a coding agent a wide enough tool surface keeps it inside jade, where edits are revision-tracked, validated and guarded — and that every gap sends the agent to bash, where none of that applies. That bet has been evaluated so far by the agent hand-writing docs/feedback.md from recollection, which is exactly the unreliable instrument the tooling was supposed to replace. This measures it instead.
The question it is built to answer is narrower than "usage stats": which tool errors plausibly send a caller back to the shell. An ambiguous anchor, a symbol that was not found, a stale revision, a timeout — each is a moment where the jade path failed and bash was one keystroke away. Those are classified and counted separately from ordinary usage for that reason.
What is deliberately not recorded ¶
No tool arguments, no response bodies, and no error message text. Only the tool name, wall time, response size, and a failure *class*.
Arguments carry source code, file paths and search queries; an error message routinely quotes the source line it failed on. A telemetry file containing those is a copy of the repository by another name — it could not be attached to a bug report, shared with the jade authors, or committed, which would defeat the entire purpose of collecting it. Keeping it content-free is what makes it shareable, and shareable is the point.
Index ¶
- Constants
- Variables
- func SummaryLine(s Summary) string
- type FailureCount
- type Outcome
- type Record
- type Recorder
- func (r *Recorder) Read() ([]Record, error)
- func (r *Recorder) Record(tool string, duration time.Duration, bytes int, err error)
- func (r *Recorder) RecordOutcome(tool string, duration time.Duration, bytes int, outcome Outcome)
- func (r *Recorder) Reset() error
- func (r *Recorder) Summarize() (Summary, error)
- type Summary
- type ToolStats
Constants ¶
const ( Dir = ".jade" File = "telemetry.jsonl" )
Dir and File name the log's location. It shares .jade/ with the command registry, so a repo has exactly one jade-owned directory.
Variables ¶
var ErrDisabled = errors.New("telemetry is disabled (JADE_TELEMETRY=0)")
ErrDisabled reports that telemetry is switched off, so a caller asking for a summary gets an explanation rather than a confusing empty one.
var RelPath = filepath.Join(Dir, File)
RelPath is the log's path relative to the workspace root.
Functions ¶
func SummaryLine ¶
SummaryLine renders the one-line headline used in responses.
Types ¶
type FailureCount ¶
FailureCount is one failure class and how often it occurred.
type Outcome ¶
type Outcome string
Outcome classifies how a tool call ended.
const ( // OK is a call that returned without error. OK Outcome = "ok" // NotFound: a symbol, file or anchor the caller named does not exist. NotFound Outcome = "not_found" // Ambiguous: an anchor or name matched more than once, so jade refused // rather than guessing. Ambiguous Outcome = "ambiguous" // StaleRevision: the workspace moved under a preconditioned edit. StaleRevision Outcome = "stale_revision" // Timeout: a job or command exceeded its bound. Timeout Outcome = "timeout" // InvalidInput: the request was malformed or missing a required field. InvalidInput Outcome = "invalid_input" // missing or failed. Distinct from the rest because the fix is // installation, not usage. Unavailable Outcome = "unavailable" // Other: an error that did not match any known class. A rising count here // means the classifier needs a new case, so it is worth watching. Other Outcome = "other" )
func Classify ¶
Classify maps an error to a failure class.
It matches on message text because jade's errors cross package boundaries as wrapped strings rather than as a single sentinel hierarchy. That is fragile by nature, which is why Other exists as an explicit bucket: a rising Other count is the signal that this function has fallen behind the errors it is classifying, and is more useful than a misclassification that looks like a real trend.
func ClassifyResponse ¶
ClassifyResponse inspects a typed response for a failure reported *in band* — as a field on a successful response rather than as an error.
Some jade tools report failure this way by design. read_symbol resolves a name to "exact", "ambiguous" or "not_found" and returns the candidates alongside, which is more useful than an error that throws them away. But the call still failed from the caller's point of view, and counting it as a success understates precisely the number this package exists to measure. Found when the first telemetry tests recorded a not_found read_symbol as OK.
The bool reports whether an in-band outcome was found at all, so a caller can tell "this response says it failed" from "this response has nothing to say about it".
What is deliberately not treated as a failure ¶
A check, test run or declared command that reports Passed=false is a *verdict*, not a jade failure: the build is broken, and jade did its job by saying so clearly. Counting it here would swamp the fallback signal with ordinary red builds and make the number meaningless.
Likewise a search or grep returning zero matches. "Nothing matches" is a correct and often expected answer; treating every empty result as a failure would punish the tools for being asked honest questions.
type Record ¶
type Record struct {
Time time.Time `json:"t"`
Tool string `json:"tool"`
Millis int64 `json:"ms"`
Bytes int `json:"bytes"`
Outcome Outcome `json:"outcome"`
}
Record is one tool call. Field names are short because this is written once per call and read in bulk.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder appends tool-call records to the workspace log.
Every method is safe to call concurrently and none of them can fail a tool call. A telemetry write that errors is dropped silently: losing a measurement is a small cost, while failing a working edit because the measurement could not be written would be an absurd trade — and would make jade less reliable than the bash it is competing with.
func New ¶
New builds a Recorder for the workspace at root.
Setting JADE_TELEMETRY=0 disables recording entirely. An off switch is not optional for something that writes a file into the user's repository on every call.
func (*Recorder) Read ¶
Read returns every record in the log, oldest first. A malformed line is skipped rather than failing the read: a partially written record from a killed process should not make the whole history unreadable.
func (*Recorder) RecordOutcome ¶
RecordOutcome appends one call with an already-decided outcome, for callers that know more than the error does — notably a response that reports its own failure in band. See ClassifyResponse.
type Summary ¶
type Summary struct {
Tools []ToolStats
// Fallbacks counts failure classes across all tools — the headline
// number, since each is a moment the jade path failed and the shell was
// available.
Fallbacks []FailureCount
TotalCalls int
TotalErrors int
TotalBytes int64
Path string
Since time.Time
}
Summary aggregates the log.