use serde::Serialize;
use crate::core::swarm_brief::WorkspaceGitSnapshot;
pub const BEADS_HYGIENE_STATE_SCHEMA_V1: &str = "ee.beads_hygiene_state.v1";
pub const BEADS_JSONL_RELATIVE_PATH: &str = ".beads/issues.jsonl";
pub const BEADS_JSONL_MAX_INSPECT_BYTES: usize = 8 * 1024 * 1024;
pub mod degraded {
pub const JSONL_INSPECTION_TRUNCATED: &str = "workspace_hygiene_beads_jsonl_truncated";
pub const CONTENT_NOT_PROVIDED: &str = "workspace_hygiene_beads_content_not_provided";
pub const PARSE_ERROR: &str = "workspace_hygiene_beads_parse_error";
pub const DB_DIVERGENCE_UNKNOWN: &str = "workspace_hygiene_beads_db_divergence_unknown";
pub const SELF_RESERVATION_OBSERVED: &str = "workspace_hygiene_beads_self_reservation";
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum BeadsClassification {
BeadsClean,
BeadsExportOnly,
BeadsDbDirtyPendingFlush,
BeadsExternalChangesPendingImport,
BeadsConflictOrParseError,
BeadsReservedByOtherAgent,
BeadsLikelyCommitReady,
}
impl BeadsClassification {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::BeadsClean => "beads_clean",
Self::BeadsExportOnly => "beads_export_only",
Self::BeadsDbDirtyPendingFlush => "beads_db_dirty_pending_flush",
Self::BeadsExternalChangesPendingImport => "beads_external_changes_pending_import",
Self::BeadsConflictOrParseError => "beads_conflict_or_parse_error",
Self::BeadsReservedByOtherAgent => "beads_reserved_by_other_agent",
Self::BeadsLikelyCommitReady => "beads_likely_commit_ready",
}
}
#[must_use]
pub const fn safety_rank(self) -> u8 {
match self {
Self::BeadsReservedByOtherAgent => 0,
Self::BeadsConflictOrParseError => 1,
Self::BeadsDbDirtyPendingFlush => 2,
Self::BeadsExternalChangesPendingImport => 3,
Self::BeadsExportOnly => 4,
Self::BeadsLikelyCommitReady => 5,
Self::BeadsClean => 6,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BeadsReservationHolder {
pub agent_name: String,
pub exclusive: bool,
pub expires_ts_rfc3339: String,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BeadsJsonlPosture {
pub present_in_dirty_set: bool,
pub staged_change: bool,
pub unstaged_change: bool,
pub untracked: bool,
pub entry_kind: Option<String>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum BeadsMetadataSignal {
Unknown,
ExportOnly,
DbDirtyPendingFlush,
ExternalChangesPendingImport,
LikelyCommitReady,
}
impl BeadsMetadataSignal {
#[must_use]
const fn classification(self, jsonl_is_dirty: bool) -> BeadsClassification {
match self {
Self::Unknown => {
if jsonl_is_dirty {
BeadsClassification::BeadsExportOnly
} else {
BeadsClassification::BeadsClean
}
}
Self::ExportOnly => BeadsClassification::BeadsExportOnly,
Self::DbDirtyPendingFlush => BeadsClassification::BeadsDbDirtyPendingFlush,
Self::ExternalChangesPendingImport => {
BeadsClassification::BeadsExternalChangesPendingImport
}
Self::LikelyCommitReady => BeadsClassification::BeadsLikelyCommitReady,
}
}
#[must_use]
const fn needs_unknown_degraded_code(self, jsonl_is_dirty: bool) -> bool {
matches!(self, Self::Unknown) && jsonl_is_dirty
}
}
impl BeadsJsonlPosture {
fn from_snapshot(snapshot: &WorkspaceGitSnapshot) -> Self {
let entry = snapshot
.entries
.iter()
.find(|entry| entry.path == BEADS_JSONL_RELATIVE_PATH);
match entry {
None => Self {
present_in_dirty_set: false,
staged_change: false,
unstaged_change: false,
untracked: false,
entry_kind: None,
},
Some(entry) => Self {
present_in_dirty_set: true,
staged_change: is_significant_status_char(&entry.staged),
unstaged_change: is_significant_status_char(&entry.unstaged),
untracked: entry.entry_kind == "untracked",
entry_kind: Some(entry.entry_kind.clone()),
},
}
}
}
fn is_significant_status_char(value: &str) -> bool {
let mut chars = value.chars();
match chars.next() {
None => false,
Some('.') | Some(' ') | Some('?') => false,
Some(_) => true,
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BeadsHygieneState {
pub schema: &'static str,
pub classification: BeadsClassification,
pub jsonl_posture: BeadsJsonlPosture,
pub metadata_signal: BeadsMetadataSignal,
pub conflict_markers_found: bool,
pub parse_error_line: Option<usize>,
pub reservation_holders: Vec<BeadsReservationHolder>,
pub degraded_codes: Vec<&'static str>,
}
#[derive(Clone, Copy, Debug)]
pub struct BeadsHygieneInputs<'a> {
pub snapshot: &'a WorkspaceGitSnapshot,
pub jsonl_content: Option<&'a [u8]>,
pub self_agent_name: Option<&'a str>,
pub metadata_signal: BeadsMetadataSignal,
pub reservations: &'a [BeadsReservationHolder],
}
#[must_use]
pub fn classify_beads_state(inputs: BeadsHygieneInputs<'_>) -> BeadsHygieneState {
let jsonl_posture = BeadsJsonlPosture::from_snapshot(inputs.snapshot);
let mut other_agent_reservations: Vec<BeadsReservationHolder> = Vec::new();
let mut saw_self_reservation = false;
for holder in inputs.reservations {
if Some(holder.agent_name.as_str()) == inputs.self_agent_name {
saw_self_reservation = true;
continue;
}
if holder.exclusive {
other_agent_reservations.push(holder.clone());
}
}
other_agent_reservations.sort_by(|left, right| left.agent_name.cmp(&right.agent_name));
let jsonl_is_dirty = jsonl_posture.present_in_dirty_set;
let (conflict_markers_found, parse_error_line, mut content_degraded) = if jsonl_is_dirty {
scan_jsonl_content(inputs.jsonl_content)
} else {
(false, None, Vec::new())
};
if !other_agent_reservations.is_empty() {
return BeadsHygieneState {
schema: BEADS_HYGIENE_STATE_SCHEMA_V1,
classification: BeadsClassification::BeadsReservedByOtherAgent,
jsonl_posture,
metadata_signal: inputs.metadata_signal,
conflict_markers_found,
parse_error_line,
reservation_holders: other_agent_reservations,
degraded_codes: maybe_with_self_reservation_code(
content_degraded,
saw_self_reservation,
),
};
}
if conflict_markers_found || parse_error_line.is_some() {
return BeadsHygieneState {
schema: BEADS_HYGIENE_STATE_SCHEMA_V1,
classification: BeadsClassification::BeadsConflictOrParseError,
jsonl_posture,
metadata_signal: inputs.metadata_signal,
conflict_markers_found,
parse_error_line,
reservation_holders: other_agent_reservations,
degraded_codes: maybe_with_self_reservation_code(
content_degraded,
saw_self_reservation,
),
};
}
if inputs
.metadata_signal
.needs_unknown_degraded_code(jsonl_is_dirty)
{
content_degraded.push(degraded::DB_DIVERGENCE_UNKNOWN);
}
content_degraded.sort_unstable();
content_degraded.dedup();
BeadsHygieneState {
schema: BEADS_HYGIENE_STATE_SCHEMA_V1,
classification: inputs.metadata_signal.classification(jsonl_is_dirty),
jsonl_posture,
metadata_signal: inputs.metadata_signal,
conflict_markers_found,
parse_error_line,
reservation_holders: other_agent_reservations,
degraded_codes: maybe_with_self_reservation_code(content_degraded, saw_self_reservation),
}
}
fn maybe_with_self_reservation_code(
mut codes: Vec<&'static str>,
saw_self: bool,
) -> Vec<&'static str> {
if saw_self {
codes.push(degraded::SELF_RESERVATION_OBSERVED);
codes.sort_unstable();
codes.dedup();
}
codes
}
fn scan_jsonl_content(content: Option<&[u8]>) -> (bool, Option<usize>, Vec<&'static str>) {
let mut degraded = Vec::new();
let raw = match content {
Some(bytes) => bytes,
None => {
degraded.push(degraded::CONTENT_NOT_PROVIDED);
return (false, None, finalized_degraded_codes(degraded));
}
};
let mut bytes = raw;
let truncated = bytes.len() > BEADS_JSONL_MAX_INSPECT_BYTES;
if truncated {
bytes = &bytes[..BEADS_JSONL_MAX_INSPECT_BYTES];
degraded.push(degraded::JSONL_INSPECTION_TRUNCATED);
}
let text = match std::str::from_utf8(bytes) {
Ok(text) => text,
Err(error) if truncated && error.error_len().is_none() => {
std::str::from_utf8(&bytes[..error.valid_up_to()]).unwrap_or_default()
}
Err(_) => {
degraded.push(degraded::PARSE_ERROR);
return (false, Some(0), finalized_degraded_codes(degraded));
}
};
let text = if truncated {
complete_jsonl_record_prefix(text)
} else {
text
};
let mut conflict_markers_found = false;
let mut parse_error_line: Option<usize> = None;
for (index, line) in text.lines().enumerate() {
let line_number = index + 1;
if line.starts_with("<<<<<<<") || line.starts_with("=======") || line.starts_with(">>>>>>>")
{
conflict_markers_found = true;
continue;
}
if line.is_empty() {
continue;
}
if parse_error_line.is_some() {
continue;
}
if !looks_like_json_object(line) {
parse_error_line = Some(line_number);
}
}
if conflict_markers_found || parse_error_line.is_some() {
degraded.push(degraded::PARSE_ERROR);
}
(
conflict_markers_found,
parse_error_line,
finalized_degraded_codes(degraded),
)
}
fn complete_jsonl_record_prefix(text: &str) -> &str {
if text.is_empty() || text.ends_with('\n') {
return text;
}
match text.rfind('\n') {
Some(index) => &text[..=index],
None => "",
}
}
fn finalized_degraded_codes(mut degraded: Vec<&'static str>) -> Vec<&'static str> {
degraded.sort_unstable();
degraded.dedup();
degraded
}
fn looks_like_json_object(line: &str) -> bool {
let trimmed = line.trim();
match serde_json::from_str::<serde_json::Value>(trimmed) {
Ok(serde_json::Value::Object(_)) => true,
Ok(_) | Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::swarm_brief::{WorkspaceGitOperationState, WorkspaceGitStatusEntry};
fn entry(
path: &str,
staged: &str,
unstaged: &str,
entry_kind: &str,
) -> WorkspaceGitStatusEntry {
WorkspaceGitStatusEntry {
path: path.to_owned(),
original_path: None,
staged: staged.to_owned(),
unstaged: unstaged.to_owned(),
entry_kind: entry_kind.to_owned(),
submodule_state: None,
metadata: None,
}
}
fn snapshot(entries: Vec<WorkspaceGitStatusEntry>) -> WorkspaceGitSnapshot {
WorkspaceGitSnapshot {
repository_root: "/tmp/beads-repo".to_owned(),
entries,
operation_state: WorkspaceGitOperationState::default(),
}
}
fn reservation(agent: &str, exclusive: bool) -> BeadsReservationHolder {
BeadsReservationHolder {
agent_name: agent.to_owned(),
exclusive,
expires_ts_rfc3339: "2026-05-17T10:00:00Z".to_owned(),
}
}
fn well_formed_jsonl() -> &'static [u8] {
b"{\"id\":\"bd-foo\",\"title\":\"x\"}\n{\"id\":\"bd-bar\",\"title\":\"y\"}\n"
}
#[test]
fn clean_workspace_classifies_as_beads_clean() {
let snap = snapshot(Vec::new());
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: None,
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(state.classification, BeadsClassification::BeadsClean);
assert!(!state.jsonl_posture.present_in_dirty_set);
assert!(state.reservation_holders.is_empty());
assert!(!state.conflict_markers_found);
assert!(state.parse_error_line.is_none());
}
#[test]
fn dirty_jsonl_without_other_reservation_or_conflict_is_export_only_with_db_divergence_unknown()
{
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, "M", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(well_formed_jsonl()),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(state.classification, BeadsClassification::BeadsExportOnly);
assert!(state.jsonl_posture.present_in_dirty_set);
assert!(state.jsonl_posture.staged_change);
assert!(state.jsonl_posture.unstaged_change);
assert!(
state
.degraded_codes
.contains(°raded::DB_DIVERGENCE_UNKNOWN),
"missing db_divergence_unknown in {:?}",
state.degraded_codes
);
assert!(!state.conflict_markers_found);
assert!(state.parse_error_line.is_none());
}
#[test]
fn untracked_jsonl_is_export_only() {
let snap = snapshot(vec![entry(
BEADS_JSONL_RELATIVE_PATH,
"?",
"?",
"untracked",
)]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(well_formed_jsonl()),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(state.classification, BeadsClassification::BeadsExportOnly);
assert!(state.jsonl_posture.untracked);
assert!(!state.jsonl_posture.staged_change);
assert!(!state.jsonl_posture.unstaged_change);
}
#[test]
fn db_dirty_signal_classifies_as_pending_flush_even_without_jsonl_dirty() {
let snap = snapshot(Vec::new());
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: None,
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::DbDirtyPendingFlush,
reservations: &[],
});
assert_eq!(
state.classification,
BeadsClassification::BeadsDbDirtyPendingFlush
);
assert!(!state.jsonl_posture.present_in_dirty_set);
assert_eq!(
state.metadata_signal,
BeadsMetadataSignal::DbDirtyPendingFlush
);
assert!(state.degraded_codes.is_empty());
}
#[test]
fn external_import_pending_signal_classifies_as_external_changes_pending_import() {
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(well_formed_jsonl()),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::ExternalChangesPendingImport,
reservations: &[],
});
assert_eq!(
state.classification,
BeadsClassification::BeadsExternalChangesPendingImport
);
assert_eq!(
state.metadata_signal,
BeadsMetadataSignal::ExternalChangesPendingImport
);
assert!(
!state
.degraded_codes
.contains(°raded::DB_DIVERGENCE_UNKNOWN)
);
}
#[test]
fn clean_metadata_signal_with_dirty_jsonl_classifies_as_likely_commit_ready() {
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, "M", ".", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(well_formed_jsonl()),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::LikelyCommitReady,
reservations: &[],
});
assert_eq!(
state.classification,
BeadsClassification::BeadsLikelyCommitReady
);
assert!(state.jsonl_posture.staged_change);
assert!(
!state
.degraded_codes
.contains(°raded::DB_DIVERGENCE_UNKNOWN)
);
}
#[test]
fn conflict_markers_classify_as_conflict_or_parse_error() {
let body = b"{\"id\":\"bd-a\"}\n<<<<<<< HEAD\n{\"id\":\"bd-b\"}\n=======\n{\"id\":\"bd-c\"}\n>>>>>>> theirs\n";
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(body),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(
state.classification,
BeadsClassification::BeadsConflictOrParseError
);
assert!(state.conflict_markers_found);
assert!(state.degraded_codes.contains(°raded::PARSE_ERROR));
}
#[test]
fn invalid_jsonl_line_classifies_as_conflict_or_parse_error_with_line_number() {
let body = b"{\"id\":\"bd-a\"}\n{not valid json\n{\"id\":\"bd-c\"}\n";
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(body),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(
state.classification,
BeadsClassification::BeadsConflictOrParseError
);
assert_eq!(state.parse_error_line, Some(2));
assert!(!state.conflict_markers_found);
assert!(state.degraded_codes.contains(°raded::PARSE_ERROR));
}
#[test]
fn exclusive_other_agent_reservation_overrides_export_only() {
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let holder = reservation("LavenderPeak", true);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(well_formed_jsonl()),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: std::slice::from_ref(&holder),
});
assert_eq!(
state.classification,
BeadsClassification::BeadsReservedByOtherAgent
);
assert_eq!(state.reservation_holders, vec![holder]);
}
#[test]
fn self_agent_reservation_does_not_trigger_reserved_by_other_and_records_diagnostic() {
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let self_hold = reservation("TopazSpring", true);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(well_formed_jsonl()),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: std::slice::from_ref(&self_hold),
});
assert_eq!(state.classification, BeadsClassification::BeadsExportOnly);
assert!(state.reservation_holders.is_empty());
assert!(
state
.degraded_codes
.contains(°raded::SELF_RESERVATION_OBSERVED)
);
}
#[test]
fn non_exclusive_other_agent_reservation_does_not_block() {
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let observer = reservation("LavenderPeak", false);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(well_formed_jsonl()),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: std::slice::from_ref(&observer),
});
assert_eq!(state.classification, BeadsClassification::BeadsExportOnly);
assert!(state.reservation_holders.is_empty());
}
#[test]
fn reservation_takes_priority_over_conflict_markers() {
let body = b"<<<<<<< HEAD\nbroken\n>>>>>>> theirs\n";
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let holder = reservation("LavenderPeak", true);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(body),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: std::slice::from_ref(&holder),
});
assert_eq!(
state.classification,
BeadsClassification::BeadsReservedByOtherAgent
);
assert!(state.conflict_markers_found);
}
#[test]
fn missing_jsonl_content_records_degraded_code_and_falls_back_to_export_only() {
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: None,
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(state.classification, BeadsClassification::BeadsExportOnly);
assert!(
state
.degraded_codes
.contains(°raded::CONTENT_NOT_PROVIDED)
);
}
#[test]
fn oversized_jsonl_content_records_truncation_degraded_code() {
let mut body = Vec::with_capacity(BEADS_JSONL_MAX_INSPECT_BYTES + 1024);
body.extend_from_slice(well_formed_jsonl());
body.resize(BEADS_JSONL_MAX_INSPECT_BYTES + 1024, b'\n');
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(&body),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert!(
state
.degraded_codes
.contains(°raded::JSONL_INSPECTION_TRUNCATED)
);
}
#[test]
fn truncated_jsonl_content_ignores_incomplete_trailing_record() {
let mut body = Vec::with_capacity(BEADS_JSONL_MAX_INSPECT_BYTES + 128);
body.extend_from_slice(b"{\"id\":\"bd-a\",\"title\":\"complete\"}\n");
body.extend_from_slice(b"{\"id\":\"bd-b\",\"title\":\"");
while body.len() <= BEADS_JSONL_MAX_INSPECT_BYTES {
body.push(b'a');
}
body.extend_from_slice(b"\"}\n");
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(&body),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(state.classification, BeadsClassification::BeadsExportOnly);
assert!(state.parse_error_line.is_none());
assert!(!state.degraded_codes.contains(°raded::PARSE_ERROR));
assert!(
state
.degraded_codes
.contains(°raded::JSONL_INSPECTION_TRUNCATED)
);
}
#[test]
fn truncated_jsonl_content_preserves_complete_record_parse_errors() {
let mut body = Vec::with_capacity(BEADS_JSONL_MAX_INSPECT_BYTES + 128);
body.extend_from_slice(b"{\"id\":\"bd-a\",\"title\":\"complete\"}\n");
body.extend_from_slice(b"{not valid json}\n");
body.extend_from_slice(b"{\"id\":\"bd-b\",\"title\":\"");
while body.len() <= BEADS_JSONL_MAX_INSPECT_BYTES {
body.push(b'a');
}
body.extend_from_slice(b"\"}\n");
let snap = snapshot(vec![entry(BEADS_JSONL_RELATIVE_PATH, ".", "M", "ordinary")]);
let state = classify_beads_state(BeadsHygieneInputs {
snapshot: &snap,
jsonl_content: Some(&body),
self_agent_name: Some("TopazSpring"),
metadata_signal: BeadsMetadataSignal::Unknown,
reservations: &[],
});
assert_eq!(
state.classification,
BeadsClassification::BeadsConflictOrParseError
);
assert_eq!(state.parse_error_line, Some(2));
assert!(state.degraded_codes.contains(°raded::PARSE_ERROR));
assert!(
state
.degraded_codes
.contains(°raded::JSONL_INSPECTION_TRUNCATED)
);
}
#[test]
fn classification_safety_rank_is_total_order_with_no_ties() {
let variants = [
BeadsClassification::BeadsReservedByOtherAgent,
BeadsClassification::BeadsConflictOrParseError,
BeadsClassification::BeadsDbDirtyPendingFlush,
BeadsClassification::BeadsExternalChangesPendingImport,
BeadsClassification::BeadsExportOnly,
BeadsClassification::BeadsLikelyCommitReady,
BeadsClassification::BeadsClean,
];
let mut ranks: Vec<u8> = variants.iter().map(|v| v.safety_rank()).collect();
ranks.sort_unstable();
ranks.dedup();
assert_eq!(ranks.len(), variants.len(), "safety_rank must be injective");
}
#[test]
fn purity_module_does_not_perform_io_or_shell_out() {
let source = include_str!("hygiene_beads_state.rs");
let prefixes = ["std::fs::", "std::process::", "tokio::process"];
let suffixes_fs = [
"write",
"create_dir",
"remove_file",
"rename",
"File::create",
];
let suffixes_proc = ["Command", "Child"];
for prefix in &prefixes {
let candidates: &[&str] = if prefix.contains("fs") {
&suffixes_fs
} else if prefix.contains("process") || prefix.contains("Command") {
&suffixes_proc
} else {
&[]
};
for suffix in candidates {
let combined = format!("{prefix}{suffix}");
assert!(
!source.contains(&combined),
"hygiene_beads_state.rs should not contain `{combined}` — module must remain pure"
);
}
}
}
#[test]
fn looks_like_json_object_accepts_balanced_object_with_nested_braces_and_strings() {
assert!(looks_like_json_object(
"{\"id\":\"bd-a\",\"data\":{\"nested\":[1,2,3]}}"
));
assert!(looks_like_json_object("{\"k\":\"with \\\"quotes\\\"\"}"));
assert!(looks_like_json_object("{}"));
}
#[test]
fn looks_like_json_object_rejects_unbalanced_or_non_object_lines() {
assert!(!looks_like_json_object("{not valid"));
assert!(!looks_like_json_object("{not valid}"));
assert!(!looks_like_json_object("plain text"));
assert!(!looks_like_json_object("[1,2,3]"));
assert!(!looks_like_json_object("{\"k\":\"v\""));
assert!(!looks_like_json_object("{\"k\":\"x}"));
}
}