Compare commits

..

6 commits
v0.8.0 ... main

Author SHA1 Message Date
Breadway
c97029f654 Document bread init: api-schema, README CLI reference, hooks_shell note
Some checks failed
dev release / build (push) Failing after 1m47s
Registers 'init' as a cli_command in api-schema.toml and adds its
lines to README.md's CLI reference, keeping xtask check-docs clean.
Also adds a note to hooks_shell.rs's module header explaining why the
compositor case earns a consent-gated config edit that the shell-hook
module deliberately avoids, so the two don't read as contradicting
each other.
2026-08-26 19:31:58 +08:00
Breadway
1fce1979c3 Add bread init: Hyprland compositor layer-rule integration
Installs the layerrule blur/ignore_alpha/animation integration that
breadbar's translucency depends on into a user's hyprland.lua, with a
consent-gated single marked block, timestamped backup, dry-run/--yes,
and --undo. Generates a self-contained ~/.config/hypr/bread.lua that
reads layerrules.json (bread-theme layerrules) and emits hl.layer_rule
calls, appearance fields only. Falls back to printing a snippet
(translated to hyprlang .conf syntax when relevant) rather than
writing when the config isn't a Lua hyprland.lua bread recognizes, and
refuses to install when an existing breadbar layer rule (e.g. BOS's
own dotfiles) is already found under ~/.config/hypr.

bread doctor now reports compositor integration status via a pure
filesystem check, independent of the daemon; it never installs
anything itself.

Wired as a top-level Commands::Init (not a HooksCommand variant) since
it needs diff/consent/backup/undo machinery the hooks family has no
use for.
2026-08-26 19:31:18 +08:00
Breadway
72afe790fd Fix clippy warnings across workspace
Some checks failed
dev release / build (push) Failing after 7s
- io.rs: factor the pending-RPC-replies map into a PendingReplies
  type alias (type_complexity)
- lua_env.rs: drop a redundant i64->i64 cast and an unneeded borrow
  before create_string (unnecessary_cast, needless_borrows_for_generic_args)
- state_engine.rs: iterate watches.values() instead of discarding the
  key from watches.iter() (for_kv_map)
- module_host_sandbox.rs: collapse nested if into a single condition
  (collapsible_if)

(cherry picked from commit 49063cb98ddfeae35d842a6cf439123cfda763d9)
2026-08-23 14:57:43 +08:00
Breadway
e073a17353 ci: package bread-emit and bread-module-host on all tracks
Some checks failed
check / check (push) Failing after 33s
dev release / build (push) Failing after 1m7s
bakery.toml already lists all four binaries; copy, hash, and GitHub
Release upload them. release.yml clones bread-ecosystem via mktemp like
dev/rc so concurrent runner jobs do not race on /tmp/bread-ecosystem-ci.
2026-08-23 14:38:52 +08:00
Breadway
3865327c66 fix(module-host): canonicalize path grants and exec without a shell
RPC-side path_allowed now canonicalizes (parent+filename for new files)
and uses Path::starts_with so ../.ssh and /Wallpapers-evil cannot ride a
Wallpapers grant. exec/exec_capture parse argv, reject shell
metacharacters, and spawn Command::new instead of sh -c. Reserve the
daemon-synthesized module/state/widget/reload event families.
2026-08-23 14:38:52 +08:00
Breadway
670cf22f2c can't be bothered writing a commit message
Some checks failed
dev release / build (push) Failing after 5m30s
2026-08-16 14:23:42 +08:00
41 changed files with 70136 additions and 11372 deletions

View file

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

View file

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

View file

@ -28,7 +28,7 @@ jobs:
VERSION="${GITHUB_REF_NAME#v}"
PKG_DIR="/srv/breadway-dl/bread/${VERSION}"
mkdir -p "${PKG_DIR}"
for bin in breadd bread; do
for bin in breadd bread bread-emit bread-module-host; do
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
strip "${PKG_DIR}/${bin}-x86_64"
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \
@ -48,9 +48,13 @@ 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)"
exit 1
fi
rm -rf /tmp/bread-ecosystem-ci
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci
bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh
rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true
# mktemp: a fixed clone path races when multiple repos' release
# workflows run close together on the same self-hosted runner.
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
env:
@ -64,6 +68,10 @@ jobs:
gh release upload "${GITHUB_REF_NAME}" --repo Breadway/bread \
"${PKG_DIR}/breadd-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}/bread-x86_64.sha256" \
"${PKG_DIR}/bread-emit-x86_64.sha256" \
"${PKG_DIR}/bread-module-host-x86_64.sha256" \
--clobber

12
.grok/config.toml Normal file
View file

@ -0,0 +1,12 @@
# 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 *)",
]

20
CLAUDE.md Normal file
View file

@ -0,0 +1,20 @@
# 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,6 +237,12 @@ bread modules audit <name> # Scan a module's Lua source and suggest a
# Hooks
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
# 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,3 +608,8 @@ since = "0.7"
name = "doctor"
kind = "cli_command"
since = "0.7"
[[entry]]
name = "init"
kind = "cli_command"
since = "0.8"

View file

@ -13,6 +13,20 @@
//! 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.
//!
//! `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`
//!
//! The generated hooks shell out to the separate `bread-emit` binary

1281
bread-cli/src/init.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
mod init;
mod hooks_git;
mod hooks_shell;
mod modules_mgmt;
@ -99,6 +100,22 @@ enum Commands {
#[arg(long)]
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)]
@ -224,6 +241,9 @@ async fn main() -> Result<()> {
print_doctor(&socket).await?;
}
}
Commands::Init { dry_run, yes, undo } => {
init::run(dry_run, yes, undo)?;
}
}
Ok(())
@ -691,6 +711,7 @@ async fn print_doctor(socket: &Path) -> Result<()> {
println!();
println!(" start the daemon: systemctl --user start breadd");
println!(" view logs: journalctl --user -u breadd -f");
print_compositor_doctor_section();
return Ok(());
}
@ -775,6 +796,20 @@ 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 {

View file

@ -33,6 +33,10 @@ 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
/// unsolicited push (a subscribed event fired, a timer fired) or "the
/// connection is gone" (breadd exited, socket closed, etc).
@ -148,8 +152,7 @@ pub fn run(
// Lua-side call is blocked waiting for them; a dedicated thread
// bridges the synchronous `cmd_rx` (fed from the Lua thread) onto an
// async channel this task can select on.
let pending: Arc<Mutex<HashMap<String, mpsc::Sender<Result<Value, String>>>>> =
Arc::new(Mutex::new(HashMap::new()));
let pending: PendingReplies = Arc::new(Mutex::new(HashMap::new()));
let (async_cmd_tx, mut async_cmd_rx) = tokio::sync::mpsc::unbounded_channel::<IoCommand>();
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::Number(n) => {
if let Some(i) = n.as_i64() {
LuaValue::Integer(i as i64)
LuaValue::Integer(i)
} else {
LuaValue::Number(n.as_f64().unwrap_or(0.0))
}
@ -243,7 +243,7 @@ impl ModuleHostLua {
let decode_fn = lua.create_function(|lua, s: String| {
match serde_json::from_str::<JsonValue>(&s) {
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)?;

View file

@ -32,7 +32,9 @@ pub const KNOWN_APPS: &[&str] = &[
/// `workspace`, `window`, and `monitor` added (event families the Hyprland
/// and Bluetooth adapters already published under, but that were missing
/// 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] = &[
"terminal",
"git",
@ -53,6 +55,10 @@ const RESERVED_DOMAINS: &[&str] = &[
"workspace",
"window",
"monitor",
"module",
"state",
"widget",
"reload",
];
/// Whether `id` is a registered sibling-app id.
@ -174,6 +180,10 @@ mod tests {
"monitor",
"window",
"system",
"module",
"state",
"widget",
"reload",
] {
assert!(
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) {
for (_id, path) in watches.iter() {
for path in watches.values() {
let old_val = value_at_path(&before, path).unwrap_or(Value::Null);
let new_val = value_at_path(&after, path).unwrap_or(Value::Null);
if old_val != new_val {

View file

@ -25,6 +25,7 @@
//! directly from Lua bypasses every check in this file — that's the
//! scenario the sandbox exists for, not this file.
use std::collections::HashMap;
use std::path::{Component, Path, PathBuf};
use std::time::Duration;
use bread_shared::{glob, ModuleHostHello, ModuleHostPush, ModulePermission, PermissionKind};
@ -349,10 +350,7 @@ impl Server {
Ok(json!({ "ok": true }))
}
"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());
}
let path = req
@ -411,16 +409,17 @@ impl Server {
.and_then(Value::as_str)
.ok_or("missing cmd")?
.to_string();
if !bin_allowed(permissions, &cmd) {
return Err("command is outside this module's granted exec bin scope".to_string());
}
let argv = exec_argv(permissions, &cmd)?;
tokio::task::spawn_blocking(move || {
match std::process::Command::new("sh").arg("-c").arg(&cmd).status() {
match std::process::Command::new(&argv[0])
.args(&argv[1..])
.status()
{
Ok(status) if !status.success() => {
warn!(cmd = %cmd, code = ?status.code(), "module_host.exec exited non-zero");
warn!(cmd = %argv[0], code = ?status.code(), "module_host.exec exited non-zero");
}
Err(e) => {
error!(cmd = %cmd, error = %e, "module_host.exec failed to spawn");
error!(cmd = %argv[0], error = %e, "module_host.exec failed to spawn");
}
_ => {}
}
@ -437,17 +436,16 @@ impl Server {
.and_then(Value::as_str)
.ok_or("missing cmd")?
.to_string();
if !bin_allowed(permissions, &cmd) {
return Err("command is outside this module's granted exec bin scope".to_string());
}
let argv = exec_argv(permissions, &cmd)?;
let timeout_ms = req
.params
.get("timeout_ms")
.and_then(Value::as_u64)
.unwrap_or(2000);
let handle =
tokio::task::spawn_blocking(move || {
std::process::Command::new("sh").arg("-c").arg(&cmd).output()
let handle = tokio::task::spawn_blocking(move || {
std::process::Command::new(&argv[0])
.args(&argv[1..])
.output()
});
match tokio::time::timeout(Duration::from_millis(timeout_ms + 500), handle).await {
Ok(Ok(Ok(out))) => Ok(json!({
@ -474,7 +472,9 @@ impl Server {
(
ModuleLoadState::LoadError,
ModuleHostOutcome::LoadError(
error.clone().unwrap_or_else(|| "module load failed".to_string()),
error
.clone()
.unwrap_or_else(|| "module load failed".to_string()),
),
)
};
@ -501,13 +501,14 @@ impl Server {
/// Belt-and-suspenders path scoping for `fs_read`/`fs_write`: if the
/// manifest declared a `path` hint for this permission kind, the requested
/// path (after `~`-expansion) must fall under at least one granted prefix.
/// No hint at all means this RPC-level check stays permissive (matching
/// Workstream D's existing "un-hinted grant = ungated within that
/// namespace" behavior) — Landlock's own ruleset (built independently in
/// `module_host::apply_sandbox`) does NOT grant a filesystem rule for an
/// un-hinted permission, so the direct `os`/`io` escape hatch remains
/// kernel-denied for that case regardless of what this function returns.
/// path (after `~`-expansion and canonicalize) must be a real descendant
/// of at least one granted prefix. No hint at all means this RPC-level
/// check stays permissive (matching Workstream D's existing "un-hinted
/// grant = ungated within that namespace" behavior) — Landlock's own
/// ruleset (built independently in `module_host::apply_sandbox`) does NOT
/// grant a filesystem rule for an un-hinted permission, so the direct
/// `os`/`io` escape hatch remains kernel-denied for that case regardless
/// of what this function returns.
fn path_allowed(permissions: &[ModulePermission], kind: PermissionKind, path: &str) -> bool {
let hints: Vec<&String> = permissions
.iter()
@ -517,18 +518,93 @@ fn path_allowed(permissions: &[ModulePermission], kind: PermissionKind, path: &s
if hints.is_empty() {
return true;
}
let expanded = bread_shared::expand_path(path);
let Some(requested) = resolve_scoped_path(path) else {
return false;
};
hints.iter().any(|hint| {
let hint_expanded = bread_shared::expand_path(hint);
expanded.starts_with(&hint_expanded)
let Some(granted) = resolve_scoped_path(hint) else {
return false;
};
// Path::starts_with is component-wise; str::starts_with would let
// `/Wallpapers-evil` match a `/Wallpapers` grant.
requested.starts_with(&granted)
})
}
/// Same idea as [`path_allowed`] for `exec`'s `bin` hint: compares by file
/// name (so `bin = "hyprpaper"` matches a command invoking
/// `/usr/bin/hyprpaper` as well as a bare `hyprpaper`) or an exact leading
/// token match.
fn bin_allowed(permissions: &[ModulePermission], cmd: &str) -> bool {
/// Canonicalize for containment: existing paths resolve symlinks and `..`;
/// new files canonicalize the parent then re-join the filename. Anything
/// that still contains `..` after lexical normalization is rejected.
fn resolve_scoped_path(path: &str) -> Option<PathBuf> {
let expanded = bread_shared::expand_path(path);
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
.iter()
.filter(|p| p.kind == PermissionKind::Exec)
@ -537,16 +613,109 @@ fn bin_allowed(permissions: &[ModulePermission], cmd: &str) -> bool {
if hints.is_empty() {
return true;
}
let first_word = cmd.split_whitespace().next().unwrap_or("");
let cmd_leaf = std::path::Path::new(first_word)
let cmd_leaf = Path::new(program)
.file_name()
.and_then(|f| f.to_str())
.unwrap_or(first_word);
.unwrap_or(program);
hints.iter().any(|hint| {
let hint_leaf = std::path::Path::new(hint.as_str())
let hint_leaf = Path::new(hint.as_str())
.file_name()
.and_then(|f| f.to_str())
.unwrap_or(hint.as_str());
cmd_leaf == hint_leaf || first_word == hint.as_str()
cmd_leaf == hint_leaf || program == 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,11 +137,9 @@ enabled = false
async fn wait_until_ready(&self) -> Result<()> {
let deadline = Instant::now() + Duration::from_secs(8);
while Instant::now() < deadline {
if self.socket_path.exists() {
if self.send_request("ping", json!({})).await.is_ok() {
if self.socket_path.exists() && self.send_request("ping", json!({})).await.is_ok() {
return Ok(());
}
}
sleep(Duration::from_millis(100)).await;
}
Err(anyhow!("daemon did not become ready in time"))

View file

@ -37,6 +37,7 @@ 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 |
| `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 |
| `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 |
| `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 |

View file

@ -0,0 +1,228 @@
-- 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": "lua/mod.rs",
"0": "LuaEngine",
"1": "RawEvent",
"2": "config.rs",
"3": "Server",
@ -15,24 +15,24 @@
"13": "bread-cli/src/main.rs",
"14": "types.rs",
"15": "podman.rs",
"16": "StateHandle",
"17": "run_udev_monitor",
"16": "Value",
"17": "Result",
"18": "hooks_shell.rs",
"19": "bluetooth.rs",
"20": "SubscriptionId",
"21": "glob.rs",
"22": "run_state_engine",
"23": "RtnetlinkAdapter",
"22": "StateHandle",
"23": "Adapter",
"24": "network.rs",
"25": "power.rs",
"26": "hyprland.rs",
"27": "Adapter",
"28": "dl.breadway.dev Distribution",
"27": "parse_upower_message",
"28": "main Branch",
"29": "git-branch-widget.lua",
"30": "TestHarness",
"31": "Sync",
"32": "active-window-widget.lua",
"33": "cpu-temp-widget.lua",
"33": ".new",
"34": "focus-mode-widget.lua",
"35": "workflow-status-widget.lua",
"36": "bread.widget API",
@ -59,10 +59,10 @@
"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",
"60": "lua/mod.rs",
"61": "external-monitors.lua",
"62": "AGENTS.md — Repo hygiene",
"63": "CLAUDE.md — Repo hygiene",
"64": "ModuleHostLua",
"65": "ModuleHostRegistry",
"66": "xtask/src/main.rs",
@ -86,5 +86,10 @@
"84": "State",
"85": "init.lua",
"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

@ -0,0 +1 @@
{"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

@ -0,0 +1,90 @@
{
"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

@ -0,0 +1,397 @@
# 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

@ -0,0 +1,18 @@
{
"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

@ -0,0 +1,327 @@
{
"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-15)
# Graph Report - bread (2026-08-16)
## Corpus Check
- 62 files · ~88,250 words
- 66 files · ~90,429 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)
- 1486 nodes · 3384 edges · 93 communities (68 shown, 25 thin omitted)
- Extraction: 99% EXTRACTED · 1% INFERRED · 0% AMBIGUOUS · INFERRED: 47 edges (avg confidence: 0.78)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `a6973360`
- Built from commit: `cdd5de8f`
- 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
- LuaEngine
- RawEvent
- config.rs
- Server
@ -31,24 +31,24 @@
- bread-cli/src/main.rs
- types.rs
- podman.rs
- StateHandle
- run_udev_monitor
- Value
- Result
- hooks_shell.rs
- bluetooth.rs
- SubscriptionId
- glob.rs
- run_state_engine
- RtnetlinkAdapter
- StateHandle
- Adapter
- network.rs
- power.rs
- hyprland.rs
- Adapter
- dl.breadway.dev Distribution
- parse_upower_message
- main Branch
- git-branch-widget.lua
- TestHarness
- Sync
- active-window-widget.lua
- cpu-temp-widget.lua
- .new
- focus-mode-widget.lua
- workflow-status-widget.lua
- bread.widget API
@ -70,10 +70,10 @@
- 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
- lua/mod.rs
- external-monitors.lua
- AGENTS.md — Repo hygiene
- CLAUDE.md — Repo hygiene
- ModuleHostLua
- ModuleHostRegistry
- xtask/src/main.rs
@ -98,15 +98,20 @@
- init.lua
- Execution
- packaging/README.md
- PathBuf
- build.sh
- beta Release Track
- dev Release Track
- stable Release Track
## God Nodes (most connected - your core abstractions)
1. `LuaEngine` - 59 edges
2. `RawEvent` - 49 edges
2. `RawEvent` - 50 edges
3. `BreadEvent` - 38 edges
4. `raw()` - 37 edges
5. `RuntimeState` - 34 edges
5. `RuntimeState` - 36 edges
6. `StateHandle` - 28 edges
7. `now_unix_ms()` - 26 edges
7. `now_unix_ms()` - 25 edges
8. `Adapter` - 25 edges
9. `SubscriptionId` - 25 edges
10. `ModuleHostLua` - 24 edges
@ -118,31 +123,29 @@
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
- `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]
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)
## Communities (93 total, 25 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 0 - "LuaEngine"
Cohesion: 0.11
Nodes (19): ErrorEntry, HandlerEntry, HandlerKind, LuaEngine, LuaMessage, ModuleInfo, AtomicU64, HashMap (+11 more)
### Community 1 - "RawEvent"
Cohesion: 0.05
Cohesion: 0.06
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"
@ -155,11 +158,11 @@ Nodes (41): A, command_target(), event_domain(), is_known_app(), is_reserved_dom
### 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)
Nodes (33): accepts_node_count_at_max(), accepts_tree_at_max_depth(), Align, Background, box_of(), default_orientation(), FontWeight, is_valid_class() (+25 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)
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)
### Community 6 - "filesystem.rs"
Cohesion: 0.12
@ -175,7 +178,7 @@ Nodes (50): daemon_survives_repeated_reloads_and_pipeline_resumes(), emit_with_a
### 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)
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)
### Community 10 - "modules_mgmt.rs"
Cohesion: 0.11
@ -194,20 +197,20 @@ 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)
Cohesion: 0.15
Nodes (21): DeviceTopology, InterfaceState, MatchCondition, ModuleStatus, Monitor, NetworkState, PowerState, ProfileState (+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 16 - "Value"
Cohesion: 0.17
Nodes (14): active_window_from_data(), apply_hyprland_snapshot(), condition_matches(), hyprland_state_key(), json_stringish(), resolve_device(), Option, Result (+6 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 17 - "Result"
Cohesion: 0.13
Nodes (26): bluetooth_connect(), bluetooth_disconnect(), bluetooth_find_adapter(), bluetooth_get_powered(), bluetooth_query(), bluetooth_set_powered(), bluetooth_set_scanning(), bluetooth_spawn() (+18 more)
### Community 18 - "hooks_shell.rs"
Cohesion: 0.18
@ -221,13 +224,13 @@ Nodes (10): address_from_path(), BluetoothAdapter, parse_bluetooth_message(), Me
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 22 - "StateHandle"
Cohesion: 0.15
Nodes (16): dispatch_event(), handle_command(), Arc, AtomicU64, HashMap, Receiver, RwLock, Self (+8 more)
### Community 23 - "RtnetlinkAdapter"
Cohesion: 0.22
Nodes (7): ip_from_bytes(), Option, Result, Self, Sender, String, RtnetlinkAdapter
### Community 23 - "Adapter"
Cohesion: 0.19
Nodes (9): Adapter, ip_from_bytes(), Option, Result, Self, Sender, String, RtnetlinkAdapter (+1 more)
### Community 24 - "network.rs"
Cohesion: 0.27
@ -239,15 +242,11 @@ Nodes (8): power_raw_event(), PowerAdapter, PowerSnapshot, read_power_state(), O
### Community 26 - "hyprland.rs"
Cohesion: 0.29
Nodes (7): hyprland_event_socket(), HyprlandAdapter, parse_hyprland_line(), PathBuf, Result, Sender, String
Nodes (11): emit_topology_snapshot(), hyprland_event_socket(), hyprland_request_json(), hyprland_request_socket(), HyprlandAdapter, parse_hyprland_line(), PathBuf, Result (+3 more)
### 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 27 - "parse_upower_message"
Cohesion: 0.27
Nodes (6): parse_upower_message(), Message, Result, Self, Sender, UPowerAdapter
### Community 29 - "git-branch-widget.lua"
Cohesion: 0.62
@ -265,9 +264,9 @@ Nodes (3): main(), Result, Sync
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 33 - ".new"
Cohesion: 0.16
Nodes (15): ModulePermission, Option, String, bluetooth_list_devices(), builtin_module_decls(), is_lib_path(), list_lua_files(), module_name_from_path() (+7 more)
### Community 34 - "focus-mode-widget.lua"
Cohesion: 1.00
@ -281,8 +280,24 @@ Nodes (3): M.on_load(), most_relevant(), widget_update()
Cohesion: 0.67
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"
Cohesion: 0.10
Cohesion: 0.09
Nodes (38): call(), HostMessage, IoCommand, RpcResponse, Duration, Option, PathBuf, Receiver (+30 more)
### Community 65 - "ModuleHostRegistry"
@ -310,8 +325,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)
### 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)
Cohesion: 0.18
Nodes (18): build_device_event(), build_event(), enumerate_payload_includes_classification_fields(), prop_bool(), prop_str(), Option, Result, Self (+10 more)
### Community 72 - "Dictionary: Lua API"
Cohesion: 0.17
@ -373,25 +388,29 @@ Nodes (3): M.on_load(), read_temp_c(), widget_root()
Cohesion: 0.67
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
- **179 isolated node(s):** `install.sh script`, `Branches`, `The release cycle`, `Tracks, from a user's perspective`, `Keeping the API docs honest` (+174 more)
- **182 isolated node(s):** `build.sh script`, `install.sh script`, `Remotes`, `CI`, `Local architecture (still true)` (+177 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.
- **25 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._
- **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`?**
_High betweenness centrality (0.089) - 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`?**
_High betweenness centrality (0.084) - this node is a cross-community bridge._
- **Why does `BreadEvent` connect `RawEvent` to `ModuleHostLua`, `LuaEngine`, `.new`, `Server`, `ModuleHostRegistry`, `state_engine.rs`, `StateHandle`?**
_High betweenness centrality (0.061) - this node is a cross-community bridge._
- **What connects `build.sh script`, `install.sh script`, `Remotes` to the rest of the system?**
_182 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `LuaEngine` be split into smaller, more focused modules?**
_Cohesion score 0.11153846153846154 - 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._
_Cohesion score 0.05702970297029703 - 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._
_Cohesion score 0.0670122176971492 - nodes in this community are weakly interconnected._

View file

@ -0,0 +1 @@
{"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

@ -0,0 +1 @@
{"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

@ -0,0 +1 @@
{"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

@ -0,0 +1 @@
{"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

@ -0,0 +1 @@
{"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 @@
1785892770.494006
1786850688.401695

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