Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.15.9"
version = "0.15.10"
edition = "2021"
license = "MIT"
authors = ["TerminallyLazy"]
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ Command ownership is Rust-native:
normal sensitivity only, bounded cambium/scar/seed classifications, and
required checkpoint identity, idempotency, and provenance.
- `evidence` is the Revolve-inspired improvement-loop entry point for evaluated outcomes.
- `dox sync` and `revolve sync` are read-only source adapters that summarize and point back to authoritative files.
- `dox sync` and `revolve sync` read source files without changing them and save source-linked summaries to the memory store. Use `--dry-run` to preview without saving.
- `integrations scan` discovers nearby agent-framework markers and suggests setup paths without changing their config.
- `export`, `import`, `audit`, `consolidate`, and `maintain` are local maintenance surfaces over the same SQLite store.
- `policy` manages optional coordinated multi-agent write authorization and its
Expand Down Expand Up @@ -669,12 +669,27 @@ Store-watch polling updates persisted counts from SQLite, while the optional
event stream lights rings in real time without treating stream events as durable
truth.

The dashboard counts memories saved in the selected project's store. A successful
harness recall can return zero results from an empty store; its activation receipt
does not create a memory. Automatic capture adds only durable outcomes selected by
an agent working in that project.

`/sync` previews DOX summaries from the project's `AGENTS.md` files, including
local instruction files that may not be tracked in Git. Review the source paths,
project, destination store and candidates before confirming. Confirmation saves
that preview and refreshes the dashboard; cancellation leaves memories unchanged.
Repeated sync updates the same source-linked records rather than duplicating them.
In Coordinated mode, saving requires the coordinator capability in the terminal
environment when the TUI starts; preview remains available without it.

Useful keys and commands:

- `s` focuses search, `/` opens the slash command palette, `r` opens exploded
ring view, `q` quits.
- `i` toggles sensitive-memory visibility, `u` toggles superseded-memory
visibility.
- In the `/sync` preview, `j`/`k` select candidates, Left/Right or Page Up/Down
scroll the preview, `y` saves, and `n` or Escape cancels.
- Slash commands include `/rings`, `/search <query>`, `/remember <summary>`,
`/forget`, `/redact`, `/promote`, `/scar`, `/seed`, `/supersede <old_id>`,
`/consolidate`, `/export <file>`, `/sync`, `/integrations`, `/stream`, and
Expand Down
15 changes: 12 additions & 3 deletions crates/tree-ring-memory-cli/src/actions/adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,25 @@ pub fn sync_dox(
let store = store.ok_or_else(|| {
"DOX sync action requires an open writable store when dry_run=false".to_string()
})?;
store
.put_many(&report.events)
.map_err(|err| err.to_string())?;
apply_dox_preview(store, &report)?;
}
Ok(DoxSyncActionReport {
report,
dry_run: request.dry_run,
})
}

/// Persist exactly the candidates returned by a reviewed DOX dry run. Store
/// validation, atomic batch writes, and coordinated policy still apply.
pub fn apply_dox_preview(
store: &mut SQLiteMemoryStore,
report: &DoxSyncReport,
) -> ActionResult<()> {
store
.put_many(&report.events)
.map_err(|err| err.to_string())
}

pub fn sync_revolve(
store: Option<&mut SQLiteMemoryStore>,
request: RevolveSyncActionRequest,
Expand Down
41 changes: 36 additions & 5 deletions crates/tree-ring-memory-cli/src/actions/integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ pub fn status(request: IntegrationStatusRequest) -> Result<IntegrationStatusActi
} else if stale_adapter && detected.id != "agent-zero" {
"The installed adapter definition is out of date. Review the managed hook files and activation manifest, then reconfigure them with this CLI; preserve the memory database.".to_string()
} else {
next_step_for_state(state, &detected.next_step)
next_step_for_state(state, &detected.name, &detected.next_step)
};
IntegrationStatusEntry {
id: detected.id,
Expand Down Expand Up @@ -293,12 +293,39 @@ pub fn activate(
detection.plan,
request.accept_managed_block,
)?;
// Publication success describes the bridge, not whether scoped recall has
// already run. Reuse status verification without creating a new receipt,
// and never let historical proof hide a blocked publication or trust step.
let (state, next_step) = if result.state == ActivationState::ConfiguredAwaitingProof {
let state = manifest
.harnesses
.get(&request.harness_id)
.and_then(|harness| {
let verification = verify_activation_receipts(
&request.memory_root,
&request.harness_id,
&manifest,
harness,
);
(verification.status == ReceiptVerificationStatus::Valid)
.then_some(verification.receipt)
.flatten()
})
.map(|receipt| receipt.state)
.unwrap_or(result.state);
(
state,
next_step_for_state(state, &detection.name, &result.next_step),
)
} else {
(result.state, result.next_step)
};
Ok(IntegrationLifecycleActionReport {
harness_id: request.harness_id,
state: result.state,
state,
changed_paths: relative_paths(result.changed_paths)?,
dry_run: false,
next_step: result.next_step,
next_step,
})
}

Expand Down Expand Up @@ -717,15 +744,19 @@ fn invalid_receipt(
}
}

fn next_step_for_state(state: ActivationState, detected_next_step: &str) -> String {
fn next_step_for_state(
state: ActivationState,
harness_name: &str,
detected_next_step: &str,
) -> String {
match state {
ActivationState::Active => "No action required for the receipt-backed session.".to_string(),
ActivationState::ActiveIsolated => {
"Bind the harness to this project's canonical store before claiming shared use."
.to_string()
}
ActivationState::ConfiguredAwaitingProof => {
"Run the adapter preflight at the start of a new harness session.".to_string()
format!("Open a new {harness_name} session in this project, then run `tree-ring integrations status --verbose` to verify lifecycle recall.")
}
_ => detected_next_step.to_string(),
}
Expand Down
43 changes: 34 additions & 9 deletions crates/tree-ring-memory-cli/src/tui/actions.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::path::PathBuf;
use std::{cell::Cell, path::PathBuf};

use tree_ring_memory_core::ConsolidationRequest;
use tree_ring_memory_core::{ConsolidationRequest, DoxSyncReport};

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq)]
pub enum ActionKind {
Delete,
Redact,
Expand All @@ -22,13 +22,17 @@ pub enum ActionKind {
include_sensitive: bool,
include_superseded: bool,
},
Sync,
SyncDox {
preview: Box<DoxSyncReport>,
selected_candidate: usize,
preview_scroll: Cell<u16>,
},
RefreshCertification {
command: String,
},
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq)]
pub struct PendingAction {
pub kind: ActionKind,
pub memory_id: Option<String>,
Expand Down Expand Up @@ -111,11 +115,31 @@ impl PendingAction {
}
}

pub fn sync_placeholder() -> Self {
pub fn sync_dox(preview: DoxSyncReport, project: &str, store_path: &std::path::Path) -> Self {
let sensitive_count = preview
.events
.iter()
.filter(|event| event.sensitivity != "normal")
.count();
let summary = format!(
"Import or update {} DOX summaries from {} AGENTS.md files.\nSource: {}\nStore: {}\nProject: {}\nIncludes {} sensitive; skips {} secret sections; {} warnings.\nStable IDs update existing summaries; source files stay authoritative.",
preview.memory_count,
preview.source_count,
preview.root.display(),
store_path.display(),
project,
sensitive_count,
preview.skipped_secret_count,
preview.warnings.len(),
Comment on lines +131 to +133

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Sync understates discarded guidance 🐞 Bug ≡ Correctness

PendingAction::sync_dox labels skipped_secret_count as a number of secret sections even though
the collector increments it once for an entirely discarded file. When any section trips the secret
guard, events_from_agents_file abandons every otherwise eligible candidate from that AGENTS.md,
so the preview and empty-state status describe a narrower omission than occurred.
Agent Prompt
## Issue description
The TUI describes the file-level `skipped_secret_count` as skipped sections, concealing that all guidance from each affected `AGENTS.md` was omitted.

## Fix Focus Areas
- crates/tree-ring-memory-cli/src/tui/actions.rs[124-133]
- crates/tree-ring-memory-cli/src/tui/app.rs[576-583]
- README.md[677-683]

## Recommended Fix
Change the confirmation, empty-state, and documentation wording to identify affected `AGENTS.md` files rather than sections, preferably stating that files containing detected secret content are skipped entirely. Add a test with one ordinary and one secret section in the same file to lock the displayed semantics to the collector's behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

);
Self {
kind: ActionKind::Sync,
kind: ActionKind::SyncDox {
preview: Box::new(preview),
selected_candidate: 0,
preview_scroll: Cell::new(0),
},
memory_id: None,
summary: "Run integration sync".to_string(),
summary,
}
}

Expand All @@ -129,8 +153,9 @@ impl PendingAction {
}
}

#[cfg(test)]
pub fn confirmation_prompt(&self) -> String {
format!("{} - press y to confirm, n/Esc to cancel", self.summary)
format!("{}\npress y to confirm, n/Esc to cancel", self.summary)
}
}

Expand Down
Loading