Compare commits

..

No commits in common. "main" and "v0.8.0" have entirely different histories.
main ... v0.8.0

41 changed files with 11373 additions and 70137 deletions

View file

@ -61,7 +61,7 @@ jobs:
set -euo pipefail set -euo pipefail
PKG_DIR="/srv/breadway-dl/dev/bread/${VERSION}" PKG_DIR="/srv/breadway-dl/dev/bread/${VERSION}"
mkdir -p "${PKG_DIR}" mkdir -p "${PKG_DIR}"
for bin in breadd bread bread-emit bread-module-host; do for bin in breadd bread; do
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64" cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
strip "${PKG_DIR}/${bin}-x86_64" strip "${PKG_DIR}/${bin}-x86_64"
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \ sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \

View file

@ -32,7 +32,7 @@ jobs:
VERSION="${GITHUB_REF_NAME#v}" VERSION="${GITHUB_REF_NAME#v}"
PKG_DIR="/srv/breadway-dl/beta/bread/${VERSION}" PKG_DIR="/srv/breadway-dl/beta/bread/${VERSION}"
mkdir -p "${PKG_DIR}" mkdir -p "${PKG_DIR}"
for bin in breadd bread bread-emit bread-module-host; do for bin in breadd bread; do
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64" cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
strip "${PKG_DIR}/${bin}-x86_64" strip "${PKG_DIR}/${bin}-x86_64"
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \ sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \

View file

@ -28,7 +28,7 @@ jobs:
VERSION="${GITHUB_REF_NAME#v}" VERSION="${GITHUB_REF_NAME#v}"
PKG_DIR="/srv/breadway-dl/bread/${VERSION}" PKG_DIR="/srv/breadway-dl/bread/${VERSION}"
mkdir -p "${PKG_DIR}" mkdir -p "${PKG_DIR}"
for bin in breadd bread bread-emit bread-module-host; do for bin in breadd bread; do
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64" cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
strip "${PKG_DIR}/${bin}-x86_64" strip "${PKG_DIR}/${bin}-x86_64"
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \ sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \
@ -48,13 +48,9 @@ jobs:
echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone)" echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone)"
exit 1 exit 1
fi fi
rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true rm -rf /tmp/bread-ecosystem-ci
# mktemp: a fixed clone path races when multiple repos' release git clone https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci
# workflows run close together on the same self-hosted runner. bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh
ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)"
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}"
bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh"
rm -rf "${ECOSYSTEM_CI_DIR}"
- name: upload to GitHub Release - name: upload to GitHub Release
env: env:
@ -68,10 +64,6 @@ jobs:
gh release upload "${GITHUB_REF_NAME}" --repo Breadway/bread \ gh release upload "${GITHUB_REF_NAME}" --repo Breadway/bread \
"${PKG_DIR}/breadd-x86_64" \ "${PKG_DIR}/breadd-x86_64" \
"${PKG_DIR}/bread-x86_64" \ "${PKG_DIR}/bread-x86_64" \
"${PKG_DIR}/bread-emit-x86_64" \
"${PKG_DIR}/bread-module-host-x86_64" \
"${PKG_DIR}/breadd-x86_64.sha256" \ "${PKG_DIR}/breadd-x86_64.sha256" \
"${PKG_DIR}/bread-x86_64.sha256" \ "${PKG_DIR}/bread-x86_64.sha256" \
"${PKG_DIR}/bread-emit-x86_64.sha256" \
"${PKG_DIR}/bread-module-host-x86_64.sha256" \
--clobber --clobber

View file

@ -1,12 +0,0 @@
# Copied from .claude/settings.local.json
[permission]
allow = [
"Bash(cargo test *)",
"Bash(/home/breadway/Projects/bread/target/debug/bread health *)",
"Bash(/home/breadway/Projects/bread/target/debug/bread sync *)",
"Bash(python3 *)",
"Bash(/home/breadway/Projects/bread/target/debug/bread modules *)",
"Bash(/home/breadway/Projects/bread/target/debug/bread doctor *)",
"Bash(/home/breadway/Projects/bread/target/debug/bread reload *)",
"Bash(cargo build *)",
]

View file

@ -1,20 +0,0 @@
# CLAUDE.md — Repo hygiene
This repo follows the branch/release workflow documented in `CONTRIBUTING.md`
— read and follow it for any git, branch, or release work here (the
dev/beta/main lifecycle, `feature/x`/`fix/x` branch naming, when to cut or
reset `beta`, etc). Don't improvise a different workflow.
When starting work on a new feature, create branch "feature/<feature-name>" \
When working on a bug or issue, create branch "fix/<issue you are fixing>"
## Remotes
- `origin` — Forgejo (`git.breadway.dev` via Hestia, SSH) — authoritative.
- `github` — GitHub mirror. Push both when publishing.
## CI
- `dev-release.yml` triggers on `push: branches: ['dev']`; `beta-release.yml`
on `push: branches: ['beta']`; `release.yml` on a `v*` tag push. `package.yml` triggers the same way for the pacman-channel package.
None of these run on plain commits or PRs beyond what's listed.
## Don't
- Don't embed credentials in remote URLs — SSH or a credential helper only.

View file

@ -237,12 +237,6 @@ bread modules audit <name> # Scan a module's Lua source and suggest a
# Hooks # Hooks
bread hooks install-shell [shell] # Install precmd/preexec/chpwd shell hooks (auto-detects $SHELL) bread hooks install-shell [shell] # Install precmd/preexec/chpwd shell hooks (auto-detects $SHELL)
bread hooks install-git # Install git hooks (post-commit/checkout/merge) in the current repo bread hooks install-git # Install git hooks (post-commit/checkout/merge) in the current repo
# Compositor integration
bread init # Install breadbar's Hyprland layer-rule integration (shows a diff, asks to confirm)
bread init --dry-run # Print the proposed diff only, change nothing
bread init --yes # Skip the confirmation prompt (scripted/image builds)
bread init --undo # Remove the previously installed integration
``` ```
--- ---

View file

@ -608,8 +608,3 @@ since = "0.7"
name = "doctor" name = "doctor"
kind = "cli_command" kind = "cli_command"
since = "0.7" since = "0.7"
[[entry]]
name = "init"
kind = "cli_command"
since = "0.8"

View file

@ -13,20 +13,6 @@
//! user later doesn't want it, an rc-file edit is much harder to notice and //! user later doesn't want it, an rc-file edit is much harder to notice and
//! undo than a printed snippet they chose to paste in. //! undo than a printed snippet they chose to paste in.
//! //!
//! `bread init` (`init.rs`) looks like it breaks this rule — it does
//! propose an edit to a user's own `hyprland.lua` — but it's a narrower,
//! consent-gated exception, not a reversal of it. The difference is what's
//! silent about the failure mode: a missing shell hook is invisible
//! enrichment (no telemetry, nothing looks wrong), so leaving it to the
//! user to paste in at their own pace is the right default. A missing
//! compositor layer rule is not invisible — breadbar renders unblurred and
//! with the wrong opacity, which reads as "this app is broken," not
//! "optional feature not enabled," and there's no snippet a user could be
//! expected to reverse-engineer for that. `init.rs` earns the edit by
//! showing the exact diff, requiring explicit consent (prompt, `--yes`, or
//! nothing at all without a TTY), backing up the file first, and shipping
//! a real `--undo`. See `init.rs`'s module docs for the full rationale.
//!
//! # Why bread-emit, not `bread emit` //! # Why bread-emit, not `bread emit`
//! //!
//! The generated hooks shell out to the separate `bread-emit` binary //! The generated hooks shell out to the separate `bread-emit` binary

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,3 @@
mod init;
mod hooks_git; mod hooks_git;
mod hooks_shell; mod hooks_shell;
mod modules_mgmt; mod modules_mgmt;
@ -100,22 +99,6 @@ enum Commands {
#[arg(long)] #[arg(long)]
json: bool, json: bool,
}, },
/// Install bread's Hyprland compositor layer-rule integration
/// (breadbar/breadbox blur, ignore_alpha, animation) into
/// ~/.config/hypr/hyprland.lua, with consent, a backup, and full undo.
/// See bread-cli/src/init.rs for the design rationale.
Init {
/// Print the proposed diff and change nothing
#[arg(long)]
dry_run: bool,
/// Skip the confirmation prompt (for scripted/image builds)
#[arg(long)]
yes: bool,
/// Remove the previously installed marked block instead of
/// installing it
#[arg(long)]
undo: bool,
},
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
@ -241,9 +224,6 @@ async fn main() -> Result<()> {
print_doctor(&socket).await?; print_doctor(&socket).await?;
} }
} }
Commands::Init { dry_run, yes, undo } => {
init::run(dry_run, yes, undo)?;
}
} }
Ok(()) Ok(())
@ -711,7 +691,6 @@ async fn print_doctor(socket: &Path) -> Result<()> {
println!(); println!();
println!(" start the daemon: systemctl --user start breadd"); println!(" start the daemon: systemctl --user start breadd");
println!(" view logs: journalctl --user -u breadd -f"); println!(" view logs: journalctl --user -u breadd -f");
print_compositor_doctor_section();
return Ok(()); return Ok(());
} }
@ -796,20 +775,6 @@ fn render_doctor(health: &Value) {
} }
} }
} }
print_compositor_doctor_section();
}
/// Compositor integration status — pure filesystem check, independent of
/// the daemon, so it prints the same whether breadd is up or not. Reports
/// only; `bread doctor` never installs anything (that's `bread init`'s
/// job).
fn print_compositor_doctor_section() {
println!();
println!("compositor");
for line in init::doctor_report(&init::hypr_dir()) {
println!("{line}");
}
} }
fn config_directory() -> PathBuf { fn config_directory() -> PathBuf {

View file

@ -33,10 +33,6 @@ pub enum IoCommand {
}, },
} }
/// In-flight RPC calls awaiting a response, keyed by request id: each entry
/// is the reply channel for the call that's blocked waiting on it.
type PendingReplies = Arc<Mutex<HashMap<String, mpsc::Sender<Result<Value, String>>>>>;
/// Something the IO thread has for the Lua-driving thread: either an /// Something the IO thread has for the Lua-driving thread: either an
/// unsolicited push (a subscribed event fired, a timer fired) or "the /// unsolicited push (a subscribed event fired, a timer fired) or "the
/// connection is gone" (breadd exited, socket closed, etc). /// connection is gone" (breadd exited, socket closed, etc).
@ -152,7 +148,8 @@ pub fn run(
// Lua-side call is blocked waiting for them; a dedicated thread // Lua-side call is blocked waiting for them; a dedicated thread
// bridges the synchronous `cmd_rx` (fed from the Lua thread) onto an // bridges the synchronous `cmd_rx` (fed from the Lua thread) onto an
// async channel this task can select on. // async channel this task can select on.
let pending: PendingReplies = Arc::new(Mutex::new(HashMap::new())); let pending: Arc<Mutex<HashMap<String, mpsc::Sender<Result<Value, String>>>>> =
Arc::new(Mutex::new(HashMap::new()));
let (async_cmd_tx, mut async_cmd_rx) = tokio::sync::mpsc::unbounded_channel::<IoCommand>(); let (async_cmd_tx, mut async_cmd_rx) = tokio::sync::mpsc::unbounded_channel::<IoCommand>();
std::thread::spawn(move || { std::thread::spawn(move || {

View file

@ -93,7 +93,7 @@ fn json_to_lua<'lua>(lua: &'lua Lua, value: &JsonValue) -> mlua::Result<LuaValue
JsonValue::Bool(b) => LuaValue::Boolean(*b), JsonValue::Bool(b) => LuaValue::Boolean(*b),
JsonValue::Number(n) => { JsonValue::Number(n) => {
if let Some(i) = n.as_i64() { if let Some(i) = n.as_i64() {
LuaValue::Integer(i) LuaValue::Integer(i as i64)
} else { } else {
LuaValue::Number(n.as_f64().unwrap_or(0.0)) LuaValue::Number(n.as_f64().unwrap_or(0.0))
} }
@ -243,7 +243,7 @@ impl ModuleHostLua {
let decode_fn = lua.create_function(|lua, s: String| { let decode_fn = lua.create_function(|lua, s: String| {
match serde_json::from_str::<JsonValue>(&s) { match serde_json::from_str::<JsonValue>(&s) {
Ok(v) => Ok((json_to_lua(lua, &v)?, LuaValue::Nil)), Ok(v) => Ok((json_to_lua(lua, &v)?, LuaValue::Nil)),
Err(e) => Ok((LuaValue::Nil, LuaValue::String(lua.create_string(e.to_string())?))), Err(e) => Ok((LuaValue::Nil, LuaValue::String(lua.create_string(&e.to_string())?))),
} }
})?; })?;
json_tbl.set("decode", decode_fn)?; json_tbl.set("decode", decode_fn)?;

View file

@ -32,9 +32,7 @@ pub const KNOWN_APPS: &[&str] = &[
/// `workspace`, `window`, and `monitor` added (event families the Hyprland /// `workspace`, `window`, and `monitor` added (event families the Hyprland
/// and Bluetooth adapters already published under, but that were missing /// and Bluetooth adapters already published under, but that were missing
/// from this list) when this became a spoofing-prevention boundary and not /// from this list) when this became a spoofing-prevention boundary and not
/// just an app-id-conflict one. Since: v1.7 — command-bus exception. /// just an app-id-conflict one. Since: v1.7 — command-bus exception.*
/// `module`, `state`, `widget`, and `reload` are daemon-synthesized
/// families and must stay unclaimable.*
const RESERVED_DOMAINS: &[&str] = &[ const RESERVED_DOMAINS: &[&str] = &[
"terminal", "terminal",
"git", "git",
@ -55,10 +53,6 @@ const RESERVED_DOMAINS: &[&str] = &[
"workspace", "workspace",
"window", "window",
"monitor", "monitor",
"module",
"state",
"widget",
"reload",
]; ];
/// Whether `id` is a registered sibling-app id. /// Whether `id` is a registered sibling-app id.
@ -180,10 +174,6 @@ mod tests {
"monitor", "monitor",
"window", "window",
"system", "system",
"module",
"state",
"widget",
"reload",
] { ] {
assert!( assert!(
is_reserved_domain(domain), is_reserved_domain(domain),

View file

@ -260,7 +260,7 @@ pub async fn run_state_engine(
} }
if let (Some(before), Some(after)) = (before_snapshot, after_snapshot) { if let (Some(before), Some(after)) = (before_snapshot, after_snapshot) {
for path in watches.values() { for (_id, path) in watches.iter() {
let old_val = value_at_path(&before, path).unwrap_or(Value::Null); let old_val = value_at_path(&before, path).unwrap_or(Value::Null);
let new_val = value_at_path(&after, path).unwrap_or(Value::Null); let new_val = value_at_path(&after, path).unwrap_or(Value::Null);
if old_val != new_val { if old_val != new_val {

View file

@ -25,7 +25,6 @@
//! directly from Lua bypasses every check in this file — that's the //! directly from Lua bypasses every check in this file — that's the
//! scenario the sandbox exists for, not this file. //! scenario the sandbox exists for, not this file.
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Component, Path, PathBuf};
use std::time::Duration; use std::time::Duration;
use bread_shared::{glob, ModuleHostHello, ModuleHostPush, ModulePermission, PermissionKind}; use bread_shared::{glob, ModuleHostHello, ModuleHostPush, ModulePermission, PermissionKind};
@ -350,7 +349,10 @@ impl Server {
Ok(json!({ "ok": true })) Ok(json!({ "ok": true }))
} }
"module_host.fs_read" => { "module_host.fs_read" => {
if !permissions.iter().any(|p| p.kind == PermissionKind::FsRead) { if !permissions
.iter()
.any(|p| p.kind == PermissionKind::FsRead)
{
return Err("fs.read not granted to this module".to_string()); return Err("fs.read not granted to this module".to_string());
} }
let path = req let path = req
@ -409,17 +411,16 @@ impl Server {
.and_then(Value::as_str) .and_then(Value::as_str)
.ok_or("missing cmd")? .ok_or("missing cmd")?
.to_string(); .to_string();
let argv = exec_argv(permissions, &cmd)?; if !bin_allowed(permissions, &cmd) {
return Err("command is outside this module's granted exec bin scope".to_string());
}
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
match std::process::Command::new(&argv[0]) match std::process::Command::new("sh").arg("-c").arg(&cmd).status() {
.args(&argv[1..])
.status()
{
Ok(status) if !status.success() => { Ok(status) if !status.success() => {
warn!(cmd = %argv[0], code = ?status.code(), "module_host.exec exited non-zero"); warn!(cmd = %cmd, code = ?status.code(), "module_host.exec exited non-zero");
} }
Err(e) => { Err(e) => {
error!(cmd = %argv[0], error = %e, "module_host.exec failed to spawn"); error!(cmd = %cmd, error = %e, "module_host.exec failed to spawn");
} }
_ => {} _ => {}
} }
@ -436,16 +437,17 @@ impl Server {
.and_then(Value::as_str) .and_then(Value::as_str)
.ok_or("missing cmd")? .ok_or("missing cmd")?
.to_string(); .to_string();
let argv = exec_argv(permissions, &cmd)?; if !bin_allowed(permissions, &cmd) {
return Err("command is outside this module's granted exec bin scope".to_string());
}
let timeout_ms = req let timeout_ms = req
.params .params
.get("timeout_ms") .get("timeout_ms")
.and_then(Value::as_u64) .and_then(Value::as_u64)
.unwrap_or(2000); .unwrap_or(2000);
let handle = tokio::task::spawn_blocking(move || { let handle =
std::process::Command::new(&argv[0]) tokio::task::spawn_blocking(move || {
.args(&argv[1..]) std::process::Command::new("sh").arg("-c").arg(&cmd).output()
.output()
}); });
match tokio::time::timeout(Duration::from_millis(timeout_ms + 500), handle).await { match tokio::time::timeout(Duration::from_millis(timeout_ms + 500), handle).await {
Ok(Ok(Ok(out))) => Ok(json!({ Ok(Ok(Ok(out))) => Ok(json!({
@ -472,9 +474,7 @@ impl Server {
( (
ModuleLoadState::LoadError, ModuleLoadState::LoadError,
ModuleHostOutcome::LoadError( ModuleHostOutcome::LoadError(
error error.clone().unwrap_or_else(|| "module load failed".to_string()),
.clone()
.unwrap_or_else(|| "module load failed".to_string()),
), ),
) )
}; };
@ -501,14 +501,13 @@ impl Server {
/// Belt-and-suspenders path scoping for `fs_read`/`fs_write`: if the /// Belt-and-suspenders path scoping for `fs_read`/`fs_write`: if the
/// manifest declared a `path` hint for this permission kind, the requested /// manifest declared a `path` hint for this permission kind, the requested
/// path (after `~`-expansion and canonicalize) must be a real descendant /// path (after `~`-expansion) must fall under at least one granted prefix.
/// of at least one granted prefix. No hint at all means this RPC-level /// No hint at all means this RPC-level check stays permissive (matching
/// check stays permissive (matching Workstream D's existing "un-hinted /// Workstream D's existing "un-hinted grant = ungated within that
/// grant = ungated within that namespace" behavior) — Landlock's own /// namespace" behavior) — Landlock's own ruleset (built independently in
/// ruleset (built independently in `module_host::apply_sandbox`) does NOT /// `module_host::apply_sandbox`) does NOT grant a filesystem rule for an
/// grant a filesystem rule for an un-hinted permission, so the direct /// un-hinted permission, so the direct `os`/`io` escape hatch remains
/// `os`/`io` escape hatch remains kernel-denied for that case regardless /// kernel-denied for that case regardless of what this function returns.
/// of what this function returns.
fn path_allowed(permissions: &[ModulePermission], kind: PermissionKind, path: &str) -> bool { fn path_allowed(permissions: &[ModulePermission], kind: PermissionKind, path: &str) -> bool {
let hints: Vec<&String> = permissions let hints: Vec<&String> = permissions
.iter() .iter()
@ -518,93 +517,18 @@ fn path_allowed(permissions: &[ModulePermission], kind: PermissionKind, path: &s
if hints.is_empty() { if hints.is_empty() {
return true; return true;
} }
let Some(requested) = resolve_scoped_path(path) else { let expanded = bread_shared::expand_path(path);
return false;
};
hints.iter().any(|hint| { hints.iter().any(|hint| {
let Some(granted) = resolve_scoped_path(hint) else { let hint_expanded = bread_shared::expand_path(hint);
return false; expanded.starts_with(&hint_expanded)
};
// Path::starts_with is component-wise; str::starts_with would let
// `/Wallpapers-evil` match a `/Wallpapers` grant.
requested.starts_with(&granted)
}) })
} }
/// Canonicalize for containment: existing paths resolve symlinks and `..`; /// Same idea as [`path_allowed`] for `exec`'s `bin` hint: compares by file
/// new files canonicalize the parent then re-join the filename. Anything /// name (so `bin = "hyprpaper"` matches a command invoking
/// that still contains `..` after lexical normalization is rejected. /// `/usr/bin/hyprpaper` as well as a bare `hyprpaper`) or an exact leading
fn resolve_scoped_path(path: &str) -> Option<PathBuf> { /// token match.
let expanded = bread_shared::expand_path(path); fn bin_allowed(permissions: &[ModulePermission], cmd: &str) -> bool {
if let Ok(canon) = expanded.canonicalize() {
return Some(canon);
}
let parent = expanded.parent().filter(|p| !p.as_os_str().is_empty());
if let (Some(parent), Some(name)) = (parent, expanded.file_name()) {
if let Ok(parent_canon) = parent.canonicalize() {
return Some(parent_canon.join(name));
}
}
lexical_abs(&expanded)
}
fn lexical_abs(path: &Path) -> Option<PathBuf> {
let abs = if path.is_absolute() {
path.to_path_buf()
} else {
std::path::absolute(path).ok()?
};
let mut out = PathBuf::new();
for c in abs.components() {
match c {
Component::CurDir => {}
Component::ParentDir => {
if !out.pop() {
return None;
}
}
rest => out.push(rest),
}
}
if out.components().any(|c| matches!(c, Component::ParentDir)) {
return None;
}
Some(out)
}
/// Split `cmd` into argv without a shell. Metacharacters are rejected so a
/// hinted binary cannot smuggle extra commands (`hyprpaper; curl evil`).
fn parse_exec_argv(cmd: &str) -> Option<Vec<String>> {
if cmd.chars().any(|c| {
matches!(
c,
'|' | ';' | '&' | '$' | '`' | '\n' | '\r' | '<' | '>' | '(' | ')'
)
}) {
return None;
}
let argv: Vec<String> = cmd.split_whitespace().map(str::to_string).collect();
if argv.is_empty() {
None
} else {
Some(argv)
}
}
/// Parse `cmd` and enforce the exec `bin` hint against argv[0].
fn exec_argv(permissions: &[ModulePermission], cmd: &str) -> Result<Vec<String>, String> {
let argv = parse_exec_argv(cmd)
.ok_or_else(|| "command contains shell metacharacters or is empty".to_string())?;
if !bin_allowed(permissions, &argv[0]) {
return Err("command is outside this module's granted exec bin scope".to_string());
}
Ok(argv)
}
/// Same idea as [`path_allowed`] for `exec`'s `bin` hint: compares argv[0]
/// by file name (so `bin = "hyprpaper"` matches `/usr/bin/hyprpaper` as
/// well as a bare `hyprpaper`) or an exact path match.
fn bin_allowed(permissions: &[ModulePermission], program: &str) -> bool {
let hints: Vec<&String> = permissions let hints: Vec<&String> = permissions
.iter() .iter()
.filter(|p| p.kind == PermissionKind::Exec) .filter(|p| p.kind == PermissionKind::Exec)
@ -613,109 +537,16 @@ fn bin_allowed(permissions: &[ModulePermission], program: &str) -> bool {
if hints.is_empty() { if hints.is_empty() {
return true; return true;
} }
let cmd_leaf = Path::new(program) let first_word = cmd.split_whitespace().next().unwrap_or("");
let cmd_leaf = std::path::Path::new(first_word)
.file_name() .file_name()
.and_then(|f| f.to_str()) .and_then(|f| f.to_str())
.unwrap_or(program); .unwrap_or(first_word);
hints.iter().any(|hint| { hints.iter().any(|hint| {
let hint_leaf = Path::new(hint.as_str()) let hint_leaf = std::path::Path::new(hint.as_str())
.file_name() .file_name()
.and_then(|f| f.to_str()) .and_then(|f| f.to_str())
.unwrap_or(hint.as_str()); .unwrap_or(hint.as_str());
cmd_leaf == hint_leaf || program == hint.as_str() cmd_leaf == hint_leaf || first_word == hint.as_str()
}) })
} }
#[cfg(test)]
mod tests {
use super::*;
fn fs_grant(kind: PermissionKind, path: &str) -> Vec<ModulePermission> {
vec![ModulePermission {
kind,
path: Some(path.to_string()),
bin: None,
}]
}
fn exec_grant(bin: &str) -> Vec<ModulePermission> {
vec![ModulePermission {
kind: PermissionKind::Exec,
path: None,
bin: Some(bin.to_string()),
}]
}
#[test]
fn path_allowed_denies_dotdot_escape_from_granted_prefix() {
let tmp = tempfile::TempDir::new().unwrap();
let wallpapers = tmp.path().join("Wallpapers");
std::fs::create_dir(&wallpapers).unwrap();
let granted = wallpapers.to_str().unwrap();
let perms = fs_grant(PermissionKind::FsRead, granted);
let escape = wallpapers.join("../../.ssh/id_rsa");
assert!(
!path_allowed(&perms, PermissionKind::FsRead, escape.to_str().unwrap()),
"Wallpapers/../../.ssh/id_rsa must not match a Wallpapers grant"
);
}
#[test]
fn path_allowed_denies_string_prefix_sibling() {
let tmp = tempfile::TempDir::new().unwrap();
let wallpapers = tmp.path().join("Wallpapers");
let evil = tmp.path().join("Wallpapers-evil");
std::fs::create_dir(&wallpapers).unwrap();
std::fs::create_dir(&evil).unwrap();
let secret = evil.join("secret");
std::fs::write(&secret, "x").unwrap();
let perms = fs_grant(PermissionKind::FsRead, wallpapers.to_str().unwrap());
assert!(!path_allowed(
&perms,
PermissionKind::FsRead,
secret.to_str().unwrap()
));
}
#[test]
fn path_allowed_accepts_real_descendant_and_new_file() {
let tmp = tempfile::TempDir::new().unwrap();
let wallpapers = tmp.path().join("Wallpapers");
std::fs::create_dir(&wallpapers).unwrap();
let existing = wallpapers.join("bg.png");
std::fs::write(&existing, "x").unwrap();
let perms = fs_grant(PermissionKind::FsWrite, wallpapers.to_str().unwrap());
assert!(path_allowed(
&perms,
PermissionKind::FsWrite,
existing.to_str().unwrap()
));
let new_file = wallpapers.join("new.png");
assert!(path_allowed(
&perms,
PermissionKind::FsWrite,
new_file.to_str().unwrap()
));
}
#[test]
fn exec_argv_denies_shell_chaining_when_bin_hinted() {
let perms = exec_grant("hyprpaper");
assert!(exec_argv(&perms, "hyprpaper; curl evil").is_err());
assert!(exec_argv(&perms, "hyprpaper").is_ok());
assert!(exec_argv(&perms, "/usr/bin/hyprpaper --config x").is_ok());
assert!(exec_argv(&perms, "curl evil").is_err());
}
#[test]
fn parse_exec_argv_rejects_metacharacters() {
assert!(parse_exec_argv("hyprpaper; curl evil").is_none());
assert!(parse_exec_argv("hyprpaper | curl evil").is_none());
assert!(parse_exec_argv("hyprpaper && curl evil").is_none());
assert_eq!(
parse_exec_argv("hyprpaper --config x"),
Some(vec!["hyprpaper".into(), "--config".into(), "x".into()])
);
}
}

View file

@ -137,9 +137,11 @@ enabled = false
async fn wait_until_ready(&self) -> Result<()> { async fn wait_until_ready(&self) -> Result<()> {
let deadline = Instant::now() + Duration::from_secs(8); let deadline = Instant::now() + Duration::from_secs(8);
while Instant::now() < deadline { while Instant::now() < deadline {
if self.socket_path.exists() && self.send_request("ping", json!({})).await.is_ok() { if self.socket_path.exists() {
if self.send_request("ping", json!({})).await.is_ok() {
return Ok(()); return Ok(());
} }
}
sleep(Duration::from_millis(100)).await; sleep(Duration::from_millis(100)).await;
} }
Err(anyhow!("daemon did not become ready in time")) Err(anyhow!("daemon did not become ready in time"))

View file

@ -37,7 +37,6 @@ suggested `[[permissions]]` block for its own `bread.module.toml`.
| `low-battery-warning.lua` | Critical notification once when the battery runs low; resets on AC. | none | | `low-battery-warning.lua` | Critical notification once when the battery runs low; resets on AC. | none |
| `pause-media-on-headphone-unplug.lua` | Runs `playerctl pause` when a headphone/earbud device disconnects. | none (needs `playerctl`) | | `pause-media-on-headphone-unplug.lua` | Runs `playerctl pause` when a headphone/earbud device disconnects. | none (needs `playerctl`) |
| `dock-monitors.lua` | Applies a multi-monitor layout when an external display connects, reverts when removed. | edit output names/resolutions | | `dock-monitors.lua` | Applies a multi-monitor layout when an external display connects, reverts when removed. | edit output names/resolutions |
| `external-monitors.lua` | Zero-config laptop displays: any HDMI/DP/USB-C head at its preferred mode, mirrored by default (or extended), lid-safe, restores the panel on unplug. | optional `ARRANGE` / `SCALE` at the top |
| `active-window-widget.lua` | Shows the focused window next to the workspace pills in breadbar, via `bread.widget` + `bread.state.watch`. | none | | `active-window-widget.lua` | Shows the focused window next to the workspace pills in breadbar, via `bread.widget` + `bread.state.watch`. | none |
| `cpu-temp-widget/` | Live CPU temperature readout in breadbar's stats area, via `bread.widget` + `bread.fs.read` on a timer. Directory module with a `bread.module.toml` declaring `fs.read` + `widget` — the permission-manifest worked example. | edit `TEMP_PATH` for your hwmon layout | | `cpu-temp-widget/` | Live CPU temperature readout in breadbar's stats area, via `bread.widget` + `bread.fs.read` on a timer. Directory module with a `bread.module.toml` declaring `fs.read` + `widget` — the permission-manifest worked example. | edit `TEMP_PATH` for your hwmon layout |
| `bluetooth-toggle-widget.lua` | One-click Bluetooth power toggle in breadbar's tray, via `bread.widget` + a click handler. | none | | `bluetooth-toggle-widget.lua` | One-click Bluetooth power toggle in breadbar's tray, via `bread.widget` + a click handler. | none |

View file

@ -1,228 +0,0 @@
-- external-monitors — behave like a normal laptop desktop
--
-- Plug in any display (HDMI, DisplayPort, USB-C dock, a random TV) and
-- the session just works. No output names to edit.
--
-- • the laptop panel stays at its preferred (native) mode
-- • each external uses its preferred mode and refresh
-- • new screens clone the laptop (set ARRANGE = "extend" to sit to the right)
-- • closing the lid does not sleep while an external is on
-- • unplug everything and the laptop is the only display again
--
-- Drop-in: copy to ~/.config/bread/modules/ and `bread reload`.
local M = bread.module({
name = "external-monitors",
version = "1.0.0",
after = { "bread.monitors" },
})
-- "mirror" = every external clones the laptop (presentations, TVs)
-- "extend" = extra desktop to the right
local ARRANGE = "mirror"
local SCALE = "auto"
local INTERNAL_RE = "^eDP"
local INHIBITOR = "/tmp/bread-lid-inhibitor.pid"
local function inhibit_lid()
if bread.fs.exists(INHIBITOR) then return end
bread.exec(
"bash -c 'systemd-inhibit --what=handle-lid-switch --who=bread "
.. "--why=external-display sleep infinity & echo $! > "
.. INHIBITOR
.. "'"
)
end
local function release_lid()
bread.exec(
"bash -c 'kill $(cat " .. INHIBITOR .. " 2>/dev/null) 2>/dev/null; rm -f " .. INHIBITOR .. "'"
)
end
local function is_internal(name)
return type(name) == "string" and name:match(INTERNAL_RE) ~= nil
end
local function drm_status(name)
for card = 0, 5 do
local raw = bread.fs.read(string.format("/sys/class/drm/card%d-%s/status", card, name))
if raw then
return raw:match("^%s*(%S+)")
end
end
return nil
end
local function drm_first_mode(name)
for card = 0, 5 do
local raw = bread.fs.read(string.format("/sys/class/drm/card%d-%s/modes", card, name))
if raw then
local w, h = raw:match("(%d+)x(%d+)")
if w then
return tonumber(w), tonumber(h)
end
end
end
return 1920, 1080
end
local function list_connectors()
local names = {}
local ok, out = bread.exec_capture("ls /sys/class/drm", { timeout_ms = 500 })
if not ok or not out then
return names
end
for ent in out:gmatch("[^%s]+") do
local name = ent:match("^card%d+%-(.+)$")
if name and not name:match("^Writeback") then
names[#names + 1] = name
end
end
table.sort(names)
return names
end
local function connected()
local internal, externals = nil, {}
for _, name in ipairs(list_connectors()) do
if drm_status(name) == "connected" then
if is_internal(name) then
internal = internal or name
else
externals[#externals + 1] = name
end
end
end
return internal or "eDP-1", externals
end
-- BOS Hyprland talks Lua (`hl.monitor`). Stock Hyprland uses the
-- `monitor=` keyword. Try eval first, then keyword.
local function apply_monitor(opts)
local extra = ""
if opts.mirror and opts.mirror ~= "" then
extra = string.format(", mirror = %q", opts.mirror)
end
local expr = string.format(
"hl.monitor({ output = %q, mode = %q, position = %q, scale = %q%s })",
opts.output,
opts.mode or "preferred",
opts.position or "0x0",
opts.scale or SCALE,
extra
)
local resp = bread.hyprland.eval(expr)
if type(resp) == "string" and resp:match("error") then
local spec = string.format(
"%s, %s, %s, %s",
opts.output,
opts.mode or "preferred",
opts.position or "0x0",
opts.scale or SCALE
)
if opts.mirror and opts.mirror ~= "" then
spec = spec .. ", mirror, " .. opts.mirror
end
bread.hyprland.keyword("monitor", spec)
end
end
local function apply(internal, externals)
apply_monitor({
output = internal,
mode = "preferred",
position = "0x0",
scale = SCALE,
})
if ARRANGE == "mirror" then
for _, name in ipairs(externals) do
apply_monitor({
output = name,
mode = "preferred",
position = "0x0",
scale = SCALE,
mirror = internal,
})
end
return
end
local x = select(1, drm_first_mode(internal)) or 1920
for _, name in ipairs(externals) do
apply_monitor({
output = name,
mode = "preferred",
position = x .. "x0",
scale = SCALE,
})
local w = select(1, drm_first_mode(name)) or 1920
x = x + w
end
end
function M.on_load()
local last = nil
local applied = false
local function evaluate()
local internal, externals = connected()
local sig = internal .. "|" .. table.concat(externals, ",")
if sig == last then
return
end
last = sig
if #externals == 0 then
if applied then
apply_monitor({
output = internal,
mode = "preferred",
position = "0x0",
scale = SCALE,
})
release_lid()
applied = false
end
return
end
apply(internal, externals)
inhibit_lid()
applied = true
bread.log("[external-monitors] " .. internal .. " + " .. table.concat(externals, ", "))
end
local settle = bread.debounce(1500, evaluate)
bread.on("bread.hyprland.monitor.connected", function(event)
local name = event.data and event.data.name
if name and not is_internal(name) then
bread.notify("Display connected: " .. name, { urgency = "low" })
end
settle()
end)
bread.on("bread.hyprland.monitor.disconnected", function()
settle()
end)
bread.on("bread.device.**", function(event)
local sub = event.data and event.data.subsystem
if sub == "drm" then
settle()
end
end)
bread.hyprland.on_raw("configreloaded", function()
last = nil
evaluate()
end)
bread.every(3000, evaluate)
settle()
end
return M

View file

@ -1,5 +1,5 @@
{ {
"0": "LuaEngine", "0": "lua/mod.rs",
"1": "RawEvent", "1": "RawEvent",
"2": "config.rs", "2": "config.rs",
"3": "Server", "3": "Server",
@ -15,24 +15,24 @@
"13": "bread-cli/src/main.rs", "13": "bread-cli/src/main.rs",
"14": "types.rs", "14": "types.rs",
"15": "podman.rs", "15": "podman.rs",
"16": "Value", "16": "StateHandle",
"17": "Result", "17": "run_udev_monitor",
"18": "hooks_shell.rs", "18": "hooks_shell.rs",
"19": "bluetooth.rs", "19": "bluetooth.rs",
"20": "SubscriptionId", "20": "SubscriptionId",
"21": "glob.rs", "21": "glob.rs",
"22": "StateHandle", "22": "run_state_engine",
"23": "Adapter", "23": "RtnetlinkAdapter",
"24": "network.rs", "24": "network.rs",
"25": "power.rs", "25": "power.rs",
"26": "hyprland.rs", "26": "hyprland.rs",
"27": "parse_upower_message", "27": "Adapter",
"28": "main Branch", "28": "dl.breadway.dev Distribution",
"29": "git-branch-widget.lua", "29": "git-branch-widget.lua",
"30": "TestHarness", "30": "TestHarness",
"31": "Sync", "31": "Sync",
"32": "active-window-widget.lua", "32": "active-window-widget.lua",
"33": ".new", "33": "cpu-temp-widget.lua",
"34": "focus-mode-widget.lua", "34": "focus-mode-widget.lua",
"35": "workflow-status-widget.lua", "35": "workflow-status-widget.lua",
"36": "bread.widget API", "36": "bread.widget API",
@ -59,10 +59,10 @@
"57": "bread.system.startup", "57": "bread.system.startup",
"58": "Monitors Configuration Example", "58": "Monitors Configuration Example",
"59": "Binds Module (Built-in)", "59": "Binds Module (Built-in)",
"60": "lua/mod.rs", "60": "Bakery Package Manager",
"61": "external-monitors.lua", "61": "Phase 3: GUI Control Center",
"62": "AGENTS.md — Repo hygiene", "62": "Phase 5: Cross-Device Mesh",
"63": "CLAUDE.md — Repo hygiene", "63": "Dev Version Computation",
"64": "ModuleHostLua", "64": "ModuleHostLua",
"65": "ModuleHostRegistry", "65": "ModuleHostRegistry",
"66": "xtask/src/main.rs", "66": "xtask/src/main.rs",
@ -86,10 +86,5 @@
"84": "State", "84": "State",
"85": "init.lua", "85": "init.lua",
"86": "Execution", "86": "Execution",
"87": "packaging/README.md", "87": "packaging/README.md"
"88": "PathBuf",
"89": "build.sh",
"90": "beta Release Track",
"91": "dev Release Track",
"92": "stable Release Track"
} }

View file

@ -1 +0,0 @@
{"0": "dff8ca2baa85bb77", "1": "2213379d8e986e1b", "2": "055a5a9a2c0abb47", "3": "4e76d4b9b12f60a0", "4": "15d25492162f61da", "5": "c9e7c9b0b9f9532c", "6": "5dc03258b399572c", "7": "7e3d24ea3cd2b4cb", "8": "d2b23fb40be07856", "9": "c63bb5497aa74cc4", "10": "37be3ba834804202", "11": "e820233ee9e24e6c", "12": "90afbdba0e9131b5", "13": "a893df0933556d63", "14": "2b140ca807922de5", "15": "77fe3c708c10a92e", "16": "f46d2bf3ec15783d", "17": "9d5d3a5ebfca7e96", "18": "4b221b9f82894754", "19": "1af003225b1510f7", "20": "cec4f1891a3503a7", "21": "af54da4748cbfe19", "22": "755f92ef4fb448a8", "23": "8d61c62e8cd29e24", "24": "a6096a26ebcbe7ca", "25": "4bb3423b0748a5b8", "26": "f14069a977bb7109", "27": "c24364ae8589386f", "28": "b7dfd1e572840cf9", "29": "38bd4a3ed7d82df4", "30": "c7a227c868287ece", "31": "8298f357ade782b6", "32": "8c3f9c59418c0d78", "33": "d441528441bffad6", "34": "0ca065898bae7a4c", "35": "91d64164a425221e", "36": "8f4f1c63c1339596", "37": "f29f07965a593c46", "38": "69c04956206264ea", "39": "d5e6784916307537", "40": "9dd3e8ecc75d42d1", "41": "975bc2eaec9ade41", "42": "c119297bd4e771d7", "43": "5df098735d8e8ccf", "44": "3ea089711b226db5", "45": "937d314565bd1f7c", "46": "644deb50d1523213", "47": "a8ba58be12f4eaaf", "48": "07122a351039caad", "49": "85df939415e8bf76", "50": "ccecca45fa071d89", "51": "d12f6c2877bf28fc", "52": "8fde0947b8e44878", "53": "1894c9960c737861", "54": "8eb6456a35bc1424", "55": "266c21cbaa157140", "56": "3dd39e27b03e805b", "57": "9914e60060e52831", "58": "082ed57548a0aa26", "59": "1d8fa6f90af6c7a9", "60": "5b3a15eab473b681", "61": "53961581c79663fe", "62": "1c3daa39aab41ca2", "63": "cd7e1aedbdbb382a", "64": "793a68e5b584e15a", "65": "e5048b5a19d78c81", "66": "abaa846ce9fdc66e", "67": "a46e2827ad3261b0", "68": "56d1c4600f18d094", "69": "b29197139c1a5fc7", "70": "f6f5196c6a2f3084", "71": "f8b572524780f968", "72": "95bd136ecb3896b6", "73": "a2e1d482f5282a2b", "74": "96279e53156461bf", "75": "3c234d3ea2845544", "76": "37f5bfa38e9dda4b", "77": "c95e6689883cdc3d", "78": "784aa039bef2aabf", "79": "1e1cd6b488d8850c", "80": "42213e202400e524", "81": "bb8f3086e24c3f87", "82": "8f000e4f1c6edcd1", "83": "19573b318feb4438", "84": "47016ab7f59f2ab4", "85": "da1e8594a8fb071a", "86": "d730f8d81a4b91ff", "87": "af426938b90fa391", "88": "dce55fb2c83882bb", "89": "9d30c290d7898f81", "90": "356e8182b8067c22", "91": "825919e98a656895", "92": "c5d2ff6dacf845a7"}

View file

@ -1,90 +0,0 @@
{
"0": "lua/mod.rs",
"1": "RawEvent",
"2": "config.rs",
"3": "Server",
"4": "widget.rs",
"5": "Bread Daemon (breadd)",
"6": "filesystem.rs",
"7": "git.rs",
"8": "Result",
"9": "state_engine.rs",
"10": "modules_mgmt.rs",
"11": "systemd.rs",
"12": "hooks_git.rs",
"13": "bread-cli/src/main.rs",
"14": "types.rs",
"15": "podman.rs",
"16": "StateHandle",
"17": "run_udev_monitor",
"18": "hooks_shell.rs",
"19": "bluetooth.rs",
"20": "SubscriptionId",
"21": "glob.rs",
"22": "run_state_engine",
"23": "RtnetlinkAdapter",
"24": "network.rs",
"25": "power.rs",
"26": "hyprland.rs",
"27": "Adapter",
"28": "dl.breadway.dev Distribution",
"29": "git-branch-widget.lua",
"30": "TestHarness",
"31": "Sync",
"32": "active-window-widget.lua",
"33": "cpu-temp-widget.lua",
"34": "focus-mode-widget.lua",
"35": "workflow-status-widget.lua",
"36": "bread.widget API",
"37": "bluetooth-toggle-widget.lua",
"38": "pause-media-on-headphone-unplug.lua",
"39": "Autostart Example Module",
"40": "dock-monitors.lua",
"41": "dock-workflow.lua",
"42": "low-battery-warning.lua",
"43": "install.sh",
"44": "Filesystem Adapter",
"45": "Git Adapter",
"46": "Podman Adapter",
"47": "Systemd Adapter",
"48": "bread.after(delay_ms, fn)",
"49": "bread.bluetooth namespace",
"50": "bread.every(interval_ms, fn)",
"51": "bread.exec(cmd)",
"52": "bread.hyprland namespace",
"53": "bread.notify(message, opts)",
"54": "bread.state.watch(path, fn)",
"55": "bread-cli/src/lib.rs",
"56": "core/mod.rs",
"57": "bread.system.startup",
"58": "Monitors Configuration Example",
"59": "Binds Module (Built-in)",
"60": "Bakery Package Manager",
"61": "Phase 3: GUI Control Center",
"62": "Phase 5: Cross-Device Mesh",
"63": "Dev Version Computation",
"64": "ModuleHostLua",
"65": "ModuleHostRegistry",
"66": "xtask/src/main.rs",
"67": "Bread",
"68": "rules.rs",
"69": "Normalized events",
"70": "Bread Documentation",
"71": "udev.rs",
"72": "Dictionary: Lua API",
"73": "Out-of-process module sandboxing *(Since: v1.6)*",
"74": "Dictionary: Built-in modules",
"75": "Events",
"76": "Machine and filesystem",
"77": "Contributing",
"78": "Bluetooth",
"79": "Widgets *(Since: v1.3)*",
"80": "Getting started",
"81": "Capability-scoped modules *(Since: v1.5)*",
"82": "Workflows *(Since: v1.2)*",
"83": "Timers",
"84": "State",
"85": "init.lua",
"86": "Execution",
"87": "packaging/README.md"
}

View file

@ -1,397 +0,0 @@
# Graph Report - bread (2026-08-15)
## Corpus Check
- 62 files · ~88,250 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 1467 nodes · 3347 edges · 88 communities (64 shown, 24 thin omitted)
- Extraction: 99% EXTRACTED · 1% INFERRED · 0% AMBIGUOUS · INFERRED: 48 edges (avg confidence: 0.78)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `a6973360`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation)
- lua/mod.rs
- RawEvent
- config.rs
- Server
- widget.rs
- Bread Daemon (breadd)
- filesystem.rs
- git.rs
- Result
- state_engine.rs
- modules_mgmt.rs
- systemd.rs
- hooks_git.rs
- bread-cli/src/main.rs
- types.rs
- podman.rs
- StateHandle
- run_udev_monitor
- hooks_shell.rs
- bluetooth.rs
- SubscriptionId
- glob.rs
- run_state_engine
- RtnetlinkAdapter
- network.rs
- power.rs
- hyprland.rs
- Adapter
- dl.breadway.dev Distribution
- git-branch-widget.lua
- TestHarness
- Sync
- active-window-widget.lua
- cpu-temp-widget.lua
- focus-mode-widget.lua
- workflow-status-widget.lua
- bread.widget API
- bluetooth-toggle-widget.lua
- pause-media-on-headphone-unplug.lua
- Autostart Example Module
- install.sh
- Filesystem Adapter
- Git Adapter
- Podman Adapter
- Systemd Adapter
- bread.after(delay_ms, fn)
- bread.bluetooth namespace
- bread.every(interval_ms, fn)
- bread.exec(cmd)
- bread.hyprland namespace
- bread.notify(message, opts)
- bread.state.watch(path, fn)
- bread.system.startup
- Monitors Configuration Example
- Binds Module (Built-in)
- Bakery Package Manager
- Phase 3: GUI Control Center
- Phase 5: Cross-Device Mesh
- Dev Version Computation
- ModuleHostLua
- ModuleHostRegistry
- xtask/src/main.rs
- Bread
- rules.rs
- Normalized events
- Bread Documentation
- udev.rs
- Dictionary: Lua API
- Out-of-process module sandboxing *(Since: v1.6)*
- Dictionary: Built-in modules
- Events
- Machine and filesystem
- Contributing
- Bluetooth
- Widgets *(Since: v1.3)*
- Getting started
- Capability-scoped modules *(Since: v1.5)*
- Workflows *(Since: v1.2)*
- Timers
- State
- init.lua
- Execution
- packaging/README.md
## God Nodes (most connected - your core abstractions)
1. `LuaEngine` - 59 edges
2. `RawEvent` - 49 edges
3. `BreadEvent` - 38 edges
4. `raw()` - 37 edges
5. `RuntimeState` - 34 edges
6. `StateHandle` - 28 edges
7. `now_unix_ms()` - 26 edges
8. `Adapter` - 25 edges
9. `SubscriptionId` - 25 edges
10. `ModuleHostLua` - 24 edges
## Surprising Connections (you probably didn't know these)
- `parse_bluetooth_message()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/bluetooth.rs → bread-shared/src/lib.rs
- `try_enumerate()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/bluetooth.rs → bread-shared/src/lib.rs
- `classify()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/filesystem.rs → bread-shared/src/lib.rs
- `network_raw_event()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/network.rs → bread-shared/src/lib.rs
- `power_raw_event()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/power.rs → bread-shared/src/lib.rs
## Import Cycles
- 2-file cycle: `breadd/src/core/state_engine.rs -> breadd/src/lua/mod.rs -> breadd/src/core/state_engine.rs`
- 2-file cycle: `bread-shared/src/lib.rs -> bread-shared/src/module_host_ipc.rs -> bread-shared/src/lib.rs`
## Hyperedges (group relationships)
- **** — ci_dev_release, workflow_version_compute, release_track_dev, distribution_dl_breadway_dev, package_bakery [INFERRED]
- **** — adapter_udev, adapter_hyprland, adapter_power, sys_bread_daemon, api_bread_on, sys_lua_runtime [INFERRED]
- **** — config_init_lua, config_modules_dir, pattern_module_skeleton, api_bread_on, sys_lua_runtime [INFERRED]
- **** — api_bread_workflow, api_bread_spawn, api_bread_wait, example_dock_workflow, api_bread_notify [INFERRED]
- **** — api_bread_widget, api_bread_every, example_widget_cpu_temp, api_bread_state_watch [INFERRED]
- **** — branch_main, ci_dev_release, ci_rc_release, ci_stable_release, release_track_dev, release_track_beta, release_track_stable [INFERRED]
## Communities (88 total, 24 thin omitted)
### Community 0 - "lua/mod.rs"
Cohesion: 0.06
Nodes (86): now_unix_ms(), ModulePermission, Option, String, WidgetSpec, RuntimeState, bluetooth_connect(), bluetooth_disconnect() (+78 more)
### Community 1 - "RawEvent"
Cohesion: 0.05
Nodes (67): adapter_source_is_hashable_and_eq(), AdapterSource, bread_event_new_accepts_owned_and_borrowed_names(), bread_event_new_assigns_unique_id_and_no_cause(), bread_event_new_sets_current_timestamp(), bread_event_with_timestamp_preserves_timestamp_and_assigns_id(), BreadEvent, DaemonSection (+59 more)
### Community 2 - "config.rs"
Cohesion: 0.07
Nodes (50): AdaptersConfig, AdapterToggle, compat_section_defaults_legacy_hyprland_names_to_true(), CompatConfig, Config, config_path(), config_path_falls_back_to_home_when_no_xdg(), config_path_respects_xdg_config_home() (+42 more)
### Community 3 - "Server"
Cohesion: 0.05
Nodes (41): A, command_target(), event_domain(), is_known_app(), is_reserved_domain(), Option, validate_app_namespace(), validate_command_event() (+33 more)
### Community 4 - "widget.rs"
Cohesion: 0.07
Nodes (32): accepts_node_count_at_max(), accepts_tree_at_max_depth(), Align, Background, box_of(), default_orientation(), FontWeight, is_valid_class() (+24 more)
### Community 5 - "Bread Daemon (breadd)"
Cohesion: 0.06
Nodes (39): Bluetooth Adapter, Hyprland Adapter, Network Adapter, Power Adapter, udev Adapter, bread.on(pattern, fn), bread.spawn(fn), bread.wait(pattern, opts) (+31 more)
### Community 6 - "filesystem.rs"
Cohesion: 0.12
Nodes (28): classify(), classify_build_artifact_created_in_target(), classify_debounces_rapid_repeat_events_for_same_path(), classify_file_changed_for_ordinary_source_file(), classify_silent_for_modify_in_target_not_create(), classify_silent_under_git(), classify_silent_under_node_modules(), detect_markers() (+20 more)
### Community 7 - "git.rs"
Cohesion: 0.12
Nodes (22): check_ahead_behind(), check_dirty(), discover_repos(), expand_roots(), expand_roots_globs_single_trailing_star(), expand_roots_skips_unreadable_glob_parent_without_panicking(), expand_roots_uses_literal_path_without_trailing_star(), GitAdapter (+14 more)
### Community 8 - "Result"
Cohesion: 0.15
Nodes (50): daemon_survives_repeated_reloads_and_pipeline_resumes(), emit_with_app_source_allows_command_to_another_app(), emit_with_app_source_rejects_wrong_namespace(), emit_with_app_source_still_rejects_foreign_app_namespace(), emit_with_internal_source_is_rejected(), emit_with_known_app_source_routes_through_normalizer(), emit_with_unregistered_app_source_is_rejected(), emit_without_event_errors() (+42 more)
### Community 9 - "state_engine.rs"
Cohesion: 0.12
Nodes (19): apply_device_change(), apply_event_to_state(), device_connect_adds_device_with_all_fields(), device_connect_is_idempotent_for_same_id(), device_disconnect_of_unknown_id_is_noop(), device_disconnect_removes_matching_id(), ev(), monitor_connect_adds_new_monitor() (+11 more)
### Community 10 - "modules_mgmt.rs"
Cohesion: 0.11
Nodes (36): audit_detects_fs_read_and_widget_from_cpu_temp_widget_style_module(), audit_extracts_exec_bin_hint_and_ignores_baseline_calls(), audit_module(), audit_scans_required_sibling_files_in_module_directory(), classify_call_site(), collect_lua_files(), copy_dir(), extract_first_string_arg() (+28 more)
### Community 11 - "systemd.rs"
Cohesion: 0.12
Nodes (17): active_state_to_kind(), failure_result(), get_unit_path(), handle_message(), is_failed_transition(), query_active_state(), Connection, HashMap (+9 more)
### Community 12 - "hooks_git.rs"
Cohesion: 0.15
Nodes (24): all_hook_scripts_exit_0_unconditionally(), all_hook_scripts_start_with_shebang_and_marker(), branch_changed_emit_line(), commit_created_emit_line(), emit_line_for(), git_dir(), hook_script(), hook_script_rejects_unknown_name() (+16 more)
### Community 13 - "bread-cli/src/main.rs"
Cohesion: 0.19
Nodes (29): CausalityTracker, Cli, Commands, config_directory(), daemon_socket_path(), format_timestamp(), handle_modules_cmd(), HooksCommand (+21 more)
### Community 14 - "types.rs"
Cohesion: 0.17
Nodes (21): Device, DeviceRule, DeviceTopology, InterfaceState, MatchCondition, ModuleStatus, Monitor, NetworkState (+13 more)
### Community 15 - "podman.rs"
Cohesion: 0.15
Nodes (17): container_event(), ignores_remove_event(), ignores_stop_event_to_avoid_double_emit_with_died(), ignores_unknown_action(), map_podman_event(), maps_died_event(), maps_health_status_event(), maps_start_event() (+9 more)
### Community 16 - "StateHandle"
Cohesion: 0.14
Nodes (10): condition_matches(), resolve_device(), Option, Result, String, Value, Vec, StateHandle (+2 more)
### Community 17 - "run_udev_monitor"
Cohesion: 0.35
Nodes (9): enumerate_with_udev(), Result, Self, Sender, String, Vec, run_udev_monitor(), ScannedDevice (+1 more)
### Community 18 - "hooks_shell.rs"
Cohesion: 0.18
Nodes (11): hook_scripts_background_every_emit_call(), hooks_dir(), install_shell(), join_line_continuations(), Option, PathBuf, Result, String (+3 more)
### Community 19 - "bluetooth.rs"
Cohesion: 0.15
Nodes (10): address_from_path(), BluetoothAdapter, parse_bluetooth_message(), Message, Option, Result, Self, Sender (+2 more)
### Community 20 - "SubscriptionId"
Cohesion: 0.29
Nodes (13): HashMap, String, Vec, Subscription, SubscriptionId, SubscriptionTable, table_add_assigns_provided_id_and_finds_match(), table_clear_removes_all() (+5 more)
### Community 22 - "run_state_engine"
Cohesion: 0.27
Nodes (12): dispatch_event(), handle_command(), Arc, AtomicU64, Receiver, RwLock, Self, Sender (+4 more)
### Community 23 - "RtnetlinkAdapter"
Cohesion: 0.22
Nodes (7): ip_from_bytes(), Option, Result, Self, Sender, String, RtnetlinkAdapter
### Community 24 - "network.rs"
Cohesion: 0.27
Nodes (9): has_default_route(), network_raw_event(), NetworkAdapter, NetworkSnapshot, read_network_state(), BTreeMap, Result, Sender (+1 more)
### Community 25 - "power.rs"
Cohesion: 0.26
Nodes (8): power_raw_event(), PowerAdapter, PowerSnapshot, read_power_state(), Option, Result, Self, Sender
### Community 26 - "hyprland.rs"
Cohesion: 0.29
Nodes (7): hyprland_event_socket(), HyprlandAdapter, parse_hyprland_line(), PathBuf, Result, Sender, String
### Community 27 - "Adapter"
Cohesion: 0.23
Nodes (8): Adapter, parse_upower_message(), Message, Result, Self, Sender, UPowerAdapter, Send
### Community 28 - "dl.breadway.dev Distribution"
Cohesion: 0.25
Nodes (8): main Branch, dev-release.yml Workflow, rc-release.yml Workflow, release.yml Workflow, dl.breadway.dev Distribution, beta Release Track, dev Release Track, stable Release Track
### Community 29 - "git-branch-widget.lua"
Cohesion: 0.62
Nodes (6): focused_tab_cwd(), git_info(), M.on_load(), shell_quote(), update(), widget_root()
### Community 30 - "TestHarness"
Cohesion: 0.16
Nodes (16): main(), parse_args(), Option, String, killing_a_module_host_child_does_not_take_down_breadd_or_other_modules(), os_execute_and_io_open_are_denied_at_the_kernel_level_outside_granted_scope(), Child, Drop (+8 more)
### Community 31 - "Sync"
Cohesion: 0.50
Nodes (3): main(), Result, Sync
### Community 32 - "active-window-widget.lua"
Cohesion: 0.83
Nodes (3): label_for(), M.on_load(), widget_root()
### Community 33 - "cpu-temp-widget.lua"
Cohesion: 0.83
Nodes (3): M.on_load(), read_temp_c(), widget_root()
### Community 34 - "focus-mode-widget.lua"
Cohesion: 1.00
Nodes (3): is_focused(), M.on_load(), widget_root()
### Community 35 - "workflow-status-widget.lua"
Cohesion: 0.83
Nodes (3): M.on_load(), most_relevant(), widget_update()
### Community 36 - "bread.widget API"
Cohesion: 0.67
Nodes (3): bread.widget API, CPU Temperature Widget Example, Live Widget Update Pattern
### Community 64 - "ModuleHostLua"
Cohesion: 0.10
Nodes (38): call(), HostMessage, IoCommand, RpcResponse, Duration, Option, PathBuf, Receiver (+30 more)
### Community 65 - "ModuleHostRegistry"
Cohesion: 0.07
Nodes (43): bin_allowed(), path_allowed(), HashMap, Option, OwnedWriteHalf, Result, Sender, String (+35 more)
### Community 66 - "xtask/src/main.rs"
Cohesion: 0.11
Nodes (36): BTreeSet, ExitCode, check(), CheckReport, clean_state_passes(), extract_cli_commands(), extract_enum_variants(), extract_ipc_methods() (+28 more)
### Community 67 - "Bread"
Cohesion: 0.06
Nodes (28): Deprecations, Hyprland legacy flat event names (since v1.5), Bread Examples, Example 1: Porting keyboard_and_display_watcher.sh (system script), Example 2: Porting autostart.lua, Example 3: Porting display/monitors.lua, Example 4: Multi-step automation workflows, Example 5: A live widget in breadbar (+20 more)
### Community 68 - "rules.rs"
Cohesion: 0.14
Nodes (27): empty_file_loads_with_no_rules(), empty_on_is_treated_as_missing(), invalid_toml_is_fatal(), load_rules(), missing_on_is_reported_with_index_and_no_on(), multiple_action_keys_is_reported(), one_bad_rule_does_not_block_other_valid_rules(), ParsedRule (+19 more)
### Community 69 - "Normalized events"
Cohesion: 0.12
Nodes (16): Bluetooth (BlueZ), Compatibility: `[compat]` config, Devices (udev / Bluetooth), Filesystem / project detection, Git (hooks + dirty-state poller), Hyprland, Network, Normalized events (+8 more)
### Community 70 - "Bread Documentation"
Cohesion: 0.14
Nodes (14): API Stability & Versioning, Bread Documentation, Contents, Debugging tips, Dictionary: Event reference, Dictionary: IPC protocol, Dictionary: Runtime state schema, Integrating a bread\* app (+6 more)
### Community 71 - "udev.rs"
Cohesion: 0.32
Nodes (10): build_device_event(), build_event(), enumerate_payload_includes_classification_fields(), prop_bool(), prop_str(), Option, Value, udev_event_payload() (+2 more)
### Community 72 - "Dictionary: Lua API"
Cohesion: 0.17
Nodes (12): `bread.debounce(delay_ms, fn) -> wrapped_fn`, `bread.log(msg)` / `bread.warn(msg)` / `bread.error(msg)`, `bread.notify(message, opts)`, `bread.profile.activate(name)`, Dictionary: Lua API, Hyprland, Module declaration, Module lifecycle hooks (+4 more)
### Community 73 - "Out-of-process module sandboxing *(Since: v1.6)*"
Cohesion: 0.20
Nodes (10): Architecture, Crash isolation, New IPC methods, Out-of-process module sandboxing *(Since: v1.6)*, RPC bridge coverage, The gap this closes, The Landlock sandbox, The token/identity handshake (+2 more)
### Community 74 - "Dictionary: Built-in modules"
Cohesion: 0.20
Nodes (10): `bread.binds`, `bread.devices`, `bread.monitors`, `bread.rules` *(Since: v1.5)*, `bread.workspaces`, Device rule options, Dictionary: Built-in modules, Example: Dock-specific setup (+2 more)
### Community 75 - "Events"
Cohesion: 0.20
Nodes (10): `bread.emit(event, data)`, `bread.filter(pattern, fn, opts) -> id`, `bread.off(id)`, `bread.on(pattern, fn) -> id`, `bread.once(pattern, fn) -> id`, `bread.spawn(fn)`, `bread.wait_all(patterns, opts) -> table` *(Since: v1.2)*, `bread.wait_any(patterns, opts) -> event | nil` *(Since: v1.2)* (+2 more)
### Community 76 - "Machine and filesystem"
Cohesion: 0.20
Nodes (10): `bread.fs.exists(path) -> bool`, `bread.fs.expand(path) -> string`, `bread.fs.read(path) -> string | nil`, `bread.fs.readlink(path) -> string | nil`, `bread.fs.write(path, content)`, `bread.json.decode(str) -> table | nil`, `bread.machine.has_tag(tag) -> bool`, `bread.machine.name() -> string` (+2 more)
### Community 77 - "Contributing"
Cohesion: 0.22
Nodes (8): Branches, CI, Contributing, Keeping the API docs honest, Local development, Questions, The release cycle, Tracks, from a user's perspective
### Community 78 - "Bluetooth"
Cohesion: 0.22
Nodes (9): Bluetooth, `bread.bluetooth.connect(address)`, `bread.bluetooth.devices() -> table | nil`, `bread.bluetooth.disconnect(address)`, `bread.bluetooth.power(enabled)`, `bread.bluetooth.powered() -> bool | nil`, `bread.bluetooth.scan(enabled)`, Example: auto-connect headphones on AC power (+1 more)
### Community 79 - "Widgets *(Since: v1.3)*"
Cohesion: 0.22
Nodes (9): `bread.widget.list() -> table`, `bread.widget.register(spec) -> ok, err`, `bread.widget.remove(id) -> bool`, `bread.widget.update(id, patch) -> ok, err`, Click events, Node types, `style` *(Since: v1.4)*, Style vs. `class` (+1 more)
### Community 80 - "Getting started"
Cohesion: 0.33
Nodes (6): 1) Create a minimal config, 2) The fast path: `rules.toml` *(Since: v1.5)*, 3) Minimal `init.lua`, 4) Start the daemon, 5) Check that it's running, Getting started
### Community 81 - "Capability-scoped modules *(Since: v1.5)*"
Cohesion: 0.33
Nodes (6): Baseline (always available, no manifest entry needed), `bread modules audit <name>`, Capability-scoped modules *(Since: v1.5)*, Gated — requires a matching `[[permissions]]` entry, `path`/`bin` enforcement depends on where the module runs, `require("bread.devices")` still works from a scoped module
### Community 82 - "Workflows *(Since: v1.2)*"
Cohesion: 0.33
Nodes (6): `bread.workflow.define(name, fn)`, `bread.workflow.list() -> table`, `bread.workflow.start(name, opts)`, `bread.workflow.status(name) -> table | nil`, `bread.workflow.step(label)`, Workflows *(Since: v1.2)*
### Community 83 - "Timers"
Cohesion: 0.50
Nodes (4): `bread.after(delay_ms, fn) -> id`, `bread.cancel(id)`, `bread.every(interval_ms, fn) -> id`, Timers
### Community 84 - "State"
Cohesion: 0.50
Nodes (4): `bread.state.get(path)`, `bread.state.watch(path, fn) -> id`, State, Typed shorthands
### Community 85 - "init.lua"
Cohesion: 0.83
Nodes (3): M.on_load(), read_temp_c(), widget_root()
### Community 86 - "Execution"
Cohesion: 0.67
Nodes (3): `bread.exec_capture(cmd, opts) -> ok, stdout`, `bread.exec(cmd)`, Execution
## Knowledge Gaps
- **179 isolated node(s):** `install.sh script`, `Branches`, `The release cycle`, `Tracks, from a user's perspective`, `Keeping the API docs honest` (+174 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **24 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `RawEvent` connect `RawEvent` to `Server`, `filesystem.rs`, `git.rs`, `udev.rs`, `systemd.rs`, `podman.rs`, `run_udev_monitor`, `bluetooth.rs`, `RtnetlinkAdapter`, `network.rs`, `power.rs`, `hyprland.rs`, `Adapter`?**
_High betweenness centrality (0.094) - this node is a cross-community bridge._
- **Why does `Adapter` connect `Adapter` to `Server`, `filesystem.rs`, `git.rs`, `udev.rs`, `systemd.rs`, `podman.rs`, `run_udev_monitor`, `bluetooth.rs`, `RtnetlinkAdapter`, `network.rs`, `power.rs`, `hyprland.rs`, `Sync`?**
_High betweenness centrality (0.077) - this node is a cross-community bridge._
- **Why does `BreadEvent` connect `RawEvent` to `ModuleHostLua`, `lua/mod.rs`, `ModuleHostRegistry`, `Server`, `state_engine.rs`, `run_state_engine`?**
_High betweenness centrality (0.063) - this node is a cross-community bridge._
- **What connects `install.sh script`, `Branches`, `The release cycle` to the rest of the system?**
_179 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `lua/mod.rs` be split into smaller, more focused modules?**
_Cohesion score 0.056 - nodes in this community are weakly interconnected._
- **Should `RawEvent` be split into smaller, more focused modules?**
_Cohesion score 0.051842708517016396 - nodes in this community are weakly interconnected._
- **Should `config.rs` be split into smaller, more focused modules?**
_Cohesion score 0.07191780821917808 - nodes in this community are weakly interconnected._

View file

@ -1,18 +0,0 @@
{
"runs": [
{
"date": "2026-08-04T09:09:09.071813+00:00",
"input_tokens": 0,
"output_tokens": 0,
"files": 55
},
{
"date": "2026-08-04T09:28:29.057021+00:00",
"input_tokens": 72759,
"output_tokens": 0,
"files": 55
}
],
"total_input_tokens": 72759,
"total_output_tokens": 0
}

File diff suppressed because it is too large Load diff

View file

@ -1,327 +0,0 @@
{
"bread-cli/src/hooks_git.rs": {
"mtime": 1786800467.7200727,
"ast_hash": "7a29b5d5d90170f0aef9cececc7d8656",
"semantic_hash": "7a29b5d5d90170f0aef9cececc7d8656"
},
"bread-cli/src/hooks_shell.rs": {
"mtime": 1786800467.7201686,
"ast_hash": "c5d5b8922fcff79954ddb00496721603",
"semantic_hash": "c5d5b8922fcff79954ddb00496721603"
},
"bread-cli/src/lib.rs": {
"mtime": 1786800467.7201686,
"ast_hash": "d1bf6e1c239498521c42418f672bc400",
"semantic_hash": "d1bf6e1c239498521c42418f672bc400"
},
"bread-cli/src/main.rs": {
"mtime": 1786800467.7201686,
"ast_hash": "e1fbe63ea3b10e0887072ab7133d0508",
"semantic_hash": ""
},
"bread-cli/src/modules_mgmt.rs": {
"mtime": 1786801244.1263742,
"ast_hash": "1cb6f3e4fa26108f81215ab57bb02b91",
"semantic_hash": ""
},
"bread-cli/tests/modules.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "84fca8f87dc915b1cb523f8199e78521",
"semantic_hash": "84fca8f87dc915b1cb523f8199e78521"
},
"bread-emit/src/main.rs": {
"mtime": 1786800467.7206197,
"ast_hash": "350cefbf697cfd93f7df7b7bcc45a101",
"semantic_hash": "350cefbf697cfd93f7df7b7bcc45a101"
},
"bread-shared/src/apps.rs": {
"mtime": 1786801218.1467261,
"ast_hash": "e8419571e7a322019d71c89559fb02fb",
"semantic_hash": ""
},
"bread-shared/src/glob.rs": {
"mtime": 1786800467.7210522,
"ast_hash": "0594a313cb1909d1cca5fd5b8f241b68",
"semantic_hash": "0594a313cb1909d1cca5fd5b8f241b68"
},
"bread-shared/src/lib.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "236b73bad29f1544a28fbf3ee1ccc795",
"semantic_hash": ""
},
"bread-shared/src/widget.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "33272eb38c7fffbfa180cef7c5a286fc",
"semantic_hash": "33272eb38c7fffbfa180cef7c5a286fc"
},
"breadd/src/adapters/bluetooth.rs": {
"mtime": 1786800467.7214499,
"ast_hash": "4d1df67347d5b7c9cdbcb12921a6ae28",
"semantic_hash": "4d1df67347d5b7c9cdbcb12921a6ae28"
},
"breadd/src/adapters/filesystem.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "8ec9dcd8de13f30922cfbe9b4a3fff56",
"semantic_hash": "8ec9dcd8de13f30922cfbe9b4a3fff56"
},
"breadd/src/adapters/git.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "6e6ff6ca318e28cd94690c30d41b520b",
"semantic_hash": "6e6ff6ca318e28cd94690c30d41b520b"
},
"breadd/src/adapters/hyprland.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "71537dbd86b413d94476f8022054f1f4",
"semantic_hash": "71537dbd86b413d94476f8022054f1f4"
},
"breadd/src/adapters/mod.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "63885887c2fc3ea2314d7fe095e5df61",
"semantic_hash": "63885887c2fc3ea2314d7fe095e5df61"
},
"breadd/src/adapters/network.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "8706dc546b7e08d5aa084ca58c48559c",
"semantic_hash": "8706dc546b7e08d5aa084ca58c48559c"
},
"breadd/src/adapters/network_rtnetlink.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "0c9d0bb2693bc46b11807f6b8ebb7c4c",
"semantic_hash": "0c9d0bb2693bc46b11807f6b8ebb7c4c"
},
"breadd/src/adapters/podman.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "97c08551c0fe53b0a5888d98730d35db",
"semantic_hash": "97c08551c0fe53b0a5888d98730d35db"
},
"breadd/src/adapters/power.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "987d202ec0d30ec26b1d747a6e320ed7",
"semantic_hash": "987d202ec0d30ec26b1d747a6e320ed7"
},
"breadd/src/adapters/power_upower.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "fc0a36ca76d340c8be77644f63e462bf",
"semantic_hash": "fc0a36ca76d340c8be77644f63e462bf"
},
"breadd/src/adapters/systemd.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "b3884dbecd39acc38abdf67589a8b954",
"semantic_hash": "b3884dbecd39acc38abdf67589a8b954"
},
"breadd/src/adapters/udev.rs": {
"mtime": 1786800978.7399695,
"ast_hash": "568993c8c2dd218879eec57aee50a6b9",
"semantic_hash": ""
},
"breadd/src/core/config.rs": {
"mtime": 1786800467.7215972,
"ast_hash": "7f479bfb51647e6131c14f0c2f153d95",
"semantic_hash": ""
},
"breadd/src/core/mod.rs": {
"mtime": 1786800467.7223108,
"ast_hash": "e38bfdb894eabd208941bd3ffb1ef464",
"semantic_hash": ""
},
"breadd/src/core/normalizer.rs": {
"mtime": 1786801218.2233918,
"ast_hash": "1596f60df5049ff8715f66f64bee9f8a",
"semantic_hash": ""
},
"breadd/src/core/state_engine.rs": {
"mtime": 1786800467.7223108,
"ast_hash": "84a35ee78f3881261a3ce378ea3c03a0",
"semantic_hash": ""
},
"breadd/src/core/subscriptions.rs": {
"mtime": 1786800467.7223108,
"ast_hash": "8735d4717b74523c787dab9ea2b0bbd8",
"semantic_hash": "8735d4717b74523c787dab9ea2b0bbd8"
},
"breadd/src/core/supervisor.rs": {
"mtime": 1786800467.7223108,
"ast_hash": "403f7ba1807c71f9b89d81bfeff2681a",
"semantic_hash": "403f7ba1807c71f9b89d81bfeff2681a"
},
"breadd/src/core/types.rs": {
"mtime": 1786800467.7223108,
"ast_hash": "8bd070d4c09725d8717749a79aa42a92",
"semantic_hash": ""
},
"breadd/src/ipc/mod.rs": {
"mtime": 1786800912.9916558,
"ast_hash": "54af229257e555313f7d4b59d57ff8d3",
"semantic_hash": ""
},
"breadd/src/lua/mod.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "ed13e2495399d532625653a22a4fee14",
"semantic_hash": ""
},
"breadd/src/main.rs": {
"mtime": 1786800467.722855,
"ast_hash": "4b31888cc9e76210df0c7cff3a8b1e42",
"semantic_hash": ""
},
"breadd/tests/ipc_integration.rs": {
"mtime": 1786801218.36339,
"ast_hash": "2b5bade2cc2e189a3af9c692232be258",
"semantic_hash": ""
},
"examples/modules/active-window-widget.lua": {
"mtime": 1786800467.7243168,
"ast_hash": "a5f6fb2c54775a49ddaf29674f86571d",
"semantic_hash": "a5f6fb2c54775a49ddaf29674f86571d"
},
"examples/modules/bluetooth-toggle-widget.lua": {
"mtime": 1786800467.7243168,
"ast_hash": "c8529dc45862adf4f63db48674a28277",
"semantic_hash": "c8529dc45862adf4f63db48674a28277"
},
"examples/modules/dock-monitors.lua": {
"mtime": 1786800467.724451,
"ast_hash": "4abe51f19614d2bf117ac35e95324801",
"semantic_hash": "4abe51f19614d2bf117ac35e95324801"
},
"examples/modules/dock-workflow.lua": {
"mtime": 1786800467.724451,
"ast_hash": "84cf81cfb7780e89a46e96a6ccfbad15",
"semantic_hash": ""
},
"examples/modules/focus-mode-widget.lua": {
"mtime": 1786800467.724451,
"ast_hash": "f62c366c2c85cfbe59eef663fc607230",
"semantic_hash": "f62c366c2c85cfbe59eef663fc607230"
},
"examples/modules/git-branch-widget.lua": {
"mtime": 1786800467.724451,
"ast_hash": "ebeeaebab5b5b29619f3db65c1df57d8",
"semantic_hash": ""
},
"examples/modules/low-battery-warning.lua": {
"mtime": 1786800467.724451,
"ast_hash": "e0ba79860562fc36fa8cb183bc576fb7",
"semantic_hash": "e0ba79860562fc36fa8cb183bc576fb7"
},
"examples/modules/pause-media-on-headphone-unplug.lua": {
"mtime": 1786800467.724451,
"ast_hash": "6c4cf82b6963eb93df224bed60d06c2d",
"semantic_hash": "6c4cf82b6963eb93df224bed60d06c2d"
},
"examples/modules/workflow-status-widget.lua": {
"mtime": 1786800467.724451,
"ast_hash": "9f913a66a0f8654dadc1c5d6c2be0938",
"semantic_hash": "9f913a66a0f8654dadc1c5d6c2be0938"
},
"scripts/install.sh": {
"mtime": 1786800994.0764284,
"ast_hash": "35d1a7f63824e9176bd105ab3d699576",
"semantic_hash": ""
},
"CONTRIBUTING.md": {
"mtime": 1786800467.716893,
"ast_hash": "6d056fcf0dae29949d19c41b41792879",
"semantic_hash": ""
},
"Documentation.md": {
"mtime": 1786801027.2816515,
"ast_hash": "7b8471e7ae45aed70682bb858826860c",
"semantic_hash": ""
},
"Examples.md": {
"mtime": 1786800467.716893,
"ast_hash": "6bcd7a14be53a9f67ef6912b160da8bc",
"semantic_hash": "6bcd7a14be53a9f67ef6912b160da8bc"
},
"README.md": {
"mtime": 1786800994.0964282,
"ast_hash": "97ae9f7efb9f2cdceb615a47cf3dfa1a",
"semantic_hash": ""
},
"bread-module-host/src/io.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "dbe04e05d1cfb02770db8d7c0bcc0b68",
"semantic_hash": ""
},
"bread-module-host/src/lua_env.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "97d009846e18d3c809663b1185c98bbb",
"semantic_hash": ""
},
"bread-module-host/src/main.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "9a93253ae44c764b8273df40999ddede",
"semantic_hash": ""
},
"bread-shared/src/module_host_ipc.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "fcdb4c03f242a5ea51fd3a096b711363",
"semantic_hash": ""
},
"bread-shared/src/permissions.rs": {
"mtime": 1786800467.7210522,
"ast_hash": "800445ef9c90bdbf8f3ac9d4f1afd98e",
"semantic_hash": ""
},
"breadd/src/core/rules.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "bd430cf213f400b8d4e96ccc35a76ff1",
"semantic_hash": ""
},
"breadd/src/ipc/module_host_bridge.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "caa9cf82695e48c88758fcfa835e1871",
"semantic_hash": ""
},
"breadd/src/module_host.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "228826ff3c8941ea5f9bb28b66d637a9",
"semantic_hash": ""
},
"breadd/tests/module_host_sandbox.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "7e637b3a950c510215a6f6a7b88dd404",
"semantic_hash": ""
},
"examples/modules/cpu-temp-widget/init.lua": {
"mtime": 1786800467.724451,
"ast_hash": "5ac893dd2f6af6d8b22dd290e3021f77",
"semantic_hash": ""
},
"xtask/src/main.rs": {
"mtime": 1786801244.1297076,
"ast_hash": "e914dd77ac3241df09e3a681228961db",
"semantic_hash": ""
},
".forgejo/workflows/dev-release.yml": {
"mtime": 1786800467.7161875,
"ast_hash": "707129aa2fe79b4539d108c03e6d7abf",
"semantic_hash": ""
},
".forgejo/workflows/rc-release.yml": {
"mtime": 1786800467.7167187,
"ast_hash": "3007d2c43d785cd72f0548096626a21d",
"semantic_hash": ""
},
".forgejo/workflows/release.yml": {
"mtime": 1786800467.7167187,
"ast_hash": "3578aa39e7b2466822c8d85247489d36",
"semantic_hash": ""
},
"DEPRECATIONS.md": {
"mtime": 1786800467.716893,
"ast_hash": "1685b60fad2ed4184e4119c30866ec12",
"semantic_hash": ""
},
"examples/modules/README.md": {
"mtime": 1786800467.7242541,
"ast_hash": "dfa8a72f41c08bd5b8036b684092a93b",
"semantic_hash": ""
},
"packaging/README.md": {
"mtime": 1786800994.0430956,
"ast_hash": "caa295eed64de126874a2e48b8aed8a2",
"semantic_hash": ""
}
}

View file

@ -1,21 +1,21 @@
# Graph Report - bread (2026-08-16) # Graph Report - bread (2026-08-15)
## Corpus Check ## Corpus Check
- 66 files · ~90,429 words - 62 files · ~88,250 words
- Verdict: corpus is large enough that graph structure adds value. - Verdict: corpus is large enough that graph structure adds value.
## Summary ## Summary
- 1486 nodes · 3384 edges · 93 communities (68 shown, 25 thin omitted) - 1467 nodes · 3347 edges · 88 communities (64 shown, 24 thin omitted)
- Extraction: 99% EXTRACTED · 1% INFERRED · 0% AMBIGUOUS · INFERRED: 47 edges (avg confidence: 0.78) - Extraction: 99% EXTRACTED · 1% INFERRED · 0% AMBIGUOUS · INFERRED: 48 edges (avg confidence: 0.78)
- Token cost: 0 input · 0 output - Token cost: 0 input · 0 output
## Graph Freshness ## Graph Freshness
- Built from commit: `cdd5de8f` - Built from commit: `a6973360`
- Run `git rev-parse HEAD` and compare to check if the graph is stale. - Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost). - Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation) ## Community Hubs (Navigation)
- LuaEngine - lua/mod.rs
- RawEvent - RawEvent
- config.rs - config.rs
- Server - Server
@ -31,24 +31,24 @@
- bread-cli/src/main.rs - bread-cli/src/main.rs
- types.rs - types.rs
- podman.rs - podman.rs
- Value - StateHandle
- Result - run_udev_monitor
- hooks_shell.rs - hooks_shell.rs
- bluetooth.rs - bluetooth.rs
- SubscriptionId - SubscriptionId
- glob.rs - glob.rs
- StateHandle - run_state_engine
- Adapter - RtnetlinkAdapter
- network.rs - network.rs
- power.rs - power.rs
- hyprland.rs - hyprland.rs
- parse_upower_message - Adapter
- main Branch - dl.breadway.dev Distribution
- git-branch-widget.lua - git-branch-widget.lua
- TestHarness - TestHarness
- Sync - Sync
- active-window-widget.lua - active-window-widget.lua
- .new - cpu-temp-widget.lua
- focus-mode-widget.lua - focus-mode-widget.lua
- workflow-status-widget.lua - workflow-status-widget.lua
- bread.widget API - bread.widget API
@ -70,10 +70,10 @@
- bread.system.startup - bread.system.startup
- Monitors Configuration Example - Monitors Configuration Example
- Binds Module (Built-in) - Binds Module (Built-in)
- lua/mod.rs - Bakery Package Manager
- external-monitors.lua - Phase 3: GUI Control Center
- AGENTS.md — Repo hygiene - Phase 5: Cross-Device Mesh
- CLAUDE.md — Repo hygiene - Dev Version Computation
- ModuleHostLua - ModuleHostLua
- ModuleHostRegistry - ModuleHostRegistry
- xtask/src/main.rs - xtask/src/main.rs
@ -98,20 +98,15 @@
- init.lua - init.lua
- Execution - Execution
- packaging/README.md - packaging/README.md
- PathBuf
- build.sh
- beta Release Track
- dev Release Track
- stable Release Track
## God Nodes (most connected - your core abstractions) ## God Nodes (most connected - your core abstractions)
1. `LuaEngine` - 59 edges 1. `LuaEngine` - 59 edges
2. `RawEvent` - 50 edges 2. `RawEvent` - 49 edges
3. `BreadEvent` - 38 edges 3. `BreadEvent` - 38 edges
4. `raw()` - 37 edges 4. `raw()` - 37 edges
5. `RuntimeState` - 36 edges 5. `RuntimeState` - 34 edges
6. `StateHandle` - 28 edges 6. `StateHandle` - 28 edges
7. `now_unix_ms()` - 25 edges 7. `now_unix_ms()` - 26 edges
8. `Adapter` - 25 edges 8. `Adapter` - 25 edges
9. `SubscriptionId` - 25 edges 9. `SubscriptionId` - 25 edges
10. `ModuleHostLua` - 24 edges 10. `ModuleHostLua` - 24 edges
@ -123,29 +118,31 @@
breadd/src/adapters/bluetooth.rs → bread-shared/src/lib.rs breadd/src/adapters/bluetooth.rs → bread-shared/src/lib.rs
- `classify()` --calls--> `now_unix_ms()` [INFERRED] - `classify()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/filesystem.rs → bread-shared/src/lib.rs breadd/src/adapters/filesystem.rs → bread-shared/src/lib.rs
- `emit_topology_snapshot()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/hyprland.rs → bread-shared/src/lib.rs
- `network_raw_event()` --calls--> `now_unix_ms()` [INFERRED] - `network_raw_event()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/network.rs → bread-shared/src/lib.rs breadd/src/adapters/network.rs → bread-shared/src/lib.rs
- `power_raw_event()` --calls--> `now_unix_ms()` [INFERRED]
breadd/src/adapters/power.rs → bread-shared/src/lib.rs
## Import Cycles ## Import Cycles
- 2-file cycle: `breadd/src/core/state_engine.rs -> breadd/src/lua/mod.rs -> breadd/src/core/state_engine.rs` - 2-file cycle: `breadd/src/core/state_engine.rs -> breadd/src/lua/mod.rs -> breadd/src/core/state_engine.rs`
- 2-file cycle: `bread-shared/src/lib.rs -> bread-shared/src/module_host_ipc.rs -> bread-shared/src/lib.rs` - 2-file cycle: `bread-shared/src/lib.rs -> bread-shared/src/module_host_ipc.rs -> bread-shared/src/lib.rs`
## Hyperedges (group relationships) ## Hyperedges (group relationships)
- **** — ci_dev_release, workflow_version_compute, release_track_dev, distribution_dl_breadway_dev, package_bakery [INFERRED]
- **** — adapter_udev, adapter_hyprland, adapter_power, sys_bread_daemon, api_bread_on, sys_lua_runtime [INFERRED] - **** — adapter_udev, adapter_hyprland, adapter_power, sys_bread_daemon, api_bread_on, sys_lua_runtime [INFERRED]
- **** — config_init_lua, config_modules_dir, pattern_module_skeleton, api_bread_on, sys_lua_runtime [INFERRED] - **** — config_init_lua, config_modules_dir, pattern_module_skeleton, api_bread_on, sys_lua_runtime [INFERRED]
- **** — api_bread_workflow, api_bread_spawn, api_bread_wait, example_dock_workflow, api_bread_notify [INFERRED] - **** — api_bread_workflow, api_bread_spawn, api_bread_wait, example_dock_workflow, api_bread_notify [INFERRED]
- **** — api_bread_widget, api_bread_every, example_widget_cpu_temp, api_bread_state_watch [INFERRED] - **** — api_bread_widget, api_bread_every, example_widget_cpu_temp, api_bread_state_watch [INFERRED]
- **** — branch_main, ci_dev_release, ci_rc_release, ci_stable_release, release_track_dev, release_track_beta, release_track_stable [INFERRED]
## Communities (93 total, 25 thin omitted) ## Communities (88 total, 24 thin omitted)
### Community 0 - "LuaEngine" ### Community 0 - "lua/mod.rs"
Cohesion: 0.11 Cohesion: 0.06
Nodes (19): ErrorEntry, HandlerEntry, HandlerKind, LuaEngine, LuaMessage, ModuleInfo, AtomicU64, HashMap (+11 more) Nodes (86): now_unix_ms(), ModulePermission, Option, String, WidgetSpec, RuntimeState, bluetooth_connect(), bluetooth_disconnect() (+78 more)
### Community 1 - "RawEvent" ### Community 1 - "RawEvent"
Cohesion: 0.06 Cohesion: 0.05
Nodes (67): adapter_source_is_hashable_and_eq(), AdapterSource, bread_event_new_accepts_owned_and_borrowed_names(), bread_event_new_assigns_unique_id_and_no_cause(), bread_event_new_sets_current_timestamp(), bread_event_with_timestamp_preserves_timestamp_and_assigns_id(), BreadEvent, DaemonSection (+59 more) Nodes (67): adapter_source_is_hashable_and_eq(), AdapterSource, bread_event_new_accepts_owned_and_borrowed_names(), bread_event_new_assigns_unique_id_and_no_cause(), bread_event_new_sets_current_timestamp(), bread_event_with_timestamp_preserves_timestamp_and_assigns_id(), BreadEvent, DaemonSection (+59 more)
### Community 2 - "config.rs" ### Community 2 - "config.rs"
@ -158,11 +155,11 @@ Nodes (41): A, command_target(), event_domain(), is_known_app(), is_reserved_dom
### Community 4 - "widget.rs" ### Community 4 - "widget.rs"
Cohesion: 0.07 Cohesion: 0.07
Nodes (33): accepts_node_count_at_max(), accepts_tree_at_max_depth(), Align, Background, box_of(), default_orientation(), FontWeight, is_valid_class() (+25 more) Nodes (32): accepts_node_count_at_max(), accepts_tree_at_max_depth(), Align, Background, box_of(), default_orientation(), FontWeight, is_valid_class() (+24 more)
### Community 5 - "Bread Daemon (breadd)" ### Community 5 - "Bread Daemon (breadd)"
Cohesion: 0.06 Cohesion: 0.06
Nodes (36): Bluetooth Adapter, Hyprland Adapter, Network Adapter, Power Adapter, udev Adapter, bread.on(pattern, fn), bread.spawn(fn), bread.wait(pattern, opts) (+28 more) Nodes (39): Bluetooth Adapter, Hyprland Adapter, Network Adapter, Power Adapter, udev Adapter, bread.on(pattern, fn), bread.spawn(fn), bread.wait(pattern, opts) (+31 more)
### Community 6 - "filesystem.rs" ### Community 6 - "filesystem.rs"
Cohesion: 0.12 Cohesion: 0.12
@ -178,7 +175,7 @@ Nodes (50): daemon_survives_repeated_reloads_and_pipeline_resumes(), emit_with_a
### Community 9 - "state_engine.rs" ### Community 9 - "state_engine.rs"
Cohesion: 0.12 Cohesion: 0.12
Nodes (20): apply_device_change(), apply_event_to_state(), device_connect_adds_device_with_all_fields(), device_connect_is_idempotent_for_same_id(), device_disconnect_of_unknown_id_is_noop(), device_disconnect_removes_matching_id(), ev(), hyprland_snapshot_replaces_topology() (+12 more) Nodes (19): apply_device_change(), apply_event_to_state(), device_connect_adds_device_with_all_fields(), device_connect_is_idempotent_for_same_id(), device_disconnect_of_unknown_id_is_noop(), device_disconnect_removes_matching_id(), ev(), monitor_connect_adds_new_monitor() (+11 more)
### Community 10 - "modules_mgmt.rs" ### Community 10 - "modules_mgmt.rs"
Cohesion: 0.11 Cohesion: 0.11
@ -197,20 +194,20 @@ Cohesion: 0.19
Nodes (29): CausalityTracker, Cli, Commands, config_directory(), daemon_socket_path(), format_timestamp(), handle_modules_cmd(), HooksCommand (+21 more) Nodes (29): CausalityTracker, Cli, Commands, config_directory(), daemon_socket_path(), format_timestamp(), handle_modules_cmd(), HooksCommand (+21 more)
### Community 14 - "types.rs" ### Community 14 - "types.rs"
Cohesion: 0.15 Cohesion: 0.17
Nodes (21): DeviceTopology, InterfaceState, MatchCondition, ModuleStatus, Monitor, NetworkState, PowerState, ProfileState (+13 more) Nodes (21): Device, DeviceRule, DeviceTopology, InterfaceState, MatchCondition, ModuleStatus, Monitor, NetworkState (+13 more)
### Community 15 - "podman.rs" ### Community 15 - "podman.rs"
Cohesion: 0.15 Cohesion: 0.15
Nodes (17): container_event(), ignores_remove_event(), ignores_stop_event_to_avoid_double_emit_with_died(), ignores_unknown_action(), map_podman_event(), maps_died_event(), maps_health_status_event(), maps_start_event() (+9 more) Nodes (17): container_event(), ignores_remove_event(), ignores_stop_event_to_avoid_double_emit_with_died(), ignores_unknown_action(), map_podman_event(), maps_died_event(), maps_health_status_event(), maps_start_event() (+9 more)
### Community 16 - "Value" ### Community 16 - "StateHandle"
Cohesion: 0.17 Cohesion: 0.14
Nodes (14): active_window_from_data(), apply_hyprland_snapshot(), condition_matches(), hyprland_state_key(), json_stringish(), resolve_device(), Option, Result (+6 more) Nodes (10): condition_matches(), resolve_device(), Option, Result, String, Value, Vec, StateHandle (+2 more)
### Community 17 - "Result" ### Community 17 - "run_udev_monitor"
Cohesion: 0.13 Cohesion: 0.35
Nodes (26): bluetooth_connect(), bluetooth_disconnect(), bluetooth_find_adapter(), bluetooth_get_powered(), bluetooth_query(), bluetooth_set_powered(), bluetooth_set_scanning(), bluetooth_spawn() (+18 more) Nodes (9): enumerate_with_udev(), Result, Self, Sender, String, Vec, run_udev_monitor(), ScannedDevice (+1 more)
### Community 18 - "hooks_shell.rs" ### Community 18 - "hooks_shell.rs"
Cohesion: 0.18 Cohesion: 0.18
@ -224,13 +221,13 @@ Nodes (10): address_from_path(), BluetoothAdapter, parse_bluetooth_message(), Me
Cohesion: 0.29 Cohesion: 0.29
Nodes (13): HashMap, String, Vec, Subscription, SubscriptionId, SubscriptionTable, table_add_assigns_provided_id_and_finds_match(), table_clear_removes_all() (+5 more) Nodes (13): HashMap, String, Vec, Subscription, SubscriptionId, SubscriptionTable, table_add_assigns_provided_id_and_finds_match(), table_clear_removes_all() (+5 more)
### Community 22 - "StateHandle" ### Community 22 - "run_state_engine"
Cohesion: 0.15 Cohesion: 0.27
Nodes (16): dispatch_event(), handle_command(), Arc, AtomicU64, HashMap, Receiver, RwLock, Self (+8 more) Nodes (12): dispatch_event(), handle_command(), Arc, AtomicU64, Receiver, RwLock, Self, Sender (+4 more)
### Community 23 - "Adapter" ### Community 23 - "RtnetlinkAdapter"
Cohesion: 0.19 Cohesion: 0.22
Nodes (9): Adapter, ip_from_bytes(), Option, Result, Self, Sender, String, RtnetlinkAdapter (+1 more) Nodes (7): ip_from_bytes(), Option, Result, Self, Sender, String, RtnetlinkAdapter
### Community 24 - "network.rs" ### Community 24 - "network.rs"
Cohesion: 0.27 Cohesion: 0.27
@ -242,11 +239,15 @@ Nodes (8): power_raw_event(), PowerAdapter, PowerSnapshot, read_power_state(), O
### Community 26 - "hyprland.rs" ### Community 26 - "hyprland.rs"
Cohesion: 0.29 Cohesion: 0.29
Nodes (11): emit_topology_snapshot(), hyprland_event_socket(), hyprland_request_json(), hyprland_request_socket(), HyprlandAdapter, parse_hyprland_line(), PathBuf, Result (+3 more) Nodes (7): hyprland_event_socket(), HyprlandAdapter, parse_hyprland_line(), PathBuf, Result, Sender, String
### Community 27 - "parse_upower_message" ### Community 27 - "Adapter"
Cohesion: 0.27 Cohesion: 0.23
Nodes (6): parse_upower_message(), Message, Result, Self, Sender, UPowerAdapter Nodes (8): Adapter, parse_upower_message(), Message, Result, Self, Sender, UPowerAdapter, Send
### Community 28 - "dl.breadway.dev Distribution"
Cohesion: 0.25
Nodes (8): main Branch, dev-release.yml Workflow, rc-release.yml Workflow, release.yml Workflow, dl.breadway.dev Distribution, beta Release Track, dev Release Track, stable Release Track
### Community 29 - "git-branch-widget.lua" ### Community 29 - "git-branch-widget.lua"
Cohesion: 0.62 Cohesion: 0.62
@ -264,9 +265,9 @@ Nodes (3): main(), Result, Sync
Cohesion: 0.83 Cohesion: 0.83
Nodes (3): label_for(), M.on_load(), widget_root() Nodes (3): label_for(), M.on_load(), widget_root()
### Community 33 - ".new" ### Community 33 - "cpu-temp-widget.lua"
Cohesion: 0.16 Cohesion: 0.83
Nodes (15): ModulePermission, Option, String, bluetooth_list_devices(), builtin_module_decls(), is_lib_path(), list_lua_files(), module_name_from_path() (+7 more) Nodes (3): M.on_load(), read_temp_c(), widget_root()
### Community 34 - "focus-mode-widget.lua" ### Community 34 - "focus-mode-widget.lua"
Cohesion: 1.00 Cohesion: 1.00
@ -280,24 +281,8 @@ Nodes (3): M.on_load(), most_relevant(), widget_update()
Cohesion: 0.67 Cohesion: 0.67
Nodes (3): bread.widget API, CPU Temperature Widget Example, Live Widget Update Pattern Nodes (3): bread.widget API, CPU Temperature Widget Example, Live Widget Update Pattern
### Community 60 - "lua/mod.rs"
Cohesion: 0.30
Nodes (20): now_unix_ms(), RuntimeState, module_store_get(), module_store_set(), Arc, JsonValue, RwLock, widget_list_json() (+12 more)
### Community 61 - "external-monitors.lua"
Cohesion: 0.29
Nodes (8): apply(), apply_monitor(), connected(), drm_first_mode(), drm_status(), is_internal(), list_connectors(), M.on_load()
### Community 62 - "AGENTS.md — Repo hygiene"
Cohesion: 0.33
Nodes (5): AGENTS.md — Repo hygiene, CI, Don't, Local architecture (still true), Remotes
### Community 63 - "CLAUDE.md — Repo hygiene"
Cohesion: 0.40
Nodes (4): CI, CLAUDE.md — Repo hygiene, Don't, Remotes
### Community 64 - "ModuleHostLua" ### Community 64 - "ModuleHostLua"
Cohesion: 0.09 Cohesion: 0.10
Nodes (38): call(), HostMessage, IoCommand, RpcResponse, Duration, Option, PathBuf, Receiver (+30 more) Nodes (38): call(), HostMessage, IoCommand, RpcResponse, Duration, Option, PathBuf, Receiver (+30 more)
### Community 65 - "ModuleHostRegistry" ### Community 65 - "ModuleHostRegistry"
@ -325,8 +310,8 @@ Cohesion: 0.14
Nodes (14): API Stability & Versioning, Bread Documentation, Contents, Debugging tips, Dictionary: Event reference, Dictionary: IPC protocol, Dictionary: Runtime state schema, Integrating a bread\* app (+6 more) Nodes (14): API Stability & Versioning, Bread Documentation, Contents, Debugging tips, Dictionary: Event reference, Dictionary: IPC protocol, Dictionary: Runtime state schema, Integrating a bread\* app (+6 more)
### Community 71 - "udev.rs" ### Community 71 - "udev.rs"
Cohesion: 0.18 Cohesion: 0.32
Nodes (18): build_device_event(), build_event(), enumerate_payload_includes_classification_fields(), prop_bool(), prop_str(), Option, Result, Self (+10 more) Nodes (10): build_device_event(), build_event(), enumerate_payload_includes_classification_fields(), prop_bool(), prop_str(), Option, Value, udev_event_payload() (+2 more)
### Community 72 - "Dictionary: Lua API" ### Community 72 - "Dictionary: Lua API"
Cohesion: 0.17 Cohesion: 0.17
@ -388,29 +373,25 @@ Nodes (3): M.on_load(), read_temp_c(), widget_root()
Cohesion: 0.67 Cohesion: 0.67
Nodes (3): `bread.exec_capture(cmd, opts) -> ok, stdout`, `bread.exec(cmd)`, Execution Nodes (3): `bread.exec_capture(cmd, opts) -> ok, stdout`, `bread.exec(cmd)`, Execution
### Community 88 - "PathBuf"
Cohesion: 1.00
Nodes (3): dirs_home(), lua_expand_path(), PathBuf
## Knowledge Gaps ## Knowledge Gaps
- **182 isolated node(s):** `build.sh script`, `install.sh script`, `Remotes`, `CI`, `Local architecture (still true)` (+177 more) - **179 isolated node(s):** `install.sh script`, `Branches`, `The release cycle`, `Tracks, from a user's perspective`, `Keeping the API docs honest` (+174 more)
These have ≤1 connection - possible missing edges or undocumented components. These have ≤1 connection - possible missing edges or undocumented components.
- **25 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes. - **24 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions ## Suggested Questions
_Questions this graph is uniquely positioned to answer:_ _Questions this graph is uniquely positioned to answer:_
- **Why does `Adapter` connect `Adapter` to `Server`, `filesystem.rs`, `git.rs`, `udev.rs`, `systemd.rs`, `podman.rs`, `bluetooth.rs`, `network.rs`, `power.rs`, `hyprland.rs`, `parse_upower_message`, `Sync`?** - **Why does `RawEvent` connect `RawEvent` to `Server`, `filesystem.rs`, `git.rs`, `udev.rs`, `systemd.rs`, `podman.rs`, `run_udev_monitor`, `bluetooth.rs`, `RtnetlinkAdapter`, `network.rs`, `power.rs`, `hyprland.rs`, `Adapter`?**
_High betweenness centrality (0.089) - this node is a cross-community bridge._ _High betweenness centrality (0.094) - this node is a cross-community bridge._
- **Why does `RawEvent` connect `RawEvent` to `Server`, `filesystem.rs`, `git.rs`, `udev.rs`, `systemd.rs`, `podman.rs`, `bluetooth.rs`, `Adapter`, `network.rs`, `power.rs`, `hyprland.rs`, `parse_upower_message`?** - **Why does `Adapter` connect `Adapter` to `Server`, `filesystem.rs`, `git.rs`, `udev.rs`, `systemd.rs`, `podman.rs`, `run_udev_monitor`, `bluetooth.rs`, `RtnetlinkAdapter`, `network.rs`, `power.rs`, `hyprland.rs`, `Sync`?**
_High betweenness centrality (0.084) - this node is a cross-community bridge._ _High betweenness centrality (0.077) - this node is a cross-community bridge._
- **Why does `BreadEvent` connect `RawEvent` to `ModuleHostLua`, `LuaEngine`, `.new`, `Server`, `ModuleHostRegistry`, `state_engine.rs`, `StateHandle`?** - **Why does `BreadEvent` connect `RawEvent` to `ModuleHostLua`, `lua/mod.rs`, `ModuleHostRegistry`, `Server`, `state_engine.rs`, `run_state_engine`?**
_High betweenness centrality (0.061) - this node is a cross-community bridge._ _High betweenness centrality (0.063) - this node is a cross-community bridge._
- **What connects `build.sh script`, `install.sh script`, `Remotes` to the rest of the system?** - **What connects `install.sh script`, `Branches`, `The release cycle` to the rest of the system?**
_182 weakly-connected nodes found - possible documentation gaps or missing edges._ _179 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `LuaEngine` be split into smaller, more focused modules?** - **Should `lua/mod.rs` be split into smaller, more focused modules?**
_Cohesion score 0.11153846153846154 - nodes in this community are weakly interconnected._ _Cohesion score 0.056 - nodes in this community are weakly interconnected._
- **Should `RawEvent` be split into smaller, more focused modules?** - **Should `RawEvent` be split into smaller, more focused modules?**
_Cohesion score 0.05702970297029703 - nodes in this community are weakly interconnected._ _Cohesion score 0.051842708517016396 - nodes in this community are weakly interconnected._
- **Should `config.rs` be split into smaller, more focused modules?** - **Should `config.rs` be split into smaller, more focused modules?**
_Cohesion score 0.0670122176971492 - nodes in this community are weakly interconnected._ _Cohesion score 0.07191780821917808 - nodes in this community are weakly interconnected._

View file

@ -1 +0,0 @@
{"nodes": [{"id": "$graphify-root$_deprecations_md", "label": "DEPRECATIONS.md", "file_type": "document", "source_file": "DEPRECATIONS.md", "source_location": "L1"}, {"id": "$graphify-root$_deprecations_deprecations", "label": "Deprecations", "file_type": "document", "source_file": "DEPRECATIONS.md", "source_location": "L1"}, {"id": "$graphify-root$_deprecations_hyprland_legacy_flat_event_names_since_v1_5", "label": "Hyprland legacy flat event names (since v1.5)", "file_type": "document", "source_file": "DEPRECATIONS.md", "source_location": "L8"}], "edges": [{"source": "$graphify-root$_deprecations_md", "target": "$graphify-root$_deprecations_deprecations", "relation": "contains", "confidence": "EXTRACTED", "source_file": "DEPRECATIONS.md", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_deprecations_md", "target": "$graphify-root$_documentation_md", "relation": "references", "confidence": "EXTRACTED", "source_file": "DEPRECATIONS.md", "source_location": "L4", "weight": 1.0, "target_file": "$graphify-root$/Documentation.md"}, {"source": "$graphify-root$_deprecations_deprecations", "target": "$graphify-root$_deprecations_hyprland_legacy_flat_event_names_since_v1_5", "relation": "contains", "confidence": "EXTRACTED", "source_file": "DEPRECATIONS.md", "source_location": "L8", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}

View file

@ -1 +0,0 @@
{"nodes": [{"id": "$graphify-root$_claude_md", "label": "CLAUDE.md", "file_type": "document", "source_file": "CLAUDE.md", "source_location": "L1"}, {"id": "$graphify-root$_claude_claude_md_repo_hygiene", "label": "CLAUDE.md \u2014 Repo hygiene", "file_type": "document", "source_file": "CLAUDE.md", "source_location": "L1"}, {"id": "$graphify-root$_claude_remotes", "label": "Remotes", "file_type": "document", "source_file": "CLAUDE.md", "source_location": "L10"}, {"id": "$graphify-root$_claude_ci", "label": "CI", "file_type": "document", "source_file": "CLAUDE.md", "source_location": "L14"}, {"id": "$graphify-root$_claude_don_t", "label": "Don't", "file_type": "document", "source_file": "CLAUDE.md", "source_location": "L19"}], "edges": [{"source": "$graphify-root$_claude_md", "target": "$graphify-root$_claude_claude_md_repo_hygiene", "relation": "contains", "confidence": "EXTRACTED", "source_file": "CLAUDE.md", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_claude_claude_md_repo_hygiene", "target": "$graphify-root$_claude_remotes", "relation": "contains", "confidence": "EXTRACTED", "source_file": "CLAUDE.md", "source_location": "L10", "weight": 1.0}, {"source": "$graphify-root$_claude_claude_md_repo_hygiene", "target": "$graphify-root$_claude_ci", "relation": "contains", "confidence": "EXTRACTED", "source_file": "CLAUDE.md", "source_location": "L14", "weight": 1.0}, {"source": "$graphify-root$_claude_claude_md_repo_hygiene", "target": "$graphify-root$_claude_don_t", "relation": "contains", "confidence": "EXTRACTED", "source_file": "CLAUDE.md", "source_location": "L19", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"nodes": [{"id": "$graphify-root$_ci_build_sh", "label": "build.sh", "file_type": "code", "source_file": "ci/build.sh", "source_location": "L1", "metadata": {"language": "bash", "kind": "file"}}, {"id": "$graphify-root$_ci_build_sh__entry", "label": "build.sh script", "file_type": "code", "source_file": "ci/build.sh", "source_location": "L1", "metadata": {"language": "bash", "kind": "bash_entrypoint"}}], "edges": [{"source": "$graphify-root$_ci_build_sh", "target": "$graphify-root$_ci_build_sh__entry", "relation": "contains", "confidence": "EXTRACTED", "source_file": "ci/build.sh", "source_location": "L1", "weight": 1.0}], "raw_calls": [{"language": "bash", "callee": "set", "caller_nid": "$graphify-root$_ci_build_sh__entry", "source_file": "ci/build.sh", "source_location": "L9"}, {"language": "bash", "callee": "rm", "caller_nid": "$graphify-root$_ci_build_sh__entry", "source_file": "ci/build.sh", "source_location": "L16"}, {"language": "bash", "callee": "git", "caller_nid": "$graphify-root$_ci_build_sh__entry", "source_file": "ci/build.sh", "source_location": "L17"}], "bash_sources": []}

View file

@ -1 +0,0 @@
{"nodes": [{"id": "$graphify-root$_examples_modules_readme_md", "label": "README.md", "file_type": "document", "source_file": "examples/modules/README.md", "source_location": "L1"}, {"id": "$graphify-root$_examples_modules_readme_example_bread_modules", "label": "Example bread modules", "file_type": "document", "source_file": "examples/modules/README.md", "source_location": "L1"}, {"id": "$graphify-root$_examples_modules_readme_installing", "label": "Installing", "file_type": "document", "source_file": "examples/modules/README.md", "source_location": "L7"}, {"id": "$graphify-root$_examples_modules_readme_modules", "label": "Modules", "file_type": "document", "source_file": "examples/modules/README.md", "source_location": "L33"}], "edges": [{"source": "$graphify-root$_examples_modules_readme_md", "target": "$graphify-root$_examples_modules_readme_example_bread_modules", "relation": "contains", "confidence": "EXTRACTED", "source_file": "examples/modules/README.md", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_examples_modules_readme_md", "target": "$graphify-root$_examples_md", "relation": "references", "confidence": "EXTRACTED", "source_file": "examples/modules/README.md", "source_location": "L4", "weight": 1.0, "target_file": "$graphify-root$/Examples.md"}, {"source": "$graphify-root$_examples_modules_readme_example_bread_modules", "target": "$graphify-root$_examples_modules_readme_installing", "relation": "contains", "confidence": "EXTRACTED", "source_file": "examples/modules/README.md", "source_location": "L7", "weight": 1.0}, {"source": "$graphify-root$_examples_modules_readme_md", "target": "$graphify-root$_examples_modules_permissions_md", "relation": "references", "confidence": "EXTRACTED", "source_file": "examples/modules/README.md", "source_location": "L18", "weight": 1.0}, {"source": "$graphify-root$_examples_modules_readme_md", "target": "$graphify-root$_documentation_md", "relation": "references", "confidence": "EXTRACTED", "source_file": "examples/modules/README.md", "source_location": "L19", "weight": 1.0, "target_file": "$graphify-root$/Documentation.md"}, {"source": "$graphify-root$_examples_modules_readme_example_bread_modules", "target": "$graphify-root$_examples_modules_readme_modules", "relation": "contains", "confidence": "EXTRACTED", "source_file": "examples/modules/README.md", "source_location": "L33", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}

View file

@ -1 +0,0 @@
{"nodes": [{"id": "$graphify-root$_agents_md", "label": "AGENTS.md", "file_type": "document", "source_file": "AGENTS.md", "source_location": "L1"}, {"id": "$graphify-root$_agents_agents_md_repo_hygiene", "label": "AGENTS.md \u2014 Repo hygiene", "file_type": "document", "source_file": "AGENTS.md", "source_location": "L1"}, {"id": "$graphify-root$_agents_remotes", "label": "Remotes", "file_type": "document", "source_file": "AGENTS.md", "source_location": "L16"}, {"id": "$graphify-root$_agents_ci", "label": "CI", "file_type": "document", "source_file": "AGENTS.md", "source_location": "L22"}, {"id": "$graphify-root$_agents_local_architecture_still_true", "label": "Local architecture (still true)", "file_type": "document", "source_file": "AGENTS.md", "source_location": "L31"}, {"id": "$graphify-root$_agents_don_t", "label": "Don't", "file_type": "document", "source_file": "AGENTS.md", "source_location": "L39"}], "edges": [{"source": "$graphify-root$_agents_md", "target": "$graphify-root$_agents_agents_md_repo_hygiene", "relation": "contains", "confidence": "EXTRACTED", "source_file": "AGENTS.md", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_agents_agents_md_repo_hygiene", "target": "$graphify-root$_agents_remotes", "relation": "contains", "confidence": "EXTRACTED", "source_file": "AGENTS.md", "source_location": "L16", "weight": 1.0}, {"source": "$graphify-root$_agents_agents_md_repo_hygiene", "target": "$graphify-root$_agents_ci", "relation": "contains", "confidence": "EXTRACTED", "source_file": "AGENTS.md", "source_location": "L22", "weight": 1.0}, {"source": "$graphify-root$_agents_agents_md_repo_hygiene", "target": "$graphify-root$_agents_local_architecture_still_true", "relation": "contains", "confidence": "EXTRACTED", "source_file": "AGENTS.md", "source_location": "L31", "weight": 1.0}, {"source": "$graphify-root$_agents_agents_md_repo_hygiene", "target": "$graphify-root$_agents_don_t", "relation": "contains", "confidence": "EXTRACTED", "source_file": "AGENTS.md", "source_location": "L39", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
1786850688.401695 1785892770.494006

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -1,352 +1,327 @@
{ {
"bread-cli/src/hooks_git.rs": { "bread-cli/src/hooks_git.rs": {
"mtime": 1784781158.0572724, "mtime": 1786800467.7200727,
"ast_hash": "7a29b5d5d90170f0aef9cececc7d8656", "ast_hash": "7a29b5d5d90170f0aef9cececc7d8656",
"semantic_hash": "7a29b5d5d90170f0aef9cececc7d8656" "semantic_hash": "7a29b5d5d90170f0aef9cececc7d8656"
}, },
"bread-cli/src/hooks_shell.rs": { "bread-cli/src/hooks_shell.rs": {
"mtime": 1784781158.0582726, "mtime": 1786800467.7201686,
"ast_hash": "c5d5b8922fcff79954ddb00496721603", "ast_hash": "c5d5b8922fcff79954ddb00496721603",
"semantic_hash": "c5d5b8922fcff79954ddb00496721603" "semantic_hash": "c5d5b8922fcff79954ddb00496721603"
}, },
"bread-cli/src/lib.rs": { "bread-cli/src/lib.rs": {
"mtime": 1778512393.4941568, "mtime": 1786800467.7201686,
"ast_hash": "d1bf6e1c239498521c42418f672bc400", "ast_hash": "d1bf6e1c239498521c42418f672bc400",
"semantic_hash": "d1bf6e1c239498521c42418f672bc400" "semantic_hash": "d1bf6e1c239498521c42418f672bc400"
}, },
"bread-cli/src/main.rs": { "bread-cli/src/main.rs": {
"mtime": 1785882914.3322613, "mtime": 1786800467.7201686,
"ast_hash": "e1fbe63ea3b10e0887072ab7133d0508", "ast_hash": "e1fbe63ea3b10e0887072ab7133d0508",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-cli/src/modules_mgmt.rs": { "bread-cli/src/modules_mgmt.rs": {
"mtime": 1785882914.3322613, "mtime": 1786801244.1263742,
"ast_hash": "1cb6f3e4fa26108f81215ab57bb02b91", "ast_hash": "1cb6f3e4fa26108f81215ab57bb02b91",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-cli/tests/modules.rs": { "bread-cli/tests/modules.rs": {
"mtime": 1784781158.0602725, "mtime": 1786801244.1297076,
"ast_hash": "84fca8f87dc915b1cb523f8199e78521", "ast_hash": "84fca8f87dc915b1cb523f8199e78521",
"semantic_hash": "84fca8f87dc915b1cb523f8199e78521" "semantic_hash": "84fca8f87dc915b1cb523f8199e78521"
}, },
"bread-emit/src/main.rs": { "bread-emit/src/main.rs": {
"mtime": 1784781158.0616376, "mtime": 1786800467.7206197,
"ast_hash": "350cefbf697cfd93f7df7b7bcc45a101", "ast_hash": "350cefbf697cfd93f7df7b7bcc45a101",
"semantic_hash": "350cefbf697cfd93f7df7b7bcc45a101" "semantic_hash": "350cefbf697cfd93f7df7b7bcc45a101"
}, },
"bread-shared/src/apps.rs": { "bread-shared/src/apps.rs": {
"mtime": 1786805047.7009091, "mtime": 1786801218.1467261,
"ast_hash": "e8419571e7a322019d71c89559fb02fb", "ast_hash": "e8419571e7a322019d71c89559fb02fb",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-shared/src/glob.rs": { "bread-shared/src/glob.rs": {
"mtime": 1784781158.0616376, "mtime": 1786800467.7210522,
"ast_hash": "0594a313cb1909d1cca5fd5b8f241b68", "ast_hash": "0594a313cb1909d1cca5fd5b8f241b68",
"semantic_hash": "0594a313cb1909d1cca5fd5b8f241b68" "semantic_hash": "0594a313cb1909d1cca5fd5b8f241b68"
}, },
"bread-shared/src/lib.rs": { "bread-shared/src/lib.rs": {
"mtime": 1785882914.3346088, "mtime": 1786801244.1297076,
"ast_hash": "236b73bad29f1544a28fbf3ee1ccc795", "ast_hash": "236b73bad29f1544a28fbf3ee1ccc795",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-shared/src/widget.rs": { "bread-shared/src/widget.rs": {
"mtime": 1784781173.9236407, "mtime": 1786801244.1297076,
"ast_hash": "33272eb38c7fffbfa180cef7c5a286fc", "ast_hash": "33272eb38c7fffbfa180cef7c5a286fc",
"semantic_hash": "33272eb38c7fffbfa180cef7c5a286fc" "semantic_hash": "33272eb38c7fffbfa180cef7c5a286fc"
}, },
"breadd/src/adapters/bluetooth.rs": { "breadd/src/adapters/bluetooth.rs": {
"mtime": 1778843727.5135255, "mtime": 1786800467.7214499,
"ast_hash": "4d1df67347d5b7c9cdbcb12921a6ae28", "ast_hash": "4d1df67347d5b7c9cdbcb12921a6ae28",
"semantic_hash": "4d1df67347d5b7c9cdbcb12921a6ae28" "semantic_hash": "4d1df67347d5b7c9cdbcb12921a6ae28"
}, },
"breadd/src/adapters/filesystem.rs": { "breadd/src/adapters/filesystem.rs": {
"mtime": 1784781158.0616376, "mtime": 1786800467.7215972,
"ast_hash": "8ec9dcd8de13f30922cfbe9b4a3fff56", "ast_hash": "8ec9dcd8de13f30922cfbe9b4a3fff56",
"semantic_hash": "8ec9dcd8de13f30922cfbe9b4a3fff56" "semantic_hash": "8ec9dcd8de13f30922cfbe9b4a3fff56"
}, },
"breadd/src/adapters/git.rs": { "breadd/src/adapters/git.rs": {
"mtime": 1784781158.0616376, "mtime": 1786800467.7215972,
"ast_hash": "6e6ff6ca318e28cd94690c30d41b520b", "ast_hash": "6e6ff6ca318e28cd94690c30d41b520b",
"semantic_hash": "6e6ff6ca318e28cd94690c30d41b520b" "semantic_hash": "6e6ff6ca318e28cd94690c30d41b520b"
}, },
"breadd/src/adapters/hyprland.rs": { "breadd/src/adapters/hyprland.rs": {
"mtime": 1786805047.7042425, "mtime": 1786800467.7215972,
"ast_hash": "86056a351db61eb1788a2147d859fc5c", "ast_hash": "71537dbd86b413d94476f8022054f1f4",
"semantic_hash": "" "semantic_hash": "71537dbd86b413d94476f8022054f1f4"
}, },
"breadd/src/adapters/mod.rs": { "breadd/src/adapters/mod.rs": {
"mtime": 1784781158.0616376, "mtime": 1786800467.7215972,
"ast_hash": "63885887c2fc3ea2314d7fe095e5df61", "ast_hash": "63885887c2fc3ea2314d7fe095e5df61",
"semantic_hash": "63885887c2fc3ea2314d7fe095e5df61" "semantic_hash": "63885887c2fc3ea2314d7fe095e5df61"
}, },
"breadd/src/adapters/network.rs": { "breadd/src/adapters/network.rs": {
"mtime": 1778470615.8460202, "mtime": 1786800467.7215972,
"ast_hash": "8706dc546b7e08d5aa084ca58c48559c", "ast_hash": "8706dc546b7e08d5aa084ca58c48559c",
"semantic_hash": "8706dc546b7e08d5aa084ca58c48559c" "semantic_hash": "8706dc546b7e08d5aa084ca58c48559c"
}, },
"breadd/src/adapters/network_rtnetlink.rs": { "breadd/src/adapters/network_rtnetlink.rs": {
"mtime": 1784781158.0616376, "mtime": 1786800467.7215972,
"ast_hash": "0c9d0bb2693bc46b11807f6b8ebb7c4c", "ast_hash": "0c9d0bb2693bc46b11807f6b8ebb7c4c",
"semantic_hash": "0c9d0bb2693bc46b11807f6b8ebb7c4c" "semantic_hash": "0c9d0bb2693bc46b11807f6b8ebb7c4c"
}, },
"breadd/src/adapters/podman.rs": { "breadd/src/adapters/podman.rs": {
"mtime": 1784781158.0622723, "mtime": 1786800467.7215972,
"ast_hash": "97c08551c0fe53b0a5888d98730d35db", "ast_hash": "97c08551c0fe53b0a5888d98730d35db",
"semantic_hash": "97c08551c0fe53b0a5888d98730d35db" "semantic_hash": "97c08551c0fe53b0a5888d98730d35db"
}, },
"breadd/src/adapters/power.rs": { "breadd/src/adapters/power.rs": {
"mtime": 1778470615.838569, "mtime": 1786800467.7215972,
"ast_hash": "987d202ec0d30ec26b1d747a6e320ed7", "ast_hash": "987d202ec0d30ec26b1d747a6e320ed7",
"semantic_hash": "987d202ec0d30ec26b1d747a6e320ed7" "semantic_hash": "987d202ec0d30ec26b1d747a6e320ed7"
}, },
"breadd/src/adapters/power_upower.rs": { "breadd/src/adapters/power_upower.rs": {
"mtime": 1784781158.0622723, "mtime": 1786800467.7215972,
"ast_hash": "fc0a36ca76d340c8be77644f63e462bf", "ast_hash": "fc0a36ca76d340c8be77644f63e462bf",
"semantic_hash": "fc0a36ca76d340c8be77644f63e462bf" "semantic_hash": "fc0a36ca76d340c8be77644f63e462bf"
}, },
"breadd/src/adapters/systemd.rs": { "breadd/src/adapters/systemd.rs": {
"mtime": 1784781158.0622723, "mtime": 1786800467.7215972,
"ast_hash": "b3884dbecd39acc38abdf67589a8b954", "ast_hash": "b3884dbecd39acc38abdf67589a8b954",
"semantic_hash": "b3884dbecd39acc38abdf67589a8b954" "semantic_hash": "b3884dbecd39acc38abdf67589a8b954"
}, },
"breadd/src/adapters/udev.rs": { "breadd/src/adapters/udev.rs": {
"mtime": 1786805047.7042425, "mtime": 1786800978.7399695,
"ast_hash": "568993c8c2dd218879eec57aee50a6b9", "ast_hash": "568993c8c2dd218879eec57aee50a6b9",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/core/config.rs": { "breadd/src/core/config.rs": {
"mtime": 1785882914.3346088, "mtime": 1786800467.7215972,
"ast_hash": "7f479bfb51647e6131c14f0c2f153d95", "ast_hash": "7f479bfb51647e6131c14f0c2f153d95",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/core/mod.rs": { "breadd/src/core/mod.rs": {
"mtime": 1785882914.3346088, "mtime": 1786800467.7223108,
"ast_hash": "e38bfdb894eabd208941bd3ffb1ef464", "ast_hash": "e38bfdb894eabd208941bd3ffb1ef464",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/core/normalizer.rs": { "breadd/src/core/normalizer.rs": {
"mtime": 1786805047.7042425, "mtime": 1786801218.2233918,
"ast_hash": "3e0e6e0b46cc3d42dbfd6348fc5f0bb1", "ast_hash": "1596f60df5049ff8715f66f64bee9f8a",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/core/state_engine.rs": { "breadd/src/core/state_engine.rs": {
"mtime": 1786805047.7042425, "mtime": 1786800467.7223108,
"ast_hash": "0e54b3b7d1de26db074454d469543192", "ast_hash": "84a35ee78f3881261a3ce378ea3c03a0",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/core/subscriptions.rs": { "breadd/src/core/subscriptions.rs": {
"mtime": 1784781158.0632722, "mtime": 1786800467.7223108,
"ast_hash": "8735d4717b74523c787dab9ea2b0bbd8", "ast_hash": "8735d4717b74523c787dab9ea2b0bbd8",
"semantic_hash": "8735d4717b74523c787dab9ea2b0bbd8" "semantic_hash": "8735d4717b74523c787dab9ea2b0bbd8"
}, },
"breadd/src/core/supervisor.rs": { "breadd/src/core/supervisor.rs": {
"mtime": 1778680581.3778126, "mtime": 1786800467.7223108,
"ast_hash": "403f7ba1807c71f9b89d81bfeff2681a", "ast_hash": "403f7ba1807c71f9b89d81bfeff2681a",
"semantic_hash": "403f7ba1807c71f9b89d81bfeff2681a" "semantic_hash": "403f7ba1807c71f9b89d81bfeff2681a"
}, },
"breadd/src/core/types.rs": { "breadd/src/core/types.rs": {
"mtime": 1785882914.3346088, "mtime": 1786800467.7223108,
"ast_hash": "8bd070d4c09725d8717749a79aa42a92", "ast_hash": "8bd070d4c09725d8717749a79aa42a92",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/ipc/mod.rs": { "breadd/src/ipc/mod.rs": {
"mtime": 1786805047.7042425, "mtime": 1786800912.9916558,
"ast_hash": "d5bc8f1194e7356de3fe181c68133cba", "ast_hash": "54af229257e555313f7d4b59d57ff8d3",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/lua/mod.rs": { "breadd/src/lua/mod.rs": {
"mtime": 1785882914.3346088, "mtime": 1786801244.1297076,
"ast_hash": "ed13e2495399d532625653a22a4fee14", "ast_hash": "ed13e2495399d532625653a22a4fee14",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/main.rs": { "breadd/src/main.rs": {
"mtime": 1785882914.3346088, "mtime": 1786800467.722855,
"ast_hash": "4b31888cc9e76210df0c7cff3a8b1e42", "ast_hash": "4b31888cc9e76210df0c7cff3a8b1e42",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/tests/ipc_integration.rs": { "breadd/tests/ipc_integration.rs": {
"mtime": 1786805047.7042425, "mtime": 1786801218.36339,
"ast_hash": "2b5bade2cc2e189a3af9c692232be258", "ast_hash": "2b5bade2cc2e189a3af9c692232be258",
"semantic_hash": "" "semantic_hash": ""
}, },
"examples/modules/active-window-widget.lua": { "examples/modules/active-window-widget.lua": {
"mtime": 1784781173.9256406, "mtime": 1786800467.7243168,
"ast_hash": "a5f6fb2c54775a49ddaf29674f86571d", "ast_hash": "a5f6fb2c54775a49ddaf29674f86571d",
"semantic_hash": "a5f6fb2c54775a49ddaf29674f86571d" "semantic_hash": "a5f6fb2c54775a49ddaf29674f86571d"
}, },
"examples/modules/bluetooth-toggle-widget.lua": { "examples/modules/bluetooth-toggle-widget.lua": {
"mtime": 1784781173.9256406, "mtime": 1786800467.7243168,
"ast_hash": "c8529dc45862adf4f63db48674a28277", "ast_hash": "c8529dc45862adf4f63db48674a28277",
"semantic_hash": "c8529dc45862adf4f63db48674a28277" "semantic_hash": "c8529dc45862adf4f63db48674a28277"
}, },
"examples/modules/dock-monitors.lua": { "examples/modules/dock-monitors.lua": {
"mtime": 1784781158.0663679, "mtime": 1786800467.724451,
"ast_hash": "4abe51f19614d2bf117ac35e95324801", "ast_hash": "4abe51f19614d2bf117ac35e95324801",
"semantic_hash": "4abe51f19614d2bf117ac35e95324801" "semantic_hash": "4abe51f19614d2bf117ac35e95324801"
}, },
"examples/modules/dock-workflow.lua": { "examples/modules/dock-workflow.lua": {
"mtime": 1785882914.337525, "mtime": 1786800467.724451,
"ast_hash": "84cf81cfb7780e89a46e96a6ccfbad15", "ast_hash": "84cf81cfb7780e89a46e96a6ccfbad15",
"semantic_hash": "" "semantic_hash": ""
}, },
"examples/modules/focus-mode-widget.lua": { "examples/modules/focus-mode-widget.lua": {
"mtime": 1784781173.9256406, "mtime": 1786800467.724451,
"ast_hash": "f62c366c2c85cfbe59eef663fc607230", "ast_hash": "f62c366c2c85cfbe59eef663fc607230",
"semantic_hash": "f62c366c2c85cfbe59eef663fc607230" "semantic_hash": "f62c366c2c85cfbe59eef663fc607230"
}, },
"examples/modules/git-branch-widget.lua": { "examples/modules/git-branch-widget.lua": {
"mtime": 1785882914.337525, "mtime": 1786800467.724451,
"ast_hash": "ebeeaebab5b5b29619f3db65c1df57d8", "ast_hash": "ebeeaebab5b5b29619f3db65c1df57d8",
"semantic_hash": "" "semantic_hash": ""
}, },
"examples/modules/low-battery-warning.lua": { "examples/modules/low-battery-warning.lua": {
"mtime": 1784781158.0663679, "mtime": 1786800467.724451,
"ast_hash": "e0ba79860562fc36fa8cb183bc576fb7", "ast_hash": "e0ba79860562fc36fa8cb183bc576fb7",
"semantic_hash": "e0ba79860562fc36fa8cb183bc576fb7" "semantic_hash": "e0ba79860562fc36fa8cb183bc576fb7"
}, },
"examples/modules/pause-media-on-headphone-unplug.lua": { "examples/modules/pause-media-on-headphone-unplug.lua": {
"mtime": 1784781158.0663679, "mtime": 1786800467.724451,
"ast_hash": "6c4cf82b6963eb93df224bed60d06c2d", "ast_hash": "6c4cf82b6963eb93df224bed60d06c2d",
"semantic_hash": "6c4cf82b6963eb93df224bed60d06c2d" "semantic_hash": "6c4cf82b6963eb93df224bed60d06c2d"
}, },
"examples/modules/workflow-status-widget.lua": { "examples/modules/workflow-status-widget.lua": {
"mtime": 1784781173.9256406, "mtime": 1786800467.724451,
"ast_hash": "9f913a66a0f8654dadc1c5d6c2be0938", "ast_hash": "9f913a66a0f8654dadc1c5d6c2be0938",
"semantic_hash": "9f913a66a0f8654dadc1c5d6c2be0938" "semantic_hash": "9f913a66a0f8654dadc1c5d6c2be0938"
}, },
"scripts/install.sh": { "scripts/install.sh": {
"mtime": 1786805047.710909, "mtime": 1786800994.0764284,
"ast_hash": "35d1a7f63824e9176bd105ab3d699576", "ast_hash": "35d1a7f63824e9176bd105ab3d699576",
"semantic_hash": "" "semantic_hash": ""
}, },
"CONTRIBUTING.md": { "CONTRIBUTING.md": {
"mtime": 1785909683.2691193, "mtime": 1786800467.716893,
"ast_hash": "6d056fcf0dae29949d19c41b41792879", "ast_hash": "6d056fcf0dae29949d19c41b41792879",
"semantic_hash": "" "semantic_hash": ""
}, },
"Documentation.md": { "Documentation.md": {
"mtime": 1786805047.7009091, "mtime": 1786801027.2816515,
"ast_hash": "e219e52fae2208f771d9b77d87d6790b", "ast_hash": "7b8471e7ae45aed70682bb858826860c",
"semantic_hash": "" "semantic_hash": ""
}, },
"Examples.md": { "Examples.md": {
"mtime": 1784781173.9236407, "mtime": 1786800467.716893,
"ast_hash": "6bcd7a14be53a9f67ef6912b160da8bc", "ast_hash": "6bcd7a14be53a9f67ef6912b160da8bc",
"semantic_hash": "6bcd7a14be53a9f67ef6912b160da8bc" "semantic_hash": "6bcd7a14be53a9f67ef6912b160da8bc"
}, },
"README.md": { "README.md": {
"mtime": 1786805047.7009091, "mtime": 1786800994.0964282,
"ast_hash": "97ae9f7efb9f2cdceb615a47cf3dfa1a", "ast_hash": "97ae9f7efb9f2cdceb615a47cf3dfa1a",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-module-host/src/io.rs": { "bread-module-host/src/io.rs": {
"mtime": 1786805041.5686455, "mtime": 1786801244.1297076,
"ast_hash": "dbe04e05d1cfb02770db8d7c0bcc0b68", "ast_hash": "dbe04e05d1cfb02770db8d7c0bcc0b68",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-module-host/src/lua_env.rs": { "bread-module-host/src/lua_env.rs": {
"mtime": 1786805041.5686455, "mtime": 1786801244.1297076,
"ast_hash": "97d009846e18d3c809663b1185c98bbb", "ast_hash": "97d009846e18d3c809663b1185c98bbb",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-module-host/src/main.rs": { "bread-module-host/src/main.rs": {
"mtime": 1785882914.3346088, "mtime": 1786801244.1297076,
"ast_hash": "9a93253ae44c764b8273df40999ddede", "ast_hash": "9a93253ae44c764b8273df40999ddede",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-shared/src/module_host_ipc.rs": { "bread-shared/src/module_host_ipc.rs": {
"mtime": 1785882914.3346088, "mtime": 1786801244.1297076,
"ast_hash": "fcdb4c03f242a5ea51fd3a096b711363", "ast_hash": "fcdb4c03f242a5ea51fd3a096b711363",
"semantic_hash": "" "semantic_hash": ""
}, },
"bread-shared/src/permissions.rs": { "bread-shared/src/permissions.rs": {
"mtime": 1785882914.3346088, "mtime": 1786800467.7210522,
"ast_hash": "800445ef9c90bdbf8f3ac9d4f1afd98e", "ast_hash": "800445ef9c90bdbf8f3ac9d4f1afd98e",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/core/rules.rs": { "breadd/src/core/rules.rs": {
"mtime": 1785882914.3346088, "mtime": 1786801244.1297076,
"ast_hash": "bd430cf213f400b8d4e96ccc35a76ff1", "ast_hash": "bd430cf213f400b8d4e96ccc35a76ff1",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/ipc/module_host_bridge.rs": { "breadd/src/ipc/module_host_bridge.rs": {
"mtime": 1785882914.3346088, "mtime": 1786801244.1297076,
"ast_hash": "caa9cf82695e48c88758fcfa835e1871", "ast_hash": "caa9cf82695e48c88758fcfa835e1871",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/src/module_host.rs": { "breadd/src/module_host.rs": {
"mtime": 1785895235.7449324, "mtime": 1786801244.1297076,
"ast_hash": "228826ff3c8941ea5f9bb28b66d637a9", "ast_hash": "228826ff3c8941ea5f9bb28b66d637a9",
"semantic_hash": "" "semantic_hash": ""
}, },
"breadd/tests/module_host_sandbox.rs": { "breadd/tests/module_host_sandbox.rs": {
"mtime": 1786805041.5686455, "mtime": 1786801244.1297076,
"ast_hash": "7e637b3a950c510215a6f6a7b88dd404", "ast_hash": "7e637b3a950c510215a6f6a7b88dd404",
"semantic_hash": "" "semantic_hash": ""
}, },
"examples/modules/cpu-temp-widget/init.lua": { "examples/modules/cpu-temp-widget/init.lua": {
"mtime": 1785882914.337525, "mtime": 1786800467.724451,
"ast_hash": "5ac893dd2f6af6d8b22dd290e3021f77", "ast_hash": "5ac893dd2f6af6d8b22dd290e3021f77",
"semantic_hash": "" "semantic_hash": ""
}, },
"xtask/src/main.rs": { "xtask/src/main.rs": {
"mtime": 1785909683.2724528, "mtime": 1786801244.1297076,
"ast_hash": "e914dd77ac3241df09e3a681228961db", "ast_hash": "e914dd77ac3241df09e3a681228961db",
"semantic_hash": "" "semantic_hash": ""
}, },
".forgejo/workflows/dev-release.yml": { ".forgejo/workflows/dev-release.yml": {
"mtime": 1786805047.7009091, "mtime": 1786800467.7161875,
"ast_hash": "ee3f41732b4ca3f34718b9c4fee68a4c", "ast_hash": "707129aa2fe79b4539d108c03e6d7abf",
"semantic_hash": "" "semantic_hash": ""
}, },
".forgejo/workflows/rc-release.yml": { ".forgejo/workflows/rc-release.yml": {
"mtime": 1786805047.7009091, "mtime": 1786800467.7167187,
"ast_hash": "ce93aea449f6698edb5e993a9f658392", "ast_hash": "3007d2c43d785cd72f0548096626a21d",
"semantic_hash": "" "semantic_hash": ""
}, },
".forgejo/workflows/release.yml": { ".forgejo/workflows/release.yml": {
"mtime": 1786805047.7009091, "mtime": 1786800467.7167187,
"ast_hash": "aa3e2461c895d0e8ff0cf0fa5556694d", "ast_hash": "3578aa39e7b2466822c8d85247489d36",
"semantic_hash": "" "semantic_hash": ""
}, },
"DEPRECATIONS.md": { "DEPRECATIONS.md": {
"mtime": 1786805047.7009091, "mtime": 1786800467.716893,
"ast_hash": "f3eb8f249d81c7c4faf2a131e890f459", "ast_hash": "1685b60fad2ed4184e4119c30866ec12",
"semantic_hash": "" "semantic_hash": ""
}, },
"examples/modules/README.md": { "examples/modules/README.md": {
"mtime": 1786858478.390548, "mtime": 1786800467.7242541,
"ast_hash": "d47ad6c1a9775a44212b89e281aae907", "ast_hash": "dfa8a72f41c08bd5b8036b684092a93b",
"semantic_hash": "" "semantic_hash": ""
}, },
"packaging/README.md": { "packaging/README.md": {
"mtime": 1786805047.710909, "mtime": 1786800994.0430956,
"ast_hash": "caa295eed64de126874a2e48b8aed8a2", "ast_hash": "caa295eed64de126874a2e48b8aed8a2",
"semantic_hash": "" "semantic_hash": ""
},
"ci/build.sh": {
"mtime": 1786805047.7071788,
"ast_hash": "363dbb1a10cf8e098a0451f43ff101cf",
"semantic_hash": ""
},
"examples/modules/external-monitors.lua": {
"mtime": 1786858478.390548,
"ast_hash": "ba0f44a4d84889b9828201aff9186466",
"semantic_hash": ""
},
".forgejo/workflows/check.yml": {
"mtime": 1786805047.7009091,
"ast_hash": "4ceb400224458b1a43c5ddc44cfbd187",
"semantic_hash": ""
},
"AGENTS.md": {
"mtime": 1786805047.7009091,
"ast_hash": "513e4a05cbbcdcb47b208b781f4d78bb",
"semantic_hash": ""
},
"CLAUDE.md": {
"mtime": 1784778267.6147587,
"ast_hash": "589cfefd400c641c059a2f8516ff2037",
"semantic_hash": ""
} }
} }