Compare commits
9 Commits
7f62c457ad
...
88cf6f366f
| Author | SHA1 | Date | |
|---|---|---|---|
| 88cf6f366f | |||
| 8b46b72a17 | |||
| 5f8b3d9a57 | |||
| 9be37ea123 | |||
| 8f987857e8 | |||
| 4830c3aba1 | |||
| 4beb1f61ea | |||
| dd509d40c6 | |||
| bf6915b43b |
|
|
@ -1,4 +1,4 @@
|
||||||
# Yoi
|
# 夜居 / Yoi agent
|
||||||
|
|
||||||
Yoi is an agent runtime for building, running, and orchestrating LLM Pods while preserving explicit history, scoped capabilities, and developer-controlled workflows.
|
Yoi is an agent runtime for building, running, and orchestrating LLM Pods while preserving explicit history, scoped capabilities, and developer-controlled workflows.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ pub use paths::user_profiles_path;
|
||||||
pub use profile::{
|
pub use profile::{
|
||||||
ProfileDiscovery, ProfileError, ProfileManifestSnapshot, ProfileMetadata, ProfileRegistry,
|
ProfileDiscovery, ProfileError, ProfileManifestSnapshot, ProfileMetadata, ProfileRegistry,
|
||||||
ProfileRegistryEntry, ProfileRegistrySource, ProfileResolveOptions, ProfileResolver,
|
ProfileRegistryEntry, ProfileRegistrySource, ProfileResolveOptions, ProfileResolver,
|
||||||
ProfileSelector, ProfileSource, ResolvedProfile, resolve_profile_artifact,
|
ProfileSelector, ProfileSource, ResolvedProfile, WorkspaceOverrideSnapshot,
|
||||||
|
resolve_profile_artifact,
|
||||||
};
|
};
|
||||||
pub use protocol::{Permission, ScopeRule};
|
pub use protocol::{Permission, ScopeRule};
|
||||||
pub use scope::{Scope, ScopeError, SharedScope};
|
pub use scope::{Scope, ScopeError, SharedScope};
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ const BUILTIN_DEFAULT_PROFILE_NAME: &str = "default";
|
||||||
const BUILTIN_DEFAULT_PROFILE: &str = include_str!("../../../resources/profiles/default.lua");
|
const BUILTIN_DEFAULT_PROFILE: &str = include_str!("../../../resources/profiles/default.lua");
|
||||||
const BUILTIN_MODEL_CATALOG: &str = include_str!("../../../resources/models/builtin.toml");
|
const BUILTIN_MODEL_CATALOG: &str = include_str!("../../../resources/models/builtin.toml");
|
||||||
const DEFAULT_POD_NAME: &str = "yoi";
|
const DEFAULT_POD_NAME: &str = "yoi";
|
||||||
|
const WORKSPACE_OVERRIDE_LOCAL_FILENAME: &str = "override.local.toml";
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
|
@ -340,6 +341,19 @@ pub struct ProfileManifestSnapshot {
|
||||||
pub source: ProfileSource,
|
pub source: ProfileSource,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub profile: Option<ProfileMetadata>,
|
pub profile: Option<ProfileMetadata>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub workspace_override: Option<WorkspaceOverrideSnapshot>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct WorkspaceOverrideSnapshot {
|
||||||
|
pub path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct WorkspaceOverrideLayer {
|
||||||
|
path: PathBuf,
|
||||||
|
config: PodManifestConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -475,6 +489,7 @@ impl ProfileResolver {
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or_else(|| Path::new(".")),
|
.unwrap_or_else(|| Path::new(".")),
|
||||||
)?;
|
)?;
|
||||||
|
let workspace_override = load_workspace_override_from(&workspace_base)?;
|
||||||
let lua_value = evaluate_lua_profile(&absolute_path, &profile_dir)?;
|
let lua_value = evaluate_lua_profile(&absolute_path, &profile_dir)?;
|
||||||
let raw_artifact = lua_value.clone();
|
let raw_artifact = lua_value.clone();
|
||||||
resolve_lua_profile_value(
|
resolve_lua_profile_value(
|
||||||
|
|
@ -484,6 +499,7 @@ impl ProfileResolver {
|
||||||
options,
|
options,
|
||||||
lua_value,
|
lua_value,
|
||||||
raw_artifact,
|
raw_artifact,
|
||||||
|
workspace_override,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -499,6 +515,7 @@ impl ProfileResolver {
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or_else(|| Path::new(".")),
|
.unwrap_or_else(|| Path::new(".")),
|
||||||
)?;
|
)?;
|
||||||
|
let workspace_override = load_workspace_override_from(&workspace_base)?;
|
||||||
let lua_value = evaluate_embedded_lua_profile(label, content)?;
|
let lua_value = evaluate_embedded_lua_profile(label, content)?;
|
||||||
let raw_artifact = lua_value.clone();
|
let raw_artifact = lua_value.clone();
|
||||||
resolve_lua_profile_value(
|
resolve_lua_profile_value(
|
||||||
|
|
@ -508,6 +525,7 @@ impl ProfileResolver {
|
||||||
options,
|
options,
|
||||||
lua_value,
|
lua_value,
|
||||||
raw_artifact,
|
raw_artifact,
|
||||||
|
workspace_override,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -519,6 +537,7 @@ fn resolve_lua_profile_value(
|
||||||
options: ProfileResolveOptions,
|
options: ProfileResolveOptions,
|
||||||
value: serde_json::Value,
|
value: serde_json::Value,
|
||||||
raw_artifact: serde_json::Value,
|
raw_artifact: serde_json::Value,
|
||||||
|
workspace_override: Option<WorkspaceOverrideLayer>,
|
||||||
) -> Result<ResolvedProfile, ProfileError> {
|
) -> Result<ResolvedProfile, ProfileError> {
|
||||||
if !workspace_base.is_absolute() {
|
if !workspace_base.is_absolute() {
|
||||||
return Err(ProfileError::InvalidPath {
|
return Err(ProfileError::InvalidPath {
|
||||||
|
|
@ -554,11 +573,28 @@ fn resolve_lua_profile_value(
|
||||||
memory: profile.memory,
|
memory: profile.memory,
|
||||||
skills: profile.skills,
|
skills: profile.skills,
|
||||||
};
|
};
|
||||||
let config = PodManifestConfig::builtin_defaults().merge(config.resolve_paths(profile_dir));
|
let mut config = PodManifestConfig::builtin_defaults().merge(config.resolve_paths(profile_dir));
|
||||||
|
let workspace_override_snapshot = if let Some(override_layer) = workspace_override {
|
||||||
|
let override_base =
|
||||||
|
override_layer
|
||||||
|
.path
|
||||||
|
.parent()
|
||||||
|
.ok_or_else(|| ProfileError::InvalidPath {
|
||||||
|
path: override_layer.path.clone(),
|
||||||
|
message: "workspace override path has no parent directory".into(),
|
||||||
|
})?;
|
||||||
|
config = config.merge(override_layer.config.resolve_paths(override_base));
|
||||||
|
Some(WorkspaceOverrideSnapshot {
|
||||||
|
path: override_layer.path,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let mut manifest = PodManifest::try_from(config).map_err(ProfileError::ManifestResolve)?;
|
let mut manifest = PodManifest::try_from(config).map_err(ProfileError::ManifestResolve)?;
|
||||||
manifest.profile = Some(ProfileManifestSnapshot {
|
manifest.profile = Some(ProfileManifestSnapshot {
|
||||||
source: source.clone(),
|
source: source.clone(),
|
||||||
profile: profile_meta.clone(),
|
profile: profile_meta.clone(),
|
||||||
|
workspace_override: workspace_override_snapshot,
|
||||||
});
|
});
|
||||||
let manifest_snapshot =
|
let manifest_snapshot =
|
||||||
serde_json::to_value(&manifest).map_err(ProfileError::SnapshotSerialize)?;
|
serde_json::to_value(&manifest).map_err(ProfileError::SnapshotSerialize)?;
|
||||||
|
|
@ -687,6 +723,54 @@ fn load_profile_registry_file(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_workspace_override_from(
|
||||||
|
workspace_base: &Path,
|
||||||
|
) -> Result<Option<WorkspaceOverrideLayer>, ProfileError> {
|
||||||
|
find_workspace_override_from(workspace_base)
|
||||||
|
.map(|path| load_workspace_override_file(&path))
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_workspace_override_file(path: &Path) -> Result<WorkspaceOverrideLayer, ProfileError> {
|
||||||
|
let content =
|
||||||
|
std::fs::read_to_string(path).map_err(|source| ProfileError::WorkspaceOverrideRead {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
source,
|
||||||
|
})?;
|
||||||
|
let config = PodManifestConfig::from_toml(&content).map_err(|source| {
|
||||||
|
ProfileError::WorkspaceOverrideParse {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
source,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
if config.pod.name.is_some() {
|
||||||
|
return Err(ProfileError::InvalidWorkspaceOverride {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
message: "workspace-local manifest overrides cannot set pod.name; Pod identity is a runtime input".into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(WorkspaceOverrideLayer {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_workspace_override_from(start: &Path) -> Option<PathBuf> {
|
||||||
|
let start = start
|
||||||
|
.canonicalize()
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_else(|| start.to_path_buf());
|
||||||
|
let mut cur: Option<&Path> = Some(start.as_path());
|
||||||
|
while let Some(dir) = cur {
|
||||||
|
let candidate = dir.join(".yoi").join(WORKSPACE_OVERRIDE_LOCAL_FILENAME);
|
||||||
|
if candidate.is_file() {
|
||||||
|
return Some(candidate);
|
||||||
|
}
|
||||||
|
cur = dir.parent();
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn find_project_profiles_from(start: &Path) -> Option<PathBuf> {
|
fn find_project_profiles_from(start: &Path) -> Option<PathBuf> {
|
||||||
let start = start
|
let start = start
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
|
|
@ -1214,6 +1298,7 @@ pub fn resolve_profile_artifact(
|
||||||
ProfileResolveOptions::default(),
|
ProfileResolveOptions::default(),
|
||||||
raw_artifact.clone(),
|
raw_artifact.clone(),
|
||||||
raw_artifact,
|
raw_artifact,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1241,6 +1326,20 @@ pub enum ProfileError {
|
||||||
#[source]
|
#[source]
|
||||||
source: toml::de::Error,
|
source: toml::de::Error,
|
||||||
},
|
},
|
||||||
|
#[error("failed to read workspace local manifest override {}: {source}", .path.display())]
|
||||||
|
WorkspaceOverrideRead {
|
||||||
|
path: PathBuf,
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
},
|
||||||
|
#[error("failed to parse workspace local manifest override {}: {source}", .path.display())]
|
||||||
|
WorkspaceOverrideParse {
|
||||||
|
path: PathBuf,
|
||||||
|
#[source]
|
||||||
|
source: toml::de::Error,
|
||||||
|
},
|
||||||
|
#[error("invalid workspace local manifest override {}: {message}", .path.display())]
|
||||||
|
InvalidWorkspaceOverride { path: PathBuf, message: String },
|
||||||
#[error("no default profile is configured")]
|
#[error("no default profile is configured")]
|
||||||
NoDefaultProfile,
|
NoDefaultProfile,
|
||||||
#[error("profile not found: {selector}")]
|
#[error("profile not found: {selector}")]
|
||||||
|
|
@ -1498,6 +1597,125 @@ return profile {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn workspace_local_override_layers_over_profile_defaults() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
let workspace = tmp.path().join("project");
|
||||||
|
let nested = workspace.join("nested");
|
||||||
|
let yoi_dir = workspace.join(".yoi");
|
||||||
|
std::fs::create_dir_all(&nested).unwrap();
|
||||||
|
std::fs::create_dir_all(&yoi_dir).unwrap();
|
||||||
|
let override_path = yoi_dir.join(WORKSPACE_OVERRIDE_LOCAL_FILENAME);
|
||||||
|
std::fs::write(
|
||||||
|
&override_path,
|
||||||
|
r#"
|
||||||
|
[pod]
|
||||||
|
prompt_pack = "prompts.toml"
|
||||||
|
[worker]
|
||||||
|
language = "ja"
|
||||||
|
[session]
|
||||||
|
record_event_trace = false
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let resolved = ProfileResolver::new()
|
||||||
|
.with_workspace_base(&nested)
|
||||||
|
.resolve(&ProfileSelector::Default, ProfileResolveOptions::default())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(resolved.manifest.pod.name, "yoi");
|
||||||
|
assert_eq!(resolved.manifest.worker.language, "ja");
|
||||||
|
assert!(!resolved.manifest.session.record_event_trace);
|
||||||
|
assert_eq!(
|
||||||
|
resolved.manifest.pod.prompt_pack.as_deref(),
|
||||||
|
Some(yoi_dir.join("prompts.toml").as_path())
|
||||||
|
);
|
||||||
|
assert_eq!(resolved.manifest.scope.allow[0].target, nested);
|
||||||
|
assert_eq!(
|
||||||
|
resolved
|
||||||
|
.manifest
|
||||||
|
.profile
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|snapshot| snapshot.workspace_override.as_ref())
|
||||||
|
.map(|snapshot| snapshot.path.as_path()),
|
||||||
|
Some(override_path.as_path())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn workspace_local_override_uses_nearest_ancestor() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
let workspace = tmp.path().join("project");
|
||||||
|
let nested = workspace.join("nested");
|
||||||
|
let child = nested.join("child");
|
||||||
|
let parent_yoi = workspace.join(".yoi");
|
||||||
|
let nested_yoi = nested.join(".yoi");
|
||||||
|
std::fs::create_dir_all(&child).unwrap();
|
||||||
|
std::fs::create_dir_all(&parent_yoi).unwrap();
|
||||||
|
std::fs::create_dir_all(&nested_yoi).unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
parent_yoi.join(WORKSPACE_OVERRIDE_LOCAL_FILENAME),
|
||||||
|
r#"
|
||||||
|
[pod]
|
||||||
|
prompt_pack = "parent-prompts.toml"
|
||||||
|
[worker]
|
||||||
|
language = "parent"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let nested_override_path = nested_yoi.join(WORKSPACE_OVERRIDE_LOCAL_FILENAME);
|
||||||
|
std::fs::write(
|
||||||
|
&nested_override_path,
|
||||||
|
r#"
|
||||||
|
[pod]
|
||||||
|
prompt_pack = "nested-prompts.toml"
|
||||||
|
[worker]
|
||||||
|
language = "nested"
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let resolved = ProfileResolver::new()
|
||||||
|
.with_workspace_base(&child)
|
||||||
|
.resolve(&ProfileSelector::Default, ProfileResolveOptions::default())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(resolved.manifest.worker.language, "nested");
|
||||||
|
assert_eq!(
|
||||||
|
resolved.manifest.pod.prompt_pack.as_deref(),
|
||||||
|
Some(nested_yoi.join("nested-prompts.toml").as_path())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resolved
|
||||||
|
.manifest
|
||||||
|
.profile
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|snapshot| snapshot.workspace_override.as_ref())
|
||||||
|
.map(|snapshot| snapshot.path.as_path()),
|
||||||
|
Some(nested_override_path.as_path())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn workspace_local_override_rejects_runtime_pod_name() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
let yoi_dir = tmp.path().join(".yoi");
|
||||||
|
std::fs::create_dir_all(&yoi_dir).unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
yoi_dir.join(WORKSPACE_OVERRIDE_LOCAL_FILENAME),
|
||||||
|
"[pod]\nname = \"not-local\"\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let err = ProfileResolver::new()
|
||||||
|
.with_workspace_base(tmp.path())
|
||||||
|
.resolve(&ProfileSelector::Default, ProfileResolveOptions::default())
|
||||||
|
.unwrap_err();
|
||||||
|
assert!(matches!(err, ProfileError::InvalidWorkspaceOverride { .. }));
|
||||||
|
assert!(err.to_string().contains("pod.name"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unsupported_profile_extension_has_clear_diagnostic() {
|
fn unsupported_profile_extension_has_clear_diagnostic() {
|
||||||
let tmp = TempDir::new().unwrap();
|
let tmp = TempDir::new().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -485,6 +485,51 @@ permission = "write"
|
||||||
assert!(loader.workspace_dir().is_none());
|
assert!(loader.workspace_dir().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn manifest_mode_does_not_apply_workspace_local_override() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
let yoi_dir = tmp.path().join(".yoi");
|
||||||
|
std::fs::create_dir_all(&yoi_dir).unwrap();
|
||||||
|
write(
|
||||||
|
&yoi_dir.join("override.local.toml"),
|
||||||
|
r#"
|
||||||
|
[pod]
|
||||||
|
name = "from-local-override"
|
||||||
|
[worker]
|
||||||
|
language = "override"
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
let manifest_path = tmp.path().join("manifest.toml");
|
||||||
|
write(
|
||||||
|
&manifest_path,
|
||||||
|
&format!(
|
||||||
|
r#"
|
||||||
|
[pod]
|
||||||
|
name = "from-single-file"
|
||||||
|
|
||||||
|
[model]
|
||||||
|
scheme = "anthropic"
|
||||||
|
model_id = "test-model"
|
||||||
|
|
||||||
|
[worker]
|
||||||
|
language = "manifest"
|
||||||
|
|
||||||
|
[[scope.allow]]
|
||||||
|
target = "{}"
|
||||||
|
permission = "write"
|
||||||
|
"#,
|
||||||
|
tmp.path().display()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let cli = Cli::try_parse_from(["yoi pod", "--manifest", manifest_path.to_str().unwrap()])
|
||||||
|
.unwrap();
|
||||||
|
let (manifest, _loader) = resolve_manifest(&cli).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manifest.pod.name, "from-single-file");
|
||||||
|
assert_eq!(manifest.worker.language, "manifest");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn profile_uses_selected_profile() {
|
fn profile_uses_selected_profile() {
|
||||||
let tmp = TempDir::new().unwrap();
|
let tmp = TempDir::new().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ Source/partial layers may omit fields. Resolved manifests should be explicit eno
|
||||||
|
|
||||||
`--manifest <path>` exists as an explicit low-level escape hatch. Normal fresh startup should select a Profile through `profiles.toml` / builtin defaults rather than ambient manifest cascades.
|
`--manifest <path>` exists as an explicit low-level escape hatch. Normal fresh startup should select a Profile through `profiles.toml` / builtin defaults rather than ambient manifest cascades.
|
||||||
|
|
||||||
|
For normal Profile/default startup, a workspace may add `.yoi/override.local.toml` as a final local manifest layer. Yoi discovers the nearest ancestor `.yoi/override.local.toml` from the workspace base used for profile resolution, resolves relative paths in that file against its containing `.yoi` directory, and applies it after the selected Profile and builtin defaults. This file is intended for machine-local choices such as provider/model, worker language, prompt pack, and permission policy tweaks; it is ignored by git via the repository `*.local.*` rule. It is not applied in explicit `--manifest <path>` mode, and it cannot set `pod.name` because Pod identity remains a runtime input.
|
||||||
|
|
||||||
## Spawned Pods
|
## Spawned Pods
|
||||||
|
|
||||||
`SpawnPod.profile` is optional and resolves through defaults when omitted. The only concrete capability delegation in the tool call is `SpawnPod.scope`, and it must be a subset of the parent's effective scope.
|
`SpawnPod.profile` is optional and resolves through defaults when omitted. The only concrete capability delegation in the tool call is `SpawnPod.scope`, and it must be a subset of the parent's effective scope.
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
id: 20260601-110026-crate-readme-boundaries
|
id: 20260601-110026-crate-readme-boundaries
|
||||||
slug: crate-readme-boundaries
|
slug: crate-readme-boundaries
|
||||||
title: Standardize crate README responsibility boundaries
|
title: Standardize crate README responsibility boundaries
|
||||||
status: open
|
status: closed
|
||||||
kind: task
|
kind: task
|
||||||
priority: P2
|
priority: P2
|
||||||
labels: [docs]
|
labels: [docs]
|
||||||
created_at: 2026-06-01T11:00:26Z
|
created_at: 2026-06-01T11:00:26Z
|
||||||
updated_at: 2026-06-01T11:09:35Z
|
updated_at: 2026-06-01T13:22:51Z
|
||||||
assignee: null
|
assignee: null
|
||||||
legacy_ticket: null
|
legacy_ticket: null
|
||||||
---
|
---
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Completed crate README boundary cleanup: all crates now have concise role/boundary README files linked to maintained design/development docs; validation passed.
|
||||||
|
|
@ -45,4 +45,13 @@ Standardized crate README files as thin responsibility-boundary documents.
|
||||||
- `git diff --check` => passed
|
- `git diff --check` => passed
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: close author: hare at: 2026-06-01T13:22:51Z status: closed -->
|
||||||
|
|
||||||
|
## Closed
|
||||||
|
|
||||||
|
Completed crate README boundary cleanup: all crates now have concise role/boundary README files linked to maintained design/development docs; validation passed.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
id: 20260601-110026-docs-information-architecture
|
id: 20260601-110026-docs-information-architecture
|
||||||
slug: docs-information-architecture
|
slug: docs-information-architecture
|
||||||
title: Reorganize documentation information architecture
|
title: Reorganize documentation information architecture
|
||||||
status: open
|
status: closed
|
||||||
kind: task
|
kind: task
|
||||||
priority: P2
|
priority: P2
|
||||||
labels: [docs]
|
labels: [docs]
|
||||||
created_at: 2026-06-01T11:00:26Z
|
created_at: 2026-06-01T11:00:26Z
|
||||||
updated_at: 2026-06-01T11:09:35Z
|
updated_at: 2026-06-01T13:22:50Z
|
||||||
assignee: null
|
assignee: null
|
||||||
legacy_ticket: null
|
legacy_ticket: null
|
||||||
---
|
---
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Completed documentation information architecture cleanup: root and docs README now define maintained docs surface; design/development docs added; old plan/ref/research/top-level docs moved out of maintained surface; validation passed.
|
||||||
|
|
@ -44,4 +44,13 @@ Reorganized maintained repository documentation around current Yoi design intent
|
||||||
- stale-surface grep over `README.md`, maintained `docs/`, and crate READMEs excluding `docs/report/` and `docs/.local/` => no output
|
- stale-surface grep over `README.md`, maintained `docs/`, and crate READMEs excluding `docs/report/` and `docs/.local/` => no output
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: close author: hare at: 2026-06-01T13:22:50Z status: closed -->
|
||||||
|
|
||||||
|
## Closed
|
||||||
|
|
||||||
|
Completed documentation information architecture cleanup: root and docs README now define maintained docs surface; design/development docs added; old plan/ref/research/top-level docs moved out of maintained surface; validation passed.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Delegation intent: workspace-local manifest override
|
||||||
|
|
||||||
|
Intent:
|
||||||
|
- Implement a workspace-local manifest override file that overlays Profile-derived normal startup configuration.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Support `.yoi/override.local.toml` as the local ignored override file.
|
||||||
|
- Detect the nearest workspace override relative to the active workspace/project base used for profile discovery.
|
||||||
|
- Apply the override as a `PodManifestConfig` layer after Profile resolution and before final `PodManifest` validation/snapshot persistence.
|
||||||
|
- Preserve explicit `--manifest <path>` as a one-file escape hatch; do not apply workspace overrides in explicit manifest mode unless you stop and report a strong reason.
|
||||||
|
- Resolve relative paths in the override file from `.yoi/` / the override file parent directory.
|
||||||
|
- Add provenance/diagnostic information where the existing manifest/profile resolution surfaces can carry it without broad refactoring.
|
||||||
|
- Add focused tests for discovery, merge order, path base, and explicit-manifest behavior.
|
||||||
|
- Update docs only if the behavior is durable and useful to document.
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
- Do not reintroduce ambient `.yoi/manifest.toml` cascade as normal startup.
|
||||||
|
- Do not put runtime-bound fields into reusable Profiles.
|
||||||
|
- Keep explicit manifest mode low-level and predictable.
|
||||||
|
- Do not read ignored secret-like file contents.
|
||||||
|
- Do not edit unrelated tickets or parent workspace files.
|
||||||
|
|
||||||
|
Non-goals:
|
||||||
|
- Do not support multiple cascading override files unless the existing workspace discovery naturally requires it.
|
||||||
|
- Do not implement a new profile language.
|
||||||
|
- Do not change dependency versions.
|
||||||
|
- Do not close the ticket or merge the worktree.
|
||||||
|
|
||||||
|
Escalate if:
|
||||||
|
- The correct workspace base is ambiguous between TUI launch, `yoi pod`, and `SpawnPod`.
|
||||||
|
- Provenance support requires changing persisted manifest schema in a broad way.
|
||||||
|
- Supporting `.yoi/override.toml` as trackable state seems necessary.
|
||||||
|
- Tests require real spawned process E2E coverage.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- Run focused manifest/profile tests, preferably `cargo test -p manifest` plus any touched `pod` tests.
|
||||||
|
- Run `./tickets.sh doctor`, `git diff --check`, and `nix build .#yoi` if feasible in the worktree.
|
||||||
|
- Record any skipped validation with rationale.
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Implementation report
|
||||||
|
|
||||||
|
Implemented workspace-local manifest override support.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
- Normal Profile/default resolution now searches upward from the resolver workspace base for the nearest `.yoi/override.local.toml`.
|
||||||
|
- The override is parsed as a `PodManifestConfig` layer, resolved relative to its parent `.yoi/` directory, and merged after builtin defaults plus the selected Profile but before final `PodManifest` validation and snapshot serialization.
|
||||||
|
- Resolved profile provenance now records the applied override path in `manifest.profile.workspace_override`.
|
||||||
|
- Explicit `--manifest <path>` mode remains a single-file escape hatch and does not apply workspace-local overrides.
|
||||||
|
- Workspace-local overrides are rejected if they set `pod.name`, keeping Pod identity runtime-bound.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- `cargo test -p manifest workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p pod manifest_mode_does_not_apply_workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p manifest -p pod`
|
||||||
|
- `./tickets.sh doctor`
|
||||||
|
- `git diff --check`
|
||||||
|
- `nix build .#yoi`
|
||||||
|
|
||||||
|
Reviewer follow-up validation:
|
||||||
|
|
||||||
|
- `cargo test -p manifest workspace_local_override -- --nocapture`
|
||||||
|
- `./tickets.sh doctor`
|
||||||
|
- `git diff --check`
|
||||||
|
|
||||||
|
All completed successfully.
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Review: workspace-local manifest override
|
||||||
|
|
||||||
|
Reviewer Pod: `workspace-override-reviewer-20260601`
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
Approved. No blockers found.
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
The implementation satisfies the intent:
|
||||||
|
|
||||||
|
- `.yoi/override.local.toml` is discovered as the workspace-local override.
|
||||||
|
- The override applies only to normal Profile/default resolution.
|
||||||
|
- Explicit `--manifest <path>` remains a single-file escape hatch and does not load the workspace override.
|
||||||
|
- Merge order is builtin/default + selected Profile, then workspace override, then final `PodManifest` validation.
|
||||||
|
- Relative paths in the override are resolved from the override file parent `.yoi/` directory.
|
||||||
|
- `.yoi/manifest.toml` cascade was not reintroduced.
|
||||||
|
- Overrides that set `pod.name` are rejected.
|
||||||
|
- Provenance is recorded narrowly through `manifest.profile.workspace_override`.
|
||||||
|
|
||||||
|
## Follow-up handled
|
||||||
|
|
||||||
|
Reviewer requested non-blocking coverage for the case where both parent and nested `.yoi/override.local.toml` exist. Coder added commit `8f98785 test: cover nearest workspace override`, which verifies the nearest nested override wins and checks provenance.
|
||||||
|
|
||||||
|
## Validation evidence
|
||||||
|
|
||||||
|
Coder reported:
|
||||||
|
|
||||||
|
- `cargo test -p manifest workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p pod manifest_mode_does_not_apply_workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p manifest -p pod`
|
||||||
|
- `./tickets.sh doctor`
|
||||||
|
- `git diff --check`
|
||||||
|
- `nix build .#yoi`
|
||||||
|
|
||||||
|
Parent/orchestrator reran after merge:
|
||||||
|
|
||||||
|
- `cargo test -p manifest workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p pod manifest_mode_does_not_apply_workspace_local_override -- --nocapture`
|
||||||
|
- `./tickets.sh doctor`
|
||||||
|
- `git diff --check`
|
||||||
|
- `nix build .#yoi`
|
||||||
|
- `./result/bin/yoi pod --help`
|
||||||
|
|
||||||
|
## Residual risk
|
||||||
|
|
||||||
|
`ProfileResolver::resolve()` still discovers named/default profiles from process cwd while `with_workspace_base(...)` controls scope and override discovery. This was judged non-blocking for the intended CLI/Profile/default/SpawnPod paths, but future callers should avoid assuming `with_workspace_base` also binds registry discovery.
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
id: 20260601-125240-workspace-local-manifest-override
|
||||||
|
slug: workspace-local-manifest-override
|
||||||
|
title: Support workspace-local manifest override layer
|
||||||
|
status: closed
|
||||||
|
kind: task
|
||||||
|
priority: P2
|
||||||
|
labels: [profile, manifest, config]
|
||||||
|
created_at: 2026-06-01T12:52:40Z
|
||||||
|
updated_at: 2026-06-01T13:19:36Z
|
||||||
|
assignee: null
|
||||||
|
legacy_ticket: null
|
||||||
|
---
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Workspace profiles are already discoverable through the nearest `.yoi/profiles.toml`, but local machine/workspace overrides currently require either changing profile inputs or using an explicit `--manifest` escape hatch. This makes it awkward to keep private local settings such as model choice, reasoning level, web/search enablement, or temporary scope adjustments out of reusable Profiles.
|
||||||
|
|
||||||
|
Add a workspace-local manifest override layer that is detected from the workspace `.yoi` directory and applied after Profile resolution. The intended file name is `.yoi/override.local.toml`; `.yoi/override.toml` can be considered only if it is intentionally trackable and the semantics are documented clearly.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Detect the nearest workspace-local override file while resolving a Profile/default startup path.
|
||||||
|
- Apply the override as a `PodManifestConfig` layer on top of the resolved Profile-generated manifest, before final `PodManifest` validation/snapshot persistence.
|
||||||
|
- Preserve the existing explicit `--manifest <path>` low-level escape hatch semantics; do not silently merge the workspace override into explicit manifest mode unless the implementation intentionally documents and tests that behavior.
|
||||||
|
- Resolve relative paths in the override file from the override file's parent directory.
|
||||||
|
- Record override provenance in the resolved manifest snapshot or diagnostics so local behavior is explainable.
|
||||||
|
- Prefer `.yoi/override.local.toml` as ignored/local state. If `.yoi/override.toml` is supported, define precedence and intended tracking policy.
|
||||||
|
- Do not reintroduce ambient `manifest.toml` cascade as the normal startup layer.
|
||||||
|
- Add focused tests for discovery, merge order, path base, and explicit-manifest behavior.
|
||||||
|
- Update design/development docs only where this is a durable configuration boundary.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- A workspace `.yoi/override.local.toml` can override Profile-derived manifest settings during normal Profile/default startup.
|
||||||
|
- Override merge order is documented and tested: builtin/default/user/project Profile resolution first, workspace-local override second, final validation last.
|
||||||
|
- `--manifest <path>` behavior is tested and either remains override-free or has explicitly documented override semantics.
|
||||||
|
- `.gitignore` / docs clarify whether `.yoi/override.local.toml` is local-only.
|
||||||
|
- Validation includes relevant manifest/profile tests, `cargo test -p manifest` or narrower equivalent, `./tickets.sh doctor`, `git diff --check`, and `nix build .#yoi` unless intentionally deferred with rationale.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Implemented workspace-local .yoi/override.local.toml as an ignored manifest override layer for normal Profile/default startup. Explicit --manifest remains override-free; focused tests and nix build passed.
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
<!-- event: create author: tickets.sh at: 2026-06-01T12:52:40Z -->
|
||||||
|
|
||||||
|
## Created
|
||||||
|
|
||||||
|
Created by tickets.sh create.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: plan author: hare at: 2026-06-01T12:53:58Z -->
|
||||||
|
|
||||||
|
## Plan
|
||||||
|
|
||||||
|
# Delegation intent: workspace-local manifest override
|
||||||
|
|
||||||
|
Intent:
|
||||||
|
- Implement a workspace-local manifest override file that overlays Profile-derived normal startup configuration.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Support `.yoi/override.local.toml` as the local ignored override file.
|
||||||
|
- Detect the nearest workspace override relative to the active workspace/project base used for profile discovery.
|
||||||
|
- Apply the override as a `PodManifestConfig` layer after Profile resolution and before final `PodManifest` validation/snapshot persistence.
|
||||||
|
- Preserve explicit `--manifest <path>` as a one-file escape hatch; do not apply workspace overrides in explicit manifest mode unless you stop and report a strong reason.
|
||||||
|
- Resolve relative paths in the override file from `.yoi/` / the override file parent directory.
|
||||||
|
- Add provenance/diagnostic information where the existing manifest/profile resolution surfaces can carry it without broad refactoring.
|
||||||
|
- Add focused tests for discovery, merge order, path base, and explicit-manifest behavior.
|
||||||
|
- Update docs only if the behavior is durable and useful to document.
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
- Do not reintroduce ambient `.yoi/manifest.toml` cascade as normal startup.
|
||||||
|
- Do not put runtime-bound fields into reusable Profiles.
|
||||||
|
- Keep explicit manifest mode low-level and predictable.
|
||||||
|
- Do not read ignored secret-like file contents.
|
||||||
|
- Do not edit unrelated tickets or parent workspace files.
|
||||||
|
|
||||||
|
Non-goals:
|
||||||
|
- Do not support multiple cascading override files unless the existing workspace discovery naturally requires it.
|
||||||
|
- Do not implement a new profile language.
|
||||||
|
- Do not change dependency versions.
|
||||||
|
- Do not close the ticket or merge the worktree.
|
||||||
|
|
||||||
|
Escalate if:
|
||||||
|
- The correct workspace base is ambiguous between TUI launch, `yoi pod`, and `SpawnPod`.
|
||||||
|
- Provenance support requires changing persisted manifest schema in a broad way.
|
||||||
|
- Supporting `.yoi/override.toml` as trackable state seems necessary.
|
||||||
|
- Tests require real spawned process E2E coverage.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- Run focused manifest/profile tests, preferably `cargo test -p manifest` plus any touched `pod` tests.
|
||||||
|
- Run `./tickets.sh doctor`, `git diff --check`, and `nix build .#yoi` if feasible in the worktree.
|
||||||
|
- Record any skipped validation with rationale.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: implementation_report author: hare at: 2026-06-01T13:05:58Z -->
|
||||||
|
|
||||||
|
## Implementation report
|
||||||
|
|
||||||
|
# Implementation report
|
||||||
|
|
||||||
|
Implemented workspace-local manifest override support.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
- Normal Profile/default resolution now searches upward from the resolver workspace base for the nearest `.yoi/override.local.toml`.
|
||||||
|
- The override is parsed as a `PodManifestConfig` layer, resolved relative to its parent `.yoi/` directory, and merged after builtin defaults plus the selected Profile but before final `PodManifest` validation and snapshot serialization.
|
||||||
|
- Resolved profile provenance now records the applied override path in `manifest.profile.workspace_override`.
|
||||||
|
- Explicit `--manifest <path>` mode remains a single-file escape hatch and does not apply workspace-local overrides.
|
||||||
|
- Workspace-local overrides are rejected if they set `pod.name`, keeping Pod identity runtime-bound.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- `cargo test -p manifest workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p pod manifest_mode_does_not_apply_workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p manifest -p pod`
|
||||||
|
- `./tickets.sh doctor`
|
||||||
|
- `git diff --check`
|
||||||
|
- `nix build .#yoi`
|
||||||
|
|
||||||
|
All completed successfully.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: review author: hare at: 2026-06-01T13:19:35Z status: approve -->
|
||||||
|
|
||||||
|
## Review: approve
|
||||||
|
|
||||||
|
# Review: workspace-local manifest override
|
||||||
|
|
||||||
|
Reviewer Pod: `workspace-override-reviewer-20260601`
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
Approved. No blockers found.
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
The implementation satisfies the intent:
|
||||||
|
|
||||||
|
- `.yoi/override.local.toml` is discovered as the workspace-local override.
|
||||||
|
- The override applies only to normal Profile/default resolution.
|
||||||
|
- Explicit `--manifest <path>` remains a single-file escape hatch and does not load the workspace override.
|
||||||
|
- Merge order is builtin/default + selected Profile, then workspace override, then final `PodManifest` validation.
|
||||||
|
- Relative paths in the override are resolved from the override file parent `.yoi/` directory.
|
||||||
|
- `.yoi/manifest.toml` cascade was not reintroduced.
|
||||||
|
- Overrides that set `pod.name` are rejected.
|
||||||
|
- Provenance is recorded narrowly through `manifest.profile.workspace_override`.
|
||||||
|
|
||||||
|
## Follow-up handled
|
||||||
|
|
||||||
|
Reviewer requested non-blocking coverage for the case where both parent and nested `.yoi/override.local.toml` exist. Coder added commit `8f98785 test: cover nearest workspace override`, which verifies the nearest nested override wins and checks provenance.
|
||||||
|
|
||||||
|
## Validation evidence
|
||||||
|
|
||||||
|
Coder reported:
|
||||||
|
|
||||||
|
- `cargo test -p manifest workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p pod manifest_mode_does_not_apply_workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p manifest -p pod`
|
||||||
|
- `./tickets.sh doctor`
|
||||||
|
- `git diff --check`
|
||||||
|
- `nix build .#yoi`
|
||||||
|
|
||||||
|
Parent/orchestrator reran after merge:
|
||||||
|
|
||||||
|
- `cargo test -p manifest workspace_local_override -- --nocapture`
|
||||||
|
- `cargo test -p pod manifest_mode_does_not_apply_workspace_local_override -- --nocapture`
|
||||||
|
- `./tickets.sh doctor`
|
||||||
|
- `git diff --check`
|
||||||
|
- `nix build .#yoi`
|
||||||
|
- `./result/bin/yoi pod --help`
|
||||||
|
|
||||||
|
## Residual risk
|
||||||
|
|
||||||
|
`ProfileResolver::resolve()` still discovers named/default profiles from process cwd while `with_workspace_base(...)` controls scope and override discovery. This was judged non-blocking for the intended CLI/Profile/default/SpawnPod paths, but future callers should avoid assuming `with_workspace_base` also binds registry discovery.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: close author: hare at: 2026-06-01T13:19:36Z status: closed -->
|
||||||
|
|
||||||
|
## Closed
|
||||||
|
|
||||||
|
Implemented workspace-local .yoi/override.local.toml as an ignored manifest override layer for normal Profile/default startup. Explicit --manifest remains override-free; focused tests and nix build passed.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
@ -0,0 +1,364 @@
|
||||||
|
# Dependency/license audit report
|
||||||
|
|
||||||
|
Date: 2026-06-01
|
||||||
|
|
||||||
|
Scope: read-mostly dependency and license audit for the Yoi workspace at `/home/hare/Projects/yoi`, per `artifacts/delegation-intent.md`. I did not modify dependency manifests, source code, lockfiles, docs, or ticket files other than this report artifact. I did not read ignored secret-like file contents.
|
||||||
|
|
||||||
|
## Executive summary
|
||||||
|
|
||||||
|
No dependency-license incompatibility blocker was identified from the available local metadata. The main release-risk gap is process/packaging: there is no checked-in dependency license policy or generated third-party notices artifact, so a public binary/source release should add one before publication if notices are expected to ship with the release.
|
||||||
|
|
||||||
|
The clearest cleanup candidates are non-blocking: normalize `reqwest` TLS features so Yoi does not enable both native OpenSSL TLS and rustls-related TLS paths, align the direct `crossterm` version with `ratatui`'s backend dependency, and periodically review the HTML/YAML parsing stacks for weight/maintenance.
|
||||||
|
|
||||||
|
## Methodology and commands used
|
||||||
|
|
||||||
|
Evidence came from local manifests, lock/metadata commands, dependency trees, and source usage greps. Commands were read-only except for writing this report.
|
||||||
|
|
||||||
|
File reads:
|
||||||
|
|
||||||
|
- `artifacts/delegation-intent.md`
|
||||||
|
- root `Cargo.toml`, `Cargo.lock`, workspace crate `Cargo.toml` files under `crates/*/Cargo.toml`
|
||||||
|
- `LICENSE`
|
||||||
|
- `flake.nix`, `package.nix`, `devshell.nix`
|
||||||
|
|
||||||
|
Inventory and license commands:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /home/hare/Projects/yoi
|
||||||
|
cargo metadata --locked --format-version 1
|
||||||
|
cargo metadata --locked --format-version 1 | jq -r '...direct workspace dependency grouping...'
|
||||||
|
cargo metadata --locked --format-version 1 | jq -r '...license field grouping and concerning-license filters...'
|
||||||
|
cargo deny check licenses
|
||||||
|
cargo deny --locked --offline list -f tsv
|
||||||
|
cargo deny --locked --offline --all-features list -f tsv
|
||||||
|
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...license counts...'
|
||||||
|
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...non-standard/copyleft/notice-relevant license packages...'
|
||||||
|
cargo tree --locked -e features -i reqwest@0.13.2
|
||||||
|
cargo tree --locked -i openssl-sys@0.9.112 --all-features
|
||||||
|
cargo tree --locked -i native-tls@0.2.18 --all-features
|
||||||
|
cargo tree --locked -i rustls@0.23.37 --all-features
|
||||||
|
cargo tree --locked --duplicates --all-features
|
||||||
|
cargo tree --locked -e no-dev --prefix none | sort -u | wc -l
|
||||||
|
cargo tree --locked -e no-dev --duplicates
|
||||||
|
```
|
||||||
|
|
||||||
|
Source usage checks:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rg 'use reqwest|reqwest::|ClientBuilder|Client::builder' crates/**/*.rs
|
||||||
|
rg 'html5ever|markup5ever|RcDom|parse_document' crates/**/*.rs
|
||||||
|
rg 'serde_yaml|frontmatter|yaml|YAML' crates/**/*.rs
|
||||||
|
rg 'zstd|encode_all|decode_all' crates/**/*.rs
|
||||||
|
rg 'mlua::|Lua::|LuaSerdeExt|require\(' crates/**/*.rs
|
||||||
|
rg 'crossterm::|ratatui::' crates/**/*.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
Fallbacks / tool notes:
|
||||||
|
|
||||||
|
- `python3` was unavailable in the audit environment, so JSON processing used `jq` and `awk`.
|
||||||
|
- `cargo deny check licenses` exited non-zero because no project license policy/config is present; I used `cargo deny ... list -f tsv` as a local metadata fallback rather than treating the policy failure itself as license evidence.
|
||||||
|
- No web lookup was used. License conclusions are therefore limited to local crates.io metadata / cargo-deny parsing and local manifests.
|
||||||
|
|
||||||
|
## Workspace/dependency shape
|
||||||
|
|
||||||
|
- Root workspace: 19 crates, workspace package license `MIT`.
|
||||||
|
- Project license file: `LICENSE` is MIT.
|
||||||
|
- `cargo metadata --locked --format-version 1` reported 486 package records in the resolved metadata set.
|
||||||
|
- `cargo tree --locked -e no-dev --prefix none | sort -u | wc -l` reported 404 unique non-dev tree lines.
|
||||||
|
- Direct external dependencies found across workspace manifests: 45 unique package names including dev/build-only dependencies.
|
||||||
|
|
||||||
|
## Direct Rust dependencies and rough purpose notes
|
||||||
|
|
||||||
|
### Runtime/build dependencies
|
||||||
|
|
||||||
|
| Dependency | Direct users | Rough purpose / usage note |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `arc-swap` | `manifest`, `pod` | Shared mutable configuration/state handles. |
|
||||||
|
| `async-trait` | `llm-worker`, `memory`, `pod`, `provider`, `tools` | Async trait object ergonomics for tool/provider abstractions. |
|
||||||
|
| `base64` | `provider` | Codex/OAuth or provider token/body encoding helpers. |
|
||||||
|
| `chrono` | `lint-common`, `memory`, `pod`, `provider`, `workflow` | Timestamps, serde timestamps, workflow/memory metadata. |
|
||||||
|
| `clap` | `pod` runtime; `llm-worker` dev | CLI parsing. |
|
||||||
|
| `crossterm` | `tui` | Terminal input/output/events. Direct version is `0.28`; `ratatui` pulls `0.29` transitively. |
|
||||||
|
| `eventsource-stream` | `llm-worker` | SSE stream parsing for LLM/provider responses. |
|
||||||
|
| `fs4` | `pod`, `pod-registry` | Cross-process file locking. |
|
||||||
|
| `futures` | `llm-worker`; dev in `pod`, `session-store` | Stream/future helpers. |
|
||||||
|
| `globset`, `ignore`, `grep-matcher`, `grep-regex`, `grep-searcher` | `tools` | Local Glob/Grep tool implementation, gitignore-aware search. |
|
||||||
|
| `html5ever`, `markup5ever_rcdom` | `tools` | WebFetch HTML parsing and Readability-style extraction (`tools/src/web.rs`). |
|
||||||
|
| `include_dir` | `pod` | Compile-time embedding of prompt/profile/runtime resources. |
|
||||||
|
| `libc` | `memory`, `pod`, `pod-registry` | Unix process/permission/runtime details. |
|
||||||
|
| `minijinja` | `pod` | Prompt/template rendering. |
|
||||||
|
| `mlua` | `manifest` | Lua profile evaluation with vendored Lua 5.4 and serde integration. |
|
||||||
|
| `proc-macro2`, `quote`, `syn` | `llm-worker-macros` | Procedural macro implementation. |
|
||||||
|
| `pulldown-cmark` | `tui` | Markdown rendering/parsing in terminal UI. |
|
||||||
|
| `ratatui`, `unicode-width` | `tui` | Terminal UI rendering and width calculations. |
|
||||||
|
| `reqwest` | `llm-worker`, `provider`, `tools` | HTTP client for LLM transport, OAuth refresh, WebSearch/WebFetch. |
|
||||||
|
| `schemars` | `memory`, `pod`, `tools`; `llm-worker` dev | JSON schema generation for tools/config/test surfaces. |
|
||||||
|
| `serde`, `serde_json`, `serde_ignored`, `toml` | many crates | Serialization and config/profile parsing. |
|
||||||
|
| `serde_yaml` | `memory`, `workflow` | YAML frontmatter parsing for memory/workflow/skill documents. |
|
||||||
|
| `sha2` | `memory`, `secrets`, `tools` | Hashing/audit/secret-integrity and web/cache utilities. |
|
||||||
|
| `tempfile` | `tools` runtime; many crates dev | Temporary files/directories for command/tool execution and tests. |
|
||||||
|
| `thiserror` | many crates | Typed error definitions. |
|
||||||
|
| `tokio`, `tokio-util` | many crates | Async runtime, process/socket/time/file operations, stream utilities. |
|
||||||
|
| `tracing` | many crates | Structured runtime logging. |
|
||||||
|
| `uuid` | `client`, `memory`, `pod`, `protocol`, `session-store`, `tui` | Session/run/Pod identifiers; v7 and serde features where needed. |
|
||||||
|
| `zstd` | `llm-worker` | Codex backend request compression; usage confirmed in `llm_client/transport.rs`. |
|
||||||
|
|
||||||
|
### Dev-only direct dependencies
|
||||||
|
|
||||||
|
| Dependency | Direct users | Rough purpose / usage note |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `dotenv` | `llm-worker`, `pod` dev | Local dev/test credential loading. Not a runtime dependency. |
|
||||||
|
| `filetime` | `tools` dev | Filesystem timestamp testing. |
|
||||||
|
| `serial_test` | `provider` dev | Serializes provider tests that mutate shared state. |
|
||||||
|
| `tracing-subscriber` | `llm-worker` dev | Test/example logging setup. |
|
||||||
|
| `trybuild` | `llm-worker` dev | Proc-macro compile-fail/compile-pass tests. |
|
||||||
|
| `wiremock` | `llm-worker`, `provider` dev | Mock HTTP server for provider/client tests. |
|
||||||
|
|
||||||
|
## Transitive/license summary
|
||||||
|
|
||||||
|
### Local project license
|
||||||
|
|
||||||
|
- Workspace package license: `MIT` in root `Cargo.toml`.
|
||||||
|
- Repository `LICENSE`: MIT.
|
||||||
|
- Nix package metadata: `meta.license = lib.licenses.mit`.
|
||||||
|
|
||||||
|
### Cargo metadata / cargo-deny findings
|
||||||
|
|
||||||
|
`cargo metadata` showed no packages with missing `license` metadata.
|
||||||
|
|
||||||
|
`cargo deny --locked --offline --all-features list -f tsv` produced the following license-column counts. Counts include packages that offer multiple license alternatives, so the total exceeds the number of packages.
|
||||||
|
|
||||||
|
| License column | Count |
|
||||||
|
| --- | ---: |
|
||||||
|
| MIT | 348 |
|
||||||
|
| Apache-2.0 | 254 |
|
||||||
|
| Unicode-3.0 | 19 |
|
||||||
|
| Unlicense | 10 |
|
||||||
|
| Apache-2.0 WITH LLVM-exception | 8 |
|
||||||
|
| ISC | 7 |
|
||||||
|
| BSD-3-Clause | 2 |
|
||||||
|
| LGPL-2.1-or-later | 2 |
|
||||||
|
| BSD-2-Clause | 1 |
|
||||||
|
| BSL-1.0 | 1 |
|
||||||
|
| CC0-1.0 | 1 |
|
||||||
|
| CDLA-Permissive-2.0 | 1 |
|
||||||
|
| MIT-0 | 1 |
|
||||||
|
| OpenSSL | 1 |
|
||||||
|
| Zlib | 1 |
|
||||||
|
|
||||||
|
### Unknown, missing, copyleft, non-standard, or notice-relevant licenses
|
||||||
|
|
||||||
|
No missing license metadata was observed locally.
|
||||||
|
|
||||||
|
Items to explicitly account for in a release license policy/notice flow:
|
||||||
|
|
||||||
|
- `r-efi@5.3.0` and `r-efi@6.0.0` have expression `MIT OR Apache-2.0 OR LGPL-2.1-or-later`. This is not a blocker if the project selects the permissive MIT/Apache alternative, but a policy tool should encode that choice so the LGPL alternative is not misread as an obligation.
|
||||||
|
- `aws-lc-sys@0.35.0` is reported with `ISC AND (Apache-2.0 OR ISC) AND OpenSSL`; `aws-lc-rs`, `rustls`, `hyper-rustls`, `rustls-native-certs`, `rustls-webpki`, and `untrusted` also show ISC-family entries. The OpenSSL marker is notice-relevant and should be included in third-party notices if that path remains enabled.
|
||||||
|
- `webpki-root-certs@1.0.5` is `CDLA-Permissive-2.0`; include in notices/policy.
|
||||||
|
- ICU4X-related crates (`icu_*`, `zerovec*`, `zerofrom*`, `yoke*`, `tinystr`, `litemap`, `writeable`, `potential_utf`, `unicode-ident`) carry `Unicode-3.0`; include in policy/notices.
|
||||||
|
- `rustix`, `linux-raw-sys`, `wasi`, `wasip2`, `wasip3`, `wit-bindgen` include `Apache-2.0 WITH LLVM-exception`; standard permissive but notice-relevant.
|
||||||
|
- `globset`, `ignore`, `grep-*`, `aho-corasick`, `memchr`, `same-file`, `walkdir`, `winapi-util` include `Unlicense OR MIT`; select MIT or otherwise account for Unlicense acceptance in policy.
|
||||||
|
- `encoding_rs` / `subtle` show BSD-3-Clause, `zerocopy` shows BSD-2-Clause alternative, `ryu` shows BSL-1.0 alternative, `foldhash` shows Zlib, and `dunce` shows CC0/MIT-0/Apache alternatives. These are not blockers but should be represented in generated notices.
|
||||||
|
|
||||||
|
A broader `cargo metadata` license-field scan also surfaced `terminfo@0.9.0` with `WTFPL`, but `cargo tree` did not show it in the active default/all-features tree for Yoi; reverse metadata edges point through optional `termwiz`/`ratatui-termwiz`. I would not treat this as a release blocker based on current tree evidence, but a future policy check should confirm inactive optional dependencies are excluded or explicitly allowed.
|
||||||
|
|
||||||
|
## Heavy/redundant/replaceable dependency candidates
|
||||||
|
|
||||||
|
### 1. `reqwest` TLS feature duplication — high confidence cleanup candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- `llm-worker` and `tools` declare `reqwest` with `default-features = false` plus `native-tls`.
|
||||||
|
- `provider` declares `reqwest = { version = "0.13", features = ["json", "native-tls"] }` without `default-features = false`.
|
||||||
|
- `cargo tree --locked -e features -i reqwest@0.13.2` showed `provider` enabling `reqwest feature "default"`, which then enables `default-tls` and rustls-related features, while `native-tls` is also enabled.
|
||||||
|
- Inverse trees showed both `openssl-sys -> native-tls -> hyper-tls -> reqwest` and `rustls -> hyper-rustls -> reqwest` in the graph.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- Larger dependency graph and binary/build surface.
|
||||||
|
- Keeps Nix `openssl`/`pkg-config` system dependency necessary via native TLS.
|
||||||
|
- Adds license/notice surface from both native TLS/OpenSSL and rustls/aws-lc paths.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Open a follow-up to choose one TLS policy for Yoi HTTP clients. If native certificate store behavior is required, encode that intentionally. If rustls is sufficient, remove native OpenSSL TLS and revisit Nix `openssl`/`pkg-config` inputs. If native TLS is preferred, disable `reqwest` defaults consistently so rustls/default TLS paths are not accidentally enabled.
|
||||||
|
|
||||||
|
### 2. Duplicate `crossterm` versions — high confidence cleanup candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
`cargo tree --locked --duplicates --all-features` shows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
crossterm v0.28.1
|
||||||
|
└── tui v0.1.0
|
||||||
|
|
||||||
|
crossterm v0.29.0
|
||||||
|
└── ratatui-crossterm v0.1.0
|
||||||
|
└── ratatui v0.30.0
|
||||||
|
└── tui v0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- Duplicate terminal backend stack and some duplicated transitive platform crates.
|
||||||
|
- Likely avoidable by aligning direct `crossterm` with `ratatui`'s backend dependency, if API changes are small.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Open a small cleanup ticket to update direct `crossterm` to the version used by `ratatui-crossterm`, run TUI/input tests, and remove the duplicate if compatible.
|
||||||
|
|
||||||
|
### 3. HTML extraction stack (`html5ever` + `markup5ever_rcdom`) — medium confidence review candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- Direct dependency in `tools`.
|
||||||
|
- Usage is localized to WebFetch HTML parsing/extraction in `crates/tools/src/web.rs` (`html5ever::parse_document`, `RcDom`).
|
||||||
|
- Duplicate tree evidence includes older `syn v1`, `phf_*`, `siphasher`, `markup5ever`, and related build-time transitive crates from this stack.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- This is a relatively heavy parser stack for one subsystem, but it implements a real product requirement: robust local HTML extraction without sending raw HTML to the model.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Do not remove opportunistically. Open a follow-up only if WebFetch binary size/build time becomes a priority; compare with a maintained lighter parser/extractor while preserving safety behavior.
|
||||||
|
|
||||||
|
### 4. `serde_yaml` frontmatter parsing — medium confidence maintenance review candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- Direct users: `memory`, `workflow`.
|
||||||
|
- Usage is frontmatter/skill/workflow parsing and linting.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- YAML is appropriate for frontmatter, but the Rust YAML ecosystem has maintenance caveats. This is not a license blocker from local metadata.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Non-blocking follow-up: decide whether to keep `serde_yaml`, switch to a maintained fork, or constrain frontmatter to a smaller parser-supported subset. This requires design judgment because it affects user-authored workflow/memory files.
|
||||||
|
|
||||||
|
### 5. Dev/test HTTP stack (`wiremock`, `serial_test`, `trybuild`) — low confidence cleanup candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- `wiremock` appears only in dev dependencies for `llm-worker` and `provider`.
|
||||||
|
- `serial_test` and `trybuild` are dev-only.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- They increase test dependency graph, not runtime release surface.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- No release action needed. Only revisit if CI time or test dependency policy becomes a problem.
|
||||||
|
|
||||||
|
### 6. `mlua` vendored Lua — low confidence replacement candidate / justified heavy dependency
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- Direct user: `manifest`.
|
||||||
|
- Source usage is concentrated in Lua Profile evaluation (`manifest/src/profile.rs`) with controlled `require("yoi.*")` modules.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- Vendored interpreter is non-trivial build surface, but it supports a core profile-authoring direction.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Keep. Do not create a replacement ticket unless the product direction away from Lua Profiles is explicitly changed.
|
||||||
|
|
||||||
|
## Nix/system dependency notes
|
||||||
|
|
||||||
|
### `flake.nix`
|
||||||
|
|
||||||
|
- Inputs: `nixpkgs` from `github:nixos/nixpkgs?ref=nixos-unstable`, `flake-utils` from `github:numtide/flake-utils`.
|
||||||
|
- Outputs expose `packages.default`, `packages.yoi`, `apps.default`, `apps.yoi`, and `checks.default = yoi`.
|
||||||
|
- No extra system libraries are introduced in `flake.nix`; it delegates to `package.nix`.
|
||||||
|
|
||||||
|
### `package.nix`
|
||||||
|
|
||||||
|
- Build dependencies:
|
||||||
|
- `nativeBuildInputs = [ pkg-config ]`
|
||||||
|
- `buildInputs = [ openssl ]` plus Darwin frameworks `CoreFoundation`, `Security`, `SystemConfiguration` on macOS.
|
||||||
|
- `openssl`/`pkg-config` are consistent with the current native TLS path through `reqwest`/`native-tls`/`openssl-sys`.
|
||||||
|
- `meta.license = lib.licenses.mit` matches workspace/repo license.
|
||||||
|
- `cargoHash` is pinned.
|
||||||
|
- `depsExtraArgs` rewrites cargo vendor fetching from crates.io API download URLs to `static.crates.io` due an upstream/nixpkgs fetcher issue; this is packaging infrastructure, not a license concern.
|
||||||
|
- Source filter excludes `.git`, `target`, `result`, `.yoi`, `.worktree`, `work-items`, and `docs/report` from package source closure. This reduces accidental release of local coordination/generated state.
|
||||||
|
|
||||||
|
### `devshell.nix`
|
||||||
|
|
||||||
|
- Dev packages: `nixfmt`, `deno`, `git`, `rustc`, `cargo`.
|
||||||
|
- Dev build inputs: `pkg-config`, `openssl`.
|
||||||
|
- These are development/build tools, not bundled runtime dependencies. `deno` is present in the dev shell but not in `package.nix` or Cargo runtime dependencies.
|
||||||
|
|
||||||
|
## Release blockers vs non-blocking follow-ups
|
||||||
|
|
||||||
|
### Release blockers
|
||||||
|
|
||||||
|
No dependency license was identified as incompatible with MIT publication from the local metadata.
|
||||||
|
|
||||||
|
Potential process blocker before public distribution: third-party notices/license policy are not currently materialized. Apache-2.0, BSD, Unicode, CDLA, OpenSSL-marker, LLVM-exception, and other permissive licenses are acceptable in principle but should be included in a generated notice/policy artifact for release hygiene.
|
||||||
|
|
||||||
|
### Non-blocking follow-ups
|
||||||
|
|
||||||
|
- Normalize `reqwest` TLS features and decide whether Yoi wants native TLS/OpenSSL or rustls. This may also simplify Nix system dependencies.
|
||||||
|
- Align direct `crossterm` with `ratatui`'s `crossterm` backend to remove duplicate versions.
|
||||||
|
- Add CI-enforced license/dependency policy (`cargo-deny` or equivalent) and generated third-party notices.
|
||||||
|
- Review HTML parser stack only if size/build time is a concern.
|
||||||
|
- Review `serde_yaml` maintenance posture for frontmatter parsing; this is design-sensitive, not an obvious cleanup.
|
||||||
|
|
||||||
|
## Version update scan
|
||||||
|
|
||||||
|
Additional commands run after the initial audit:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo outdated --workspace --root-deps-only --format json > /tmp/yoi-cargo-outdated-root.json
|
||||||
|
cargo outdated --workspace --format json > /tmp/yoi-cargo-outdated-all.json
|
||||||
|
cargo update --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
`cargo outdated --workspace --root-deps-only` reported the following direct workspace dependency updates:
|
||||||
|
|
||||||
|
| Dependency | Current | Latest reported | Kind | Direct users | Note |
|
||||||
|
| --- | ---: | ---: | --- | --- | --- |
|
||||||
|
| `reqwest` | 0.13.2 | 0.13.4 | normal | `llm-worker`, `provider`, `tools` | Patch update; should be combined with the TLS feature normalization follow-up. |
|
||||||
|
| `clap` | 4.6.0 | 4.6.1 | normal/dev | `pod`, `llm-worker` dev | Patch update. |
|
||||||
|
| `minijinja` | 2.19.0 | 2.20.0 | normal | `pod` | Minor update. |
|
||||||
|
| `crossterm` | 0.28.1 | 0.29.0 | normal | `tui` | Also matches the earlier duplicate-version finding with `ratatui`'s backend. |
|
||||||
|
| `pulldown-cmark` | 0.13.3 | 0.13.4 | normal | `tui` | Patch update. |
|
||||||
|
| `html5ever` | 0.26.0 | 0.39.0 | normal | `tools` | Major stack update; treat as WebFetch parser migration work, not routine bump. |
|
||||||
|
| `markup5ever_rcdom` | 0.2.0 | 0.39.0+unofficial | normal | `tools` | Major/unofficial stack update; tied to `html5ever` review. |
|
||||||
|
| `filetime` | 0.2.27 | 0.2.29 | dev | `tools` dev | Dev/test-only patch update. |
|
||||||
|
|
||||||
|
`cargo update --dry-run` reported that 80 locked packages can move to latest compatible versions without editing manifests. Notable compatible lockfile updates include:
|
||||||
|
|
||||||
|
- HTTP/TLS stack: `reqwest 0.13.2 -> 0.13.4`, `hyper 1.9.0 -> 1.10.1`, `h2 0.4.13 -> 0.4.14`, `rustls 0.23.37 -> 0.23.40`, `rustls-native-certs 0.8.3 -> 0.8.4`, `rustls-platform-verifier 0.6.2 -> 0.7.0`, `openssl 0.10.76 -> 0.10.80`, `openssl-sys 0.9.112 -> 0.9.116`, `aws-lc-rs 1.15.2 -> 1.17.0`, `aws-lc-sys 0.35.0 -> 0.41.0`.
|
||||||
|
- Core/runtime stack: `tokio 1.52.1 -> 1.52.3`, `uuid 1.23.1 -> 1.23.2`, `serde_json 1.0.149 -> 1.0.150`, `memchr 2.8.0 -> 2.8.1`, `indexmap 2.13.1 -> 2.14.0`, `socket2 0.6.3 -> 0.6.4`.
|
||||||
|
- UI/parser/dev stack: `minijinja 2.19.0 -> 2.20.0`, `pulldown-cmark 0.13.3 -> 0.13.4`, `filetime 0.2.27 -> 0.2.29`, `serial_test 3.4.0 -> 3.5.0`.
|
||||||
|
- Platform/wasm/windows support crates: multiple `wasm-bindgen`, `web-sys`, `windows-*`, `wasip2`, and `zerocopy` patch/minor updates.
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
|
||||||
|
- There is a low-risk follow-up to run a lockfile refresh (`cargo update`) and validate it, but it should be separate from dependency policy changes because it touches many transitive packages.
|
||||||
|
- Direct manifest bumps can be grouped by risk: small patch/minor bumps (`reqwest`, `clap`, `minijinja`, `pulldown-cmark`, `filetime`) vs behavior/API-sensitive stack bumps (`crossterm`, `html5ever`, `markup5ever_rcdom`).
|
||||||
|
- The `reqwest` bump should not be done blindly before deciding the TLS feature policy, because the current audit already found accidental native-tls/rustls feature duplication.
|
||||||
|
|
||||||
|
## Recommended follow-up tickets
|
||||||
|
|
||||||
|
1. **Add dependency license policy and third-party notice generation**
|
||||||
|
- Acceptance: checked-in `cargo-deny` or equivalent policy; generated/reproducible notices for release artifacts; explicit choices for dual/multi-license crates such as `r-efi`, `Unlicense OR MIT`, and rustls/aws-lc/OpenSSL-marked crates; CI command documented.
|
||||||
|
|
||||||
|
2. **Normalize HTTP TLS backend and Nix OpenSSL dependency**
|
||||||
|
- Acceptance: all direct `reqwest` users consistently disable or enable defaults according to one documented TLS policy; dependency tree no longer unintentionally contains both `native-tls`/OpenSSL and rustls/default TLS paths unless intentionally justified; `package.nix` `openssl`/`pkg-config` inputs are retained or removed according to evidence.
|
||||||
|
|
||||||
|
3. **Deduplicate TUI terminal backend dependencies**
|
||||||
|
- Acceptance: direct `crossterm` version is aligned with `ratatui-crossterm` or duplicate is otherwise justified; TUI/input behavior is validated.
|
||||||
|
|
||||||
|
4. **Evaluate frontmatter YAML parser maintenance**
|
||||||
|
- Acceptance: decide to keep `serde_yaml`, migrate to a maintained fork, or specify a smaller frontmatter subset; include migration/compatibility implications for `.yoi/workflow`, memory, and skill files.
|
||||||
|
|
||||||
|
5. **Optional WebFetch parser weight review**
|
||||||
|
- Acceptance: compare current `html5ever`/`RcDom` extractor with viable maintained alternatives; preserve bounded, safe, link-aware extraction behavior; only proceed if measurable binary/build-time benefit exists.
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Delegation intent: dependency/license audit
|
||||||
|
|
||||||
|
Intent:
|
||||||
|
- Audit Yoi's external dependencies and license posture before public MIT publication.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Inventory Rust dependencies from `Cargo.lock` / `cargo metadata`, separating direct workspace dependencies from transitive dependencies where practical.
|
||||||
|
- Identify direct dependencies that look heavy, weakly justified, redundant, or replaceable with simpler local code or already-present dependencies.
|
||||||
|
- Check license metadata for direct and transitive Rust dependencies; flag unknown, missing, copyleft, non-standard, or notice-relevant licenses.
|
||||||
|
- Inspect Nix/system dependencies from `flake.nix`, `package.nix`, and `devshell.nix` at a high level.
|
||||||
|
- Produce a report at `work-items/open/20260601-123641-dependency-license-audit/artifacts/audit-report.md`.
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
- Do not modify dependency manifests, source code, lockfiles, docs, or work item files other than the audit report artifact.
|
||||||
|
- Do not read ignored secret-like file contents.
|
||||||
|
- Treat Cargo/Nix files and command output as current-state evidence; do not rely on resident memory for exact dependency/license facts.
|
||||||
|
- Distinguish release blockers from advisory cleanup opportunities.
|
||||||
|
|
||||||
|
Non-goals:
|
||||||
|
- Do not remove dependencies.
|
||||||
|
- Do not change licenses.
|
||||||
|
- Do not implement replacements.
|
||||||
|
- Do not perform a public-release history sanitation audit beyond dependency/license implications.
|
||||||
|
|
||||||
|
Escalate if:
|
||||||
|
- A dependency appears incompatible with MIT publication.
|
||||||
|
- License metadata is missing for a significant dependency and cannot be resolved from local registry metadata without web access.
|
||||||
|
- A dependency replacement would require design judgment rather than obvious cleanup.
|
||||||
|
- Commands require network access or mutation beyond the artifact report.
|
||||||
|
|
||||||
|
Validation/evidence:
|
||||||
|
- Record every command used for inventory/license evidence.
|
||||||
|
- Prefer read-only commands such as `cargo metadata --locked`, `cargo tree --locked`, `nix flake metadata`, and direct file reads.
|
||||||
|
- If a tool is unavailable, record the fallback method.
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
id: 20260601-123641-dependency-license-audit
|
||||||
|
slug: dependency-license-audit
|
||||||
|
title: Audit external dependencies and license posture
|
||||||
|
status: open
|
||||||
|
kind: task
|
||||||
|
priority: P2
|
||||||
|
labels: [audit, dependencies, license]
|
||||||
|
created_at: 2026-06-01T12:36:41Z
|
||||||
|
updated_at: 2026-06-01T13:08:45Z
|
||||||
|
assignee: null
|
||||||
|
legacy_ticket: null
|
||||||
|
---
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Before public MIT release, Yoi needs a focused audit of external dependencies and their licenses. The goal is not to remove every dependency, but to identify heavy or weakly-justified dependencies, dependencies that are easy to replace with simpler code or existing transitive functionality, and any licensing or notice obligations that conflict with an MIT release posture.
|
||||||
|
|
||||||
|
This is an investigation/audit ticket. It should produce an actionable report rather than immediate dependency changes.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Inventory Rust crate dependencies from `Cargo.lock` / workspace metadata, including direct vs transitive status where practical.
|
||||||
|
- Identify direct dependencies that appear heavy, barely used, redundant, or plausibly replaceable.
|
||||||
|
- Check license metadata for direct and transitive Rust dependencies and flag unknown, copyleft, non-standard, or notice-relevant licenses.
|
||||||
|
- Inspect Nix/runtime/system dependencies declared by `flake.nix`, `package.nix`, and `devshell.nix` at a high level.
|
||||||
|
- Distinguish release blockers from advisory cleanup opportunities.
|
||||||
|
- Do not read ignored secret-like file contents.
|
||||||
|
- Do not modify dependency manifests as part of this ticket unless explicitly approved later.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- An artifact report is written under this ticket with:
|
||||||
|
- dependency inventory methodology;
|
||||||
|
- direct dependency list with rough purpose/usage notes;
|
||||||
|
- heavy/redundant/replaceable dependency candidates;
|
||||||
|
- license compatibility findings for an MIT publication posture;
|
||||||
|
- Nix/system dependency notes;
|
||||||
|
- recommended follow-up tickets, if any.
|
||||||
|
- Report clearly marks any release blockers vs non-blocking cleanup.
|
||||||
|
- Validation/evidence commands used for the audit are recorded.
|
||||||
|
|
@ -0,0 +1,421 @@
|
||||||
|
<!-- event: create author: tickets.sh at: 2026-06-01T12:36:41Z -->
|
||||||
|
|
||||||
|
## Created
|
||||||
|
|
||||||
|
Created by tickets.sh create.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: plan author: hare at: 2026-06-01T12:37:30Z -->
|
||||||
|
|
||||||
|
## Plan
|
||||||
|
|
||||||
|
# Delegation intent: dependency/license audit
|
||||||
|
|
||||||
|
Intent:
|
||||||
|
- Audit Yoi's external dependencies and license posture before public MIT publication.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Inventory Rust dependencies from `Cargo.lock` / `cargo metadata`, separating direct workspace dependencies from transitive dependencies where practical.
|
||||||
|
- Identify direct dependencies that look heavy, weakly justified, redundant, or replaceable with simpler local code or already-present dependencies.
|
||||||
|
- Check license metadata for direct and transitive Rust dependencies; flag unknown, missing, copyleft, non-standard, or notice-relevant licenses.
|
||||||
|
- Inspect Nix/system dependencies from `flake.nix`, `package.nix`, and `devshell.nix` at a high level.
|
||||||
|
- Produce a report at `work-items/open/20260601-123641-dependency-license-audit/artifacts/audit-report.md`.
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
- Do not modify dependency manifests, source code, lockfiles, docs, or work item files other than the audit report artifact.
|
||||||
|
- Do not read ignored secret-like file contents.
|
||||||
|
- Treat Cargo/Nix files and command output as current-state evidence; do not rely on resident memory for exact dependency/license facts.
|
||||||
|
- Distinguish release blockers from advisory cleanup opportunities.
|
||||||
|
|
||||||
|
Non-goals:
|
||||||
|
- Do not remove dependencies.
|
||||||
|
- Do not change licenses.
|
||||||
|
- Do not implement replacements.
|
||||||
|
- Do not perform a public-release history sanitation audit beyond dependency/license implications.
|
||||||
|
|
||||||
|
Escalate if:
|
||||||
|
- A dependency appears incompatible with MIT publication.
|
||||||
|
- License metadata is missing for a significant dependency and cannot be resolved from local registry metadata without web access.
|
||||||
|
- A dependency replacement would require design judgment rather than obvious cleanup.
|
||||||
|
- Commands require network access or mutation beyond the artifact report.
|
||||||
|
|
||||||
|
Validation/evidence:
|
||||||
|
- Record every command used for inventory/license evidence.
|
||||||
|
- Prefer read-only commands such as `cargo metadata --locked`, `cargo tree --locked`, `nix flake metadata`, and direct file reads.
|
||||||
|
- If a tool is unavailable, record the fallback method.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: implementation_report author: hare at: 2026-06-01T13:08:45Z -->
|
||||||
|
|
||||||
|
## Implementation report
|
||||||
|
|
||||||
|
# Dependency/license audit report
|
||||||
|
|
||||||
|
Date: 2026-06-01
|
||||||
|
|
||||||
|
Scope: read-mostly dependency and license audit for the Yoi workspace at `/home/hare/Projects/yoi`, per `artifacts/delegation-intent.md`. I did not modify dependency manifests, source code, lockfiles, docs, or ticket files other than this report artifact. I did not read ignored secret-like file contents.
|
||||||
|
|
||||||
|
## Executive summary
|
||||||
|
|
||||||
|
No dependency-license incompatibility blocker was identified from the available local metadata. The main release-risk gap is process/packaging: there is no checked-in dependency license policy or generated third-party notices artifact, so a public binary/source release should add one before publication if notices are expected to ship with the release.
|
||||||
|
|
||||||
|
The clearest cleanup candidates are non-blocking: normalize `reqwest` TLS features so Yoi does not enable both native OpenSSL TLS and rustls-related TLS paths, align the direct `crossterm` version with `ratatui`'s backend dependency, and periodically review the HTML/YAML parsing stacks for weight/maintenance.
|
||||||
|
|
||||||
|
## Methodology and commands used
|
||||||
|
|
||||||
|
Evidence came from local manifests, lock/metadata commands, dependency trees, and source usage greps. Commands were read-only except for writing this report.
|
||||||
|
|
||||||
|
File reads:
|
||||||
|
|
||||||
|
- `artifacts/delegation-intent.md`
|
||||||
|
- root `Cargo.toml`, `Cargo.lock`, workspace crate `Cargo.toml` files under `crates/*/Cargo.toml`
|
||||||
|
- `LICENSE`
|
||||||
|
- `flake.nix`, `package.nix`, `devshell.nix`
|
||||||
|
|
||||||
|
Inventory and license commands:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /home/hare/Projects/yoi
|
||||||
|
cargo metadata --locked --format-version 1
|
||||||
|
cargo metadata --locked --format-version 1 | jq -r '...direct workspace dependency grouping...'
|
||||||
|
cargo metadata --locked --format-version 1 | jq -r '...license field grouping and concerning-license filters...'
|
||||||
|
cargo deny check licenses
|
||||||
|
cargo deny --locked --offline list -f tsv
|
||||||
|
cargo deny --locked --offline --all-features list -f tsv
|
||||||
|
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...license counts...'
|
||||||
|
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...non-standard/copyleft/notice-relevant license packages...'
|
||||||
|
cargo tree --locked -e features -i reqwest@0.13.2
|
||||||
|
cargo tree --locked -i openssl-sys@0.9.112 --all-features
|
||||||
|
cargo tree --locked -i native-tls@0.2.18 --all-features
|
||||||
|
cargo tree --locked -i rustls@0.23.37 --all-features
|
||||||
|
cargo tree --locked --duplicates --all-features
|
||||||
|
cargo tree --locked -e no-dev --prefix none | sort -u | wc -l
|
||||||
|
cargo tree --locked -e no-dev --duplicates
|
||||||
|
```
|
||||||
|
|
||||||
|
Source usage checks:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rg 'use reqwest|reqwest::|ClientBuilder|Client::builder' crates/**/*.rs
|
||||||
|
rg 'html5ever|markup5ever|RcDom|parse_document' crates/**/*.rs
|
||||||
|
rg 'serde_yaml|frontmatter|yaml|YAML' crates/**/*.rs
|
||||||
|
rg 'zstd|encode_all|decode_all' crates/**/*.rs
|
||||||
|
rg 'mlua::|Lua::|LuaSerdeExt|require\(' crates/**/*.rs
|
||||||
|
rg 'crossterm::|ratatui::' crates/**/*.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
Fallbacks / tool notes:
|
||||||
|
|
||||||
|
- `python3` was unavailable in the audit environment, so JSON processing used `jq` and `awk`.
|
||||||
|
- `cargo deny check licenses` exited non-zero because no project license policy/config is present; I used `cargo deny ... list -f tsv` as a local metadata fallback rather than treating the policy failure itself as license evidence.
|
||||||
|
- No web lookup was used. License conclusions are therefore limited to local crates.io metadata / cargo-deny parsing and local manifests.
|
||||||
|
|
||||||
|
## Workspace/dependency shape
|
||||||
|
|
||||||
|
- Root workspace: 19 crates, workspace package license `MIT`.
|
||||||
|
- Project license file: `LICENSE` is MIT.
|
||||||
|
- `cargo metadata --locked --format-version 1` reported 486 package records in the resolved metadata set.
|
||||||
|
- `cargo tree --locked -e no-dev --prefix none | sort -u | wc -l` reported 404 unique non-dev tree lines.
|
||||||
|
- Direct external dependencies found across workspace manifests: 45 unique package names including dev/build-only dependencies.
|
||||||
|
|
||||||
|
## Direct Rust dependencies and rough purpose notes
|
||||||
|
|
||||||
|
### Runtime/build dependencies
|
||||||
|
|
||||||
|
| Dependency | Direct users | Rough purpose / usage note |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `arc-swap` | `manifest`, `pod` | Shared mutable configuration/state handles. |
|
||||||
|
| `async-trait` | `llm-worker`, `memory`, `pod`, `provider`, `tools` | Async trait object ergonomics for tool/provider abstractions. |
|
||||||
|
| `base64` | `provider` | Codex/OAuth or provider token/body encoding helpers. |
|
||||||
|
| `chrono` | `lint-common`, `memory`, `pod`, `provider`, `workflow` | Timestamps, serde timestamps, workflow/memory metadata. |
|
||||||
|
| `clap` | `pod` runtime; `llm-worker` dev | CLI parsing. |
|
||||||
|
| `crossterm` | `tui` | Terminal input/output/events. Direct version is `0.28`; `ratatui` pulls `0.29` transitively. |
|
||||||
|
| `eventsource-stream` | `llm-worker` | SSE stream parsing for LLM/provider responses. |
|
||||||
|
| `fs4` | `pod`, `pod-registry` | Cross-process file locking. |
|
||||||
|
| `futures` | `llm-worker`; dev in `pod`, `session-store` | Stream/future helpers. |
|
||||||
|
| `globset`, `ignore`, `grep-matcher`, `grep-regex`, `grep-searcher` | `tools` | Local Glob/Grep tool implementation, gitignore-aware search. |
|
||||||
|
| `html5ever`, `markup5ever_rcdom` | `tools` | WebFetch HTML parsing and Readability-style extraction (`tools/src/web.rs`). |
|
||||||
|
| `include_dir` | `pod` | Compile-time embedding of prompt/profile/runtime resources. |
|
||||||
|
| `libc` | `memory`, `pod`, `pod-registry` | Unix process/permission/runtime details. |
|
||||||
|
| `minijinja` | `pod` | Prompt/template rendering. |
|
||||||
|
| `mlua` | `manifest` | Lua profile evaluation with vendored Lua 5.4 and serde integration. |
|
||||||
|
| `proc-macro2`, `quote`, `syn` | `llm-worker-macros` | Procedural macro implementation. |
|
||||||
|
| `pulldown-cmark` | `tui` | Markdown rendering/parsing in terminal UI. |
|
||||||
|
| `ratatui`, `unicode-width` | `tui` | Terminal UI rendering and width calculations. |
|
||||||
|
| `reqwest` | `llm-worker`, `provider`, `tools` | HTTP client for LLM transport, OAuth refresh, WebSearch/WebFetch. |
|
||||||
|
| `schemars` | `memory`, `pod`, `tools`; `llm-worker` dev | JSON schema generation for tools/config/test surfaces. |
|
||||||
|
| `serde`, `serde_json`, `serde_ignored`, `toml` | many crates | Serialization and config/profile parsing. |
|
||||||
|
| `serde_yaml` | `memory`, `workflow` | YAML frontmatter parsing for memory/workflow/skill documents. |
|
||||||
|
| `sha2` | `memory`, `secrets`, `tools` | Hashing/audit/secret-integrity and web/cache utilities. |
|
||||||
|
| `tempfile` | `tools` runtime; many crates dev | Temporary files/directories for command/tool execution and tests. |
|
||||||
|
| `thiserror` | many crates | Typed error definitions. |
|
||||||
|
| `tokio`, `tokio-util` | many crates | Async runtime, process/socket/time/file operations, stream utilities. |
|
||||||
|
| `tracing` | many crates | Structured runtime logging. |
|
||||||
|
| `uuid` | `client`, `memory`, `pod`, `protocol`, `session-store`, `tui` | Session/run/Pod identifiers; v7 and serde features where needed. |
|
||||||
|
| `zstd` | `llm-worker` | Codex backend request compression; usage confirmed in `llm_client/transport.rs`. |
|
||||||
|
|
||||||
|
### Dev-only direct dependencies
|
||||||
|
|
||||||
|
| Dependency | Direct users | Rough purpose / usage note |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `dotenv` | `llm-worker`, `pod` dev | Local dev/test credential loading. Not a runtime dependency. |
|
||||||
|
| `filetime` | `tools` dev | Filesystem timestamp testing. |
|
||||||
|
| `serial_test` | `provider` dev | Serializes provider tests that mutate shared state. |
|
||||||
|
| `tracing-subscriber` | `llm-worker` dev | Test/example logging setup. |
|
||||||
|
| `trybuild` | `llm-worker` dev | Proc-macro compile-fail/compile-pass tests. |
|
||||||
|
| `wiremock` | `llm-worker`, `provider` dev | Mock HTTP server for provider/client tests. |
|
||||||
|
|
||||||
|
## Transitive/license summary
|
||||||
|
|
||||||
|
### Local project license
|
||||||
|
|
||||||
|
- Workspace package license: `MIT` in root `Cargo.toml`.
|
||||||
|
- Repository `LICENSE`: MIT.
|
||||||
|
- Nix package metadata: `meta.license = lib.licenses.mit`.
|
||||||
|
|
||||||
|
### Cargo metadata / cargo-deny findings
|
||||||
|
|
||||||
|
`cargo metadata` showed no packages with missing `license` metadata.
|
||||||
|
|
||||||
|
`cargo deny --locked --offline --all-features list -f tsv` produced the following license-column counts. Counts include packages that offer multiple license alternatives, so the total exceeds the number of packages.
|
||||||
|
|
||||||
|
| License column | Count |
|
||||||
|
| --- | ---: |
|
||||||
|
| MIT | 348 |
|
||||||
|
| Apache-2.0 | 254 |
|
||||||
|
| Unicode-3.0 | 19 |
|
||||||
|
| Unlicense | 10 |
|
||||||
|
| Apache-2.0 WITH LLVM-exception | 8 |
|
||||||
|
| ISC | 7 |
|
||||||
|
| BSD-3-Clause | 2 |
|
||||||
|
| LGPL-2.1-or-later | 2 |
|
||||||
|
| BSD-2-Clause | 1 |
|
||||||
|
| BSL-1.0 | 1 |
|
||||||
|
| CC0-1.0 | 1 |
|
||||||
|
| CDLA-Permissive-2.0 | 1 |
|
||||||
|
| MIT-0 | 1 |
|
||||||
|
| OpenSSL | 1 |
|
||||||
|
| Zlib | 1 |
|
||||||
|
|
||||||
|
### Unknown, missing, copyleft, non-standard, or notice-relevant licenses
|
||||||
|
|
||||||
|
No missing license metadata was observed locally.
|
||||||
|
|
||||||
|
Items to explicitly account for in a release license policy/notice flow:
|
||||||
|
|
||||||
|
- `r-efi@5.3.0` and `r-efi@6.0.0` have expression `MIT OR Apache-2.0 OR LGPL-2.1-or-later`. This is not a blocker if the project selects the permissive MIT/Apache alternative, but a policy tool should encode that choice so the LGPL alternative is not misread as an obligation.
|
||||||
|
- `aws-lc-sys@0.35.0` is reported with `ISC AND (Apache-2.0 OR ISC) AND OpenSSL`; `aws-lc-rs`, `rustls`, `hyper-rustls`, `rustls-native-certs`, `rustls-webpki`, and `untrusted` also show ISC-family entries. The OpenSSL marker is notice-relevant and should be included in third-party notices if that path remains enabled.
|
||||||
|
- `webpki-root-certs@1.0.5` is `CDLA-Permissive-2.0`; include in notices/policy.
|
||||||
|
- ICU4X-related crates (`icu_*`, `zerovec*`, `zerofrom*`, `yoke*`, `tinystr`, `litemap`, `writeable`, `potential_utf`, `unicode-ident`) carry `Unicode-3.0`; include in policy/notices.
|
||||||
|
- `rustix`, `linux-raw-sys`, `wasi`, `wasip2`, `wasip3`, `wit-bindgen` include `Apache-2.0 WITH LLVM-exception`; standard permissive but notice-relevant.
|
||||||
|
- `globset`, `ignore`, `grep-*`, `aho-corasick`, `memchr`, `same-file`, `walkdir`, `winapi-util` include `Unlicense OR MIT`; select MIT or otherwise account for Unlicense acceptance in policy.
|
||||||
|
- `encoding_rs` / `subtle` show BSD-3-Clause, `zerocopy` shows BSD-2-Clause alternative, `ryu` shows BSL-1.0 alternative, `foldhash` shows Zlib, and `dunce` shows CC0/MIT-0/Apache alternatives. These are not blockers but should be represented in generated notices.
|
||||||
|
|
||||||
|
A broader `cargo metadata` license-field scan also surfaced `terminfo@0.9.0` with `WTFPL`, but `cargo tree` did not show it in the active default/all-features tree for Yoi; reverse metadata edges point through optional `termwiz`/`ratatui-termwiz`. I would not treat this as a release blocker based on current tree evidence, but a future policy check should confirm inactive optional dependencies are excluded or explicitly allowed.
|
||||||
|
|
||||||
|
## Heavy/redundant/replaceable dependency candidates
|
||||||
|
|
||||||
|
### 1. `reqwest` TLS feature duplication — high confidence cleanup candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- `llm-worker` and `tools` declare `reqwest` with `default-features = false` plus `native-tls`.
|
||||||
|
- `provider` declares `reqwest = { version = "0.13", features = ["json", "native-tls"] }` without `default-features = false`.
|
||||||
|
- `cargo tree --locked -e features -i reqwest@0.13.2` showed `provider` enabling `reqwest feature "default"`, which then enables `default-tls` and rustls-related features, while `native-tls` is also enabled.
|
||||||
|
- Inverse trees showed both `openssl-sys -> native-tls -> hyper-tls -> reqwest` and `rustls -> hyper-rustls -> reqwest` in the graph.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- Larger dependency graph and binary/build surface.
|
||||||
|
- Keeps Nix `openssl`/`pkg-config` system dependency necessary via native TLS.
|
||||||
|
- Adds license/notice surface from both native TLS/OpenSSL and rustls/aws-lc paths.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Open a follow-up to choose one TLS policy for Yoi HTTP clients. If native certificate store behavior is required, encode that intentionally. If rustls is sufficient, remove native OpenSSL TLS and revisit Nix `openssl`/`pkg-config` inputs. If native TLS is preferred, disable `reqwest` defaults consistently so rustls/default TLS paths are not accidentally enabled.
|
||||||
|
|
||||||
|
### 2. Duplicate `crossterm` versions — high confidence cleanup candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
`cargo tree --locked --duplicates --all-features` shows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
crossterm v0.28.1
|
||||||
|
└── tui v0.1.0
|
||||||
|
|
||||||
|
crossterm v0.29.0
|
||||||
|
└── ratatui-crossterm v0.1.0
|
||||||
|
└── ratatui v0.30.0
|
||||||
|
└── tui v0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- Duplicate terminal backend stack and some duplicated transitive platform crates.
|
||||||
|
- Likely avoidable by aligning direct `crossterm` with `ratatui`'s backend dependency, if API changes are small.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Open a small cleanup ticket to update direct `crossterm` to the version used by `ratatui-crossterm`, run TUI/input tests, and remove the duplicate if compatible.
|
||||||
|
|
||||||
|
### 3. HTML extraction stack (`html5ever` + `markup5ever_rcdom`) — medium confidence review candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- Direct dependency in `tools`.
|
||||||
|
- Usage is localized to WebFetch HTML parsing/extraction in `crates/tools/src/web.rs` (`html5ever::parse_document`, `RcDom`).
|
||||||
|
- Duplicate tree evidence includes older `syn v1`, `phf_*`, `siphasher`, `markup5ever`, and related build-time transitive crates from this stack.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- This is a relatively heavy parser stack for one subsystem, but it implements a real product requirement: robust local HTML extraction without sending raw HTML to the model.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Do not remove opportunistically. Open a follow-up only if WebFetch binary size/build time becomes a priority; compare with a maintained lighter parser/extractor while preserving safety behavior.
|
||||||
|
|
||||||
|
### 4. `serde_yaml` frontmatter parsing — medium confidence maintenance review candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- Direct users: `memory`, `workflow`.
|
||||||
|
- Usage is frontmatter/skill/workflow parsing and linting.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- YAML is appropriate for frontmatter, but the Rust YAML ecosystem has maintenance caveats. This is not a license blocker from local metadata.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Non-blocking follow-up: decide whether to keep `serde_yaml`, switch to a maintained fork, or constrain frontmatter to a smaller parser-supported subset. This requires design judgment because it affects user-authored workflow/memory files.
|
||||||
|
|
||||||
|
### 5. Dev/test HTTP stack (`wiremock`, `serial_test`, `trybuild`) — low confidence cleanup candidate
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- `wiremock` appears only in dev dependencies for `llm-worker` and `provider`.
|
||||||
|
- `serial_test` and `trybuild` are dev-only.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- They increase test dependency graph, not runtime release surface.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- No release action needed. Only revisit if CI time or test dependency policy becomes a problem.
|
||||||
|
|
||||||
|
### 6. `mlua` vendored Lua — low confidence replacement candidate / justified heavy dependency
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- Direct user: `manifest`.
|
||||||
|
- Source usage is concentrated in Lua Profile evaluation (`manifest/src/profile.rs`) with controlled `require("yoi.*")` modules.
|
||||||
|
|
||||||
|
Impact:
|
||||||
|
|
||||||
|
- Vendored interpreter is non-trivial build surface, but it supports a core profile-authoring direction.
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- Keep. Do not create a replacement ticket unless the product direction away from Lua Profiles is explicitly changed.
|
||||||
|
|
||||||
|
## Nix/system dependency notes
|
||||||
|
|
||||||
|
### `flake.nix`
|
||||||
|
|
||||||
|
- Inputs: `nixpkgs` from `github:nixos/nixpkgs?ref=nixos-unstable`, `flake-utils` from `github:numtide/flake-utils`.
|
||||||
|
- Outputs expose `packages.default`, `packages.yoi`, `apps.default`, `apps.yoi`, and `checks.default = yoi`.
|
||||||
|
- No extra system libraries are introduced in `flake.nix`; it delegates to `package.nix`.
|
||||||
|
|
||||||
|
### `package.nix`
|
||||||
|
|
||||||
|
- Build dependencies:
|
||||||
|
- `nativeBuildInputs = [ pkg-config ]`
|
||||||
|
- `buildInputs = [ openssl ]` plus Darwin frameworks `CoreFoundation`, `Security`, `SystemConfiguration` on macOS.
|
||||||
|
- `openssl`/`pkg-config` are consistent with the current native TLS path through `reqwest`/`native-tls`/`openssl-sys`.
|
||||||
|
- `meta.license = lib.licenses.mit` matches workspace/repo license.
|
||||||
|
- `cargoHash` is pinned.
|
||||||
|
- `depsExtraArgs` rewrites cargo vendor fetching from crates.io API download URLs to `static.crates.io` due an upstream/nixpkgs fetcher issue; this is packaging infrastructure, not a license concern.
|
||||||
|
- Source filter excludes `.git`, `target`, `result`, `.yoi`, `.worktree`, `work-items`, and `docs/report` from package source closure. This reduces accidental release of local coordination/generated state.
|
||||||
|
|
||||||
|
### `devshell.nix`
|
||||||
|
|
||||||
|
- Dev packages: `nixfmt`, `deno`, `git`, `rustc`, `cargo`.
|
||||||
|
- Dev build inputs: `pkg-config`, `openssl`.
|
||||||
|
- These are development/build tools, not bundled runtime dependencies. `deno` is present in the dev shell but not in `package.nix` or Cargo runtime dependencies.
|
||||||
|
|
||||||
|
## Release blockers vs non-blocking follow-ups
|
||||||
|
|
||||||
|
### Release blockers
|
||||||
|
|
||||||
|
No dependency license was identified as incompatible with MIT publication from the local metadata.
|
||||||
|
|
||||||
|
Potential process blocker before public distribution: third-party notices/license policy are not currently materialized. Apache-2.0, BSD, Unicode, CDLA, OpenSSL-marker, LLVM-exception, and other permissive licenses are acceptable in principle but should be included in a generated notice/policy artifact for release hygiene.
|
||||||
|
|
||||||
|
### Non-blocking follow-ups
|
||||||
|
|
||||||
|
- Normalize `reqwest` TLS features and decide whether Yoi wants native TLS/OpenSSL or rustls. This may also simplify Nix system dependencies.
|
||||||
|
- Align direct `crossterm` with `ratatui`'s `crossterm` backend to remove duplicate versions.
|
||||||
|
- Add CI-enforced license/dependency policy (`cargo-deny` or equivalent) and generated third-party notices.
|
||||||
|
- Review HTML parser stack only if size/build time is a concern.
|
||||||
|
- Review `serde_yaml` maintenance posture for frontmatter parsing; this is design-sensitive, not an obvious cleanup.
|
||||||
|
|
||||||
|
## Version update scan
|
||||||
|
|
||||||
|
Additional commands run after the initial audit:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo outdated --workspace --root-deps-only --format json > /tmp/yoi-cargo-outdated-root.json
|
||||||
|
cargo outdated --workspace --format json > /tmp/yoi-cargo-outdated-all.json
|
||||||
|
cargo update --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
`cargo outdated --workspace --root-deps-only` reported the following direct workspace dependency updates:
|
||||||
|
|
||||||
|
| Dependency | Current | Latest reported | Kind | Direct users | Note |
|
||||||
|
| --- | ---: | ---: | --- | --- | --- |
|
||||||
|
| `reqwest` | 0.13.2 | 0.13.4 | normal | `llm-worker`, `provider`, `tools` | Patch update; should be combined with the TLS feature normalization follow-up. |
|
||||||
|
| `clap` | 4.6.0 | 4.6.1 | normal/dev | `pod`, `llm-worker` dev | Patch update. |
|
||||||
|
| `minijinja` | 2.19.0 | 2.20.0 | normal | `pod` | Minor update. |
|
||||||
|
| `crossterm` | 0.28.1 | 0.29.0 | normal | `tui` | Also matches the earlier duplicate-version finding with `ratatui`'s backend. |
|
||||||
|
| `pulldown-cmark` | 0.13.3 | 0.13.4 | normal | `tui` | Patch update. |
|
||||||
|
| `html5ever` | 0.26.0 | 0.39.0 | normal | `tools` | Major stack update; treat as WebFetch parser migration work, not routine bump. |
|
||||||
|
| `markup5ever_rcdom` | 0.2.0 | 0.39.0+unofficial | normal | `tools` | Major/unofficial stack update; tied to `html5ever` review. |
|
||||||
|
| `filetime` | 0.2.27 | 0.2.29 | dev | `tools` dev | Dev/test-only patch update. |
|
||||||
|
|
||||||
|
`cargo update --dry-run` reported that 80 locked packages can move to latest compatible versions without editing manifests. Notable compatible lockfile updates include:
|
||||||
|
|
||||||
|
- HTTP/TLS stack: `reqwest 0.13.2 -> 0.13.4`, `hyper 1.9.0 -> 1.10.1`, `h2 0.4.13 -> 0.4.14`, `rustls 0.23.37 -> 0.23.40`, `rustls-native-certs 0.8.3 -> 0.8.4`, `rustls-platform-verifier 0.6.2 -> 0.7.0`, `openssl 0.10.76 -> 0.10.80`, `openssl-sys 0.9.112 -> 0.9.116`, `aws-lc-rs 1.15.2 -> 1.17.0`, `aws-lc-sys 0.35.0 -> 0.41.0`.
|
||||||
|
- Core/runtime stack: `tokio 1.52.1 -> 1.52.3`, `uuid 1.23.1 -> 1.23.2`, `serde_json 1.0.149 -> 1.0.150`, `memchr 2.8.0 -> 2.8.1`, `indexmap 2.13.1 -> 2.14.0`, `socket2 0.6.3 -> 0.6.4`.
|
||||||
|
- UI/parser/dev stack: `minijinja 2.19.0 -> 2.20.0`, `pulldown-cmark 0.13.3 -> 0.13.4`, `filetime 0.2.27 -> 0.2.29`, `serial_test 3.4.0 -> 3.5.0`.
|
||||||
|
- Platform/wasm/windows support crates: multiple `wasm-bindgen`, `web-sys`, `windows-*`, `wasip2`, and `zerocopy` patch/minor updates.
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
|
||||||
|
- There is a low-risk follow-up to run a lockfile refresh (`cargo update`) and validate it, but it should be separate from dependency policy changes because it touches many transitive packages.
|
||||||
|
- Direct manifest bumps can be grouped by risk: small patch/minor bumps (`reqwest`, `clap`, `minijinja`, `pulldown-cmark`, `filetime`) vs behavior/API-sensitive stack bumps (`crossterm`, `html5ever`, `markup5ever_rcdom`).
|
||||||
|
- The `reqwest` bump should not be done blindly before deciding the TLS feature policy, because the current audit already found accidental native-tls/rustls feature duplication.
|
||||||
|
|
||||||
|
## Recommended follow-up tickets
|
||||||
|
|
||||||
|
1. **Add dependency license policy and third-party notice generation**
|
||||||
|
- Acceptance: checked-in `cargo-deny` or equivalent policy; generated/reproducible notices for release artifacts; explicit choices for dual/multi-license crates such as `r-efi`, `Unlicense OR MIT`, and rustls/aws-lc/OpenSSL-marked crates; CI command documented.
|
||||||
|
|
||||||
|
2. **Normalize HTTP TLS backend and Nix OpenSSL dependency**
|
||||||
|
- Acceptance: all direct `reqwest` users consistently disable or enable defaults according to one documented TLS policy; dependency tree no longer unintentionally contains both `native-tls`/OpenSSL and rustls/default TLS paths unless intentionally justified; `package.nix` `openssl`/`pkg-config` inputs are retained or removed according to evidence.
|
||||||
|
|
||||||
|
3. **Deduplicate TUI terminal backend dependencies**
|
||||||
|
- Acceptance: direct `crossterm` version is aligned with `ratatui-crossterm` or duplicate is otherwise justified; TUI/input behavior is validated.
|
||||||
|
|
||||||
|
4. **Evaluate frontmatter YAML parser maintenance**
|
||||||
|
- Acceptance: decide to keep `serde_yaml`, migrate to a maintained fork, or specify a smaller frontmatter subset; include migration/compatibility implications for `.yoi/workflow`, memory, and skill files.
|
||||||
|
|
||||||
|
5. **Optional WebFetch parser weight review**
|
||||||
|
- Acceptance: compare current `html5ever`/`RcDom` extractor with viable maintained alternatives; preserve bounded, safe, link-aware extraction behavior; only proceed if measurable binary/build-time benefit exists.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
Loading…
Reference in New Issue
Block a user