kernel/event/record/skill.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//! What shelving a skill and auditing it record
7//! (`crates/kernel/spec/Event/Record.lean` D23).
8//!
9//! The line is record-only: what an auditor said about a skill decides
10//! no byte of a model request. A skill's use is not recorded here; the
11//! `tool_called` lines that read it already say so.
12
13use serde::{Deserialize, Serialize};
14
15use crate::B3Hash;
16
17/// `skill_audited`: one audit of one version of a skill.
18///
19/// The digest binds the verdict to the bytes it judged: once the skill's
20/// content changes, its digest changes and this line no longer speaks
21/// for it.
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
24pub struct SkillAudited {
25 pub skill: String,
26 pub digest: B3Hash,
27 pub source: AuditSource,
28 /// The auditor's own version or name; for skills.sh, the partner,
29 /// such as `socket`.
30 pub scanner: String,
31 pub verdict: AuditVerdict,
32 /// The risk level in the auditor's own word.
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub risk: Option<String>,
35 /// When the auditor says it audited, in its own spelling.
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub audited_at: Option<String>,
38 /// Where a person reads the audit when the city could not fetch it.
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub link: Option<String>,
41}
42
43/// `skill_shelved`: one version of a skill landed on a shelf through the
44/// city's install door.
45///
46/// Written only when bytes actually landed: a reinstall of the same
47/// content changes nothing and writes nothing. `digest` is the value the
48/// audit binds to, so a version, its shelving and its audits meet on one
49/// digest.
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
52pub struct SkillShelved {
53 pub skill: String,
54 pub digest: B3Hash,
55 pub source: ShelvedFrom,
56}
57
58/// Where a shelved version came from.
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(tag = "kind", rename_all = "snake_case")]
61#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
62pub enum ShelvedFrom {
63 /// A directory or document on this machine.
64 Path,
65 /// A clone of an `https://` repository at a revision.
66 Git { url: String, rev: Option<String> },
67 /// A skill skills.sh names as `<owner>/<repo>/<skill>`.
68 SkillsSh { name: String },
69 /// One of the skills the binary carries.
70 Shipped,
71 /// Text written on the shelf page.
72 Page,
73}
74
75/// Who audited.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
79pub enum AuditSource {
80 /// The partner audits skills.sh publishes for a skill.
81 SkillsSh,
82 /// The SkillSpector scanner run on this machine.
83 SkillSpector,
84}
85
86/// What the audit concluded.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(rename_all = "snake_case")]
89#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
90pub enum AuditVerdict {
91 Pass,
92 Warn,
93 Fail,
94 /// An auditor applied and no audit could be had: skills.sh did not
95 /// answer or refused, or SkillSpector exited with neither verdict code.
96 /// Shelving goes ahead, and the audit state never counts this line as
97 /// an audit. When no auditor applies - a local or shipped skill and no
98 /// SkillSpector on the PATH - no line is written at all (city D19).
99 Unreachable,
100}