Skip to main content

wire/answer/
usage.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! How often each skill and each tool server was used, by whom and on
7//! which day (`crates/wire/spec/Reading.lean` D28, D33).
8//!
9//! Folded from the ledger by `accounting::views::usage`; nothing here
10//! decides what counts as a use, which D33 states once.
11
12use std::collections::BTreeMap;
13
14use kernel::event::record::{AuditVerdict, ShelvedFrom};
15use kernel::{Address, B3Hash, RunId, Seq, TimeMs};
16use serde::{Deserialize, Serialize};
17
18use super::SkillShelf;
19
20/// Every skill on a shelf, and every skill the ledger saw used, one line
21/// each.
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
24pub struct SkillUsageAnswer {
25    /// In name order.
26    pub skills: Vec<SkillUsageLine>,
27}
28
29/// One skill, by name: where it sits now, the contents runs have read,
30/// and every time one of them read it.
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
33pub struct SkillUsageLine {
34    pub name: String,
35    /// Every shelf that holds the name now, with what it holds there.
36    /// Empty for a skill the ledger saw used that no shelf holds any
37    /// more.
38    pub held: Vec<HeldSkill>,
39    /// Each content a run was frozen with, oldest first.
40    pub versions: Vec<SkillVersion>,
41    /// Every use, in ledger order.
42    pub uses: Vec<SkillUse>,
43    /// Uses per UTC calendar day, oldest day first.
44    pub per_day: Vec<DayCount>,
45}
46
47/// One shelf's copy of a skill as it is now.
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
50pub struct HeldSkill {
51    pub shelf: SkillShelf,
52    pub digest: B3Hash,
53    pub audit: SkillAudit,
54}
55
56/// What the audits on the ledger say about the content a shelf holds now
57/// (`city::library::audit_state`).
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(tag = "state", rename_all = "snake_case")]
60#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
61pub enum SkillAudit {
62    /// No audit was ever had for any content of this skill.
63    Unaudited,
64    /// The latest audit of this content.
65    Audited { verdict: AuditVerdict, at: Seq },
66    /// Audits exist, none of them of this content: it changed since, and
67    /// the User is asked to audit it again.
68    Stale { audited: B3Hash },
69}
70
71/// One content of a skill, from the first line that named it.
72#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
73#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
74pub struct SkillVersion {
75    pub digest: B3Hash,
76    /// The first line with this content: its `skill_shelved`, or the
77    /// `run_started` that first pinned it.
78    pub seq: Seq,
79    pub at: TimeMs,
80    pub run: RunId,
81    #[serde(default)]
82    pub author: VersionAuthor,
83}
84
85/// Who put one content of a skill on its shelf, read off the ledger in
86/// ledger order (`crates/wire/spec/Reading.lean` D33).
87#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(tag = "by", rename_all = "snake_case")]
89#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
90pub enum VersionAuthor {
91    /// The city shelved it: the `skill_shelved` line and where it came
92    /// from.
93    Shelved { seq: Seq, from: ShelvedFrom },
94    /// The name was shelved before a run first read this content, never
95    /// with it: the content was changed outside the city's doors.
96    OutsideShelf,
97    /// No shelving line names the skill before a run first read this
98    /// content: it was on the shelf before the city recorded shelving.
99    #[default]
100    Unrecorded,
101}
102
103/// One read of a skill by a run that pinned it.
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
106pub struct SkillUse {
107    pub run: RunId,
108    /// The room the reading run worked in.
109    pub resident: Option<Address>,
110    pub seq: Seq,
111    pub at: TimeMs,
112    /// `guide` for a `describe`, `SKILL.md` for the document, or the
113    /// path inside the skill's package.
114    pub part: String,
115    /// The content the run was frozen with.
116    pub digest: B3Hash,
117    pub outcome: UseOutcome,
118}
119
120/// How the call that used it ended.
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
122#[serde(rename_all = "snake_case")]
123#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
124pub enum UseOutcome {
125    Ok,
126    Failed,
127    /// No `tool_result` answers the call: the run was stopped first.
128    Unknown,
129}
130
131/// How many uses one UTC calendar day holds.
132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
133#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
134pub struct DayCount {
135    /// `YYYY-MM-DD`.
136    pub day: String,
137    pub count: u32,
138}
139
140/// Every tool server a building is configured with, and every one the
141/// ledger saw used.
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
143#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
144pub struct McpUsageAnswer {
145    /// In server order; the line with no server comes last.
146    pub servers: Vec<McpServerUsage>,
147}
148
149/// One tool server and the tools of it that were used.
150#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
152pub struct McpServerUsage {
153    /// `None` gathers the calls no known server name prefixes: a server
154    /// removed before the ledger said whose tool it was.
155    pub server: Option<String>,
156    /// Whether some building's configuration names it now.
157    pub configured: bool,
158    /// The tools of it that were used, in tool order.
159    pub tools: Vec<McpToolUsage>,
160}
161
162/// One tool of a server.
163#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
164#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
165pub struct McpToolUsage {
166    /// The tool's name without the server prefix, or the whole name
167    /// under the line with no server.
168    pub tool: String,
169    pub uses: Vec<McpUse>,
170    pub per_day: Vec<DayCount>,
171}
172
173/// One call of a server's tool.
174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
175#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
176pub struct McpUse {
177    pub run: RunId,
178    pub resident: Option<Address>,
179    pub seq: Seq,
180    pub at: TimeMs,
181    pub outcome: UseOutcome,
182}
183
184/// Which table an export writes.
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
186#[serde(rename_all = "snake_case")]
187#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
188pub enum UsageKind {
189    Skills,
190    Mcp,
191}
192
193/// How an export is written.
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
195#[serde(rename_all = "snake_case")]
196#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
197pub enum ExportFormat {
198    Jsonl,
199    Csv,
200}
201
202/// One export: every use as one row, in the format asked for.
203#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
204#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
205pub struct UsageExportAnswer {
206    pub what: UsageKind,
207    pub format: ExportFormat,
208    /// UTF-8 text the page hands the User as a download.
209    pub body: String,
210}
211
212/// Every shell interpreter the ledger saw, with how its shell lines
213/// ended (`crates/wire/spec/Reading.lean` D48). The fold is
214/// `runtime::ShellTally`'s; this is its reading on the wire.
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
217pub struct ShellsAnswer {
218    /// In interpreter name order.
219    pub interpreters: Vec<ShellCalls>,
220}
221
222/// One interpreter's shell lines that ended with a code.
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
224#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
225pub struct ShellCalls {
226    /// The program that ran, as the result recorded it; a result written
227    /// before the field existed counts under `system`.
228    pub interpreter: String,
229    pub calls: u64,
230    /// Failures per class that occurred, keyed by
231    /// `runtime::FailureClass::as_str`.
232    pub failures: BTreeMap<String, u64>,
233}