Skip to main content

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