Skip to content

Commit 32f6c1b

Browse files
solomonneascodex
andauthored
feat(graphtrail): deprecate direct MiseLedger adapter (#392)
Co-authored-by: Codex <codex@openai.com>
1 parent 9966436 commit 32f6c1b

7 files changed

Lines changed: 361 additions & 1 deletion

File tree

engines/code-graph/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Deprecated
11+
- The direct `miseledger` Cargo feature, `graphtrail context --evidence`, and `graphtrail links` are deprecated but remain functional. Use `brigade code sync .`, `brigade code context "rate limiting" --markdown`, `brigade code impact dispatch --json`, `brigade evidence crawl plan --target .`, `brigade evidence search "dispatch"`, `brigade evidence search "dispatch" --code-reference '{"schema":"brigade.code-reference.v1","repository":"escoffier-labs/brigade","revision":{"commit":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"file_path":"src/brigade/receipts_cmd.py","qualified_name":"brigade.receipts_cmd._metadata_with_delta","symbol_kind":"function","source_span":{"start_line":788,"line_count":3}}'`, and `brigade evidence doctor --target .` for structured composition. An exact code reference is matched before lexical fallback. Compatibility lasts through at least two minor GraphTrail releases or 90 days after the first GraphTrail release containing this deprecation, whichever is longer. Removal cannot occur before that policy is satisfied. The standalone release pipeline is unchanged.
12+
1013
## [0.4.0] - 2026-07-17
1114

1215
### Fixed

engines/code-graph/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,22 @@ cargo build --features codesearch --bin graphtrail-mcp
187187
cargo run --features miseledger -- links "dispatch" --json
188188
```
189189

190+
### MiseLedger adapter deprecation
191+
192+
The direct `miseledger` Cargo feature, `graphtrail context --evidence`, and `graphtrail links` are deprecated but remain functional. Use Brigade for structured composition instead:
193+
194+
```bash
195+
brigade code sync .
196+
brigade code context "rate limiting" --markdown
197+
brigade code impact dispatch --json
198+
brigade evidence crawl plan --target .
199+
brigade evidence search "dispatch"
200+
brigade evidence search "dispatch" --code-reference '{"schema":"brigade.code-reference.v1","repository":"escoffier-labs/brigade","revision":{"commit":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"file_path":"src/brigade/receipts_cmd.py","qualified_name":"brigade.receipts_cmd._metadata_with_delta","symbol_kind":"function","source_span":{"start_line":788,"line_count":3}}'
201+
brigade evidence doctor --target .
202+
```
203+
204+
An exact code reference is matched before lexical fallback. GraphTrail will keep the direct adapter functional through at least two minor GraphTrail releases or 90 days after the first GraphTrail release containing this deprecation, whichever is longer. Removal cannot occur before that compatibility policy is satisfied. The direct adapter keeps its existing default database resolution and read-only FTS behavior during the compatibility window.
205+
190206
When `codesearch` is enabled, GraphTrail also reads the shared Code Search index
191207
manifest from `CODE_INDEX_MANIFEST`, `XDG_DATA_HOME/code-index/manifest.json`, or
192208
`~/.local/share/code-index/manifest.json`. `CODE_SEARCH_URL` still wins when it is

engines/code-graph/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_MISELEDGER");
3+
4+
if std::env::var_os("CARGO_FEATURE_MISELEDGER").is_some() {
5+
println!(
6+
"cargo:warning=GraphTrail's direct MiseLedger adapter is deprecated. Use `brigade code sync`, `brigade code context`, `brigade code impact`, `brigade evidence crawl`, `brigade evidence search`, and `brigade evidence doctor` instead. The adapter remains functional for at least two minor GraphTrail releases or 90 days after the first GraphTrail release containing this deprecation, whichever is longer. It will not be removed before that compatibility policy is satisfied."
7+
);
8+
}
9+
}

engines/code-graph/src/adapters/miseledger.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ use anyhow::{Context, Result};
99
use rusqlite::{Connection, OpenFlags, params};
1010
use serde::Serialize;
1111

12+
/// Emitted once by each deprecated CLI command invocation, always on stderr.
13+
pub const DEPRECATION_WARNING: &str = "warning: GraphTrail's direct MiseLedger adapter is deprecated. Use `brigade code sync`, `brigade code context`, `brigade code impact`, `brigade evidence crawl`, `brigade evidence search`, and `brigade evidence doctor` instead. The adapter remains functional for at least two minor GraphTrail releases or 90 days after the first GraphTrail release containing this deprecation, whichever is longer. It will not be removed before that compatibility policy is satisfied.";
14+
15+
pub fn emit_deprecation_warning() {
16+
eprintln!("{DEPRECATION_WARNING}");
17+
}
18+
1219
#[derive(Debug, Serialize)]
1320
pub struct EvidenceHit {
1421
pub item_id: String,

engines/code-graph/src/cli.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,10 @@ pub fn run(cli: Cli) -> Result<()> {
322322
#[cfg(feature = "miseledger")]
323323
evidence,
324324
} => {
325+
#[cfg(feature = "miseledger")]
326+
if evidence {
327+
crate::adapters::miseledger::emit_deprecation_warning();
328+
}
325329
let db_path = default_query_db_path(cli.db.clone());
326330
let conn = open_read_only(&db_path)?;
327331
#[cfg(feature = "codesearch")]
@@ -603,6 +607,7 @@ pub fn run(cli: Cli) -> Result<()> {
603607
}
604608
#[cfg(feature = "miseledger")]
605609
Command::Links { term, limit, json } => {
610+
crate::adapters::miseledger::emit_deprecation_warning();
606611
let db = crate::adapters::miseledger::default_db_path();
607612
let hits = crate::adapters::miseledger::search_evidence(&db, &term, limit)?;
608613
if json {

engines/code-graph/tests/cli_read_only.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use std::time::SystemTime;
99
use graphtrail::store::{init_schema, open_db, sync_repo};
1010
use sha2::{Digest, Sha256};
1111

12+
#[cfg(feature = "miseledger")]
13+
const MISELEDGER_DEPRECATION_WARNING: &str = "warning: GraphTrail's direct MiseLedger adapter is deprecated. Use `brigade code sync`, `brigade code context`, `brigade code impact`, `brigade evidence crawl`, `brigade evidence search`, and `brigade evidence doctor` instead. The adapter remains functional for at least two minor GraphTrail releases or 90 days after the first GraphTrail release containing this deprecation, whichever is longer. It will not be removed before that compatibility policy is satisfied.";
14+
1215
fn graphtrail() -> &'static str {
1316
env!("CARGO_BIN_EXE_graphtrail")
1417
}
@@ -50,6 +53,60 @@ def run():
5053
db
5154
}
5255

56+
#[cfg(feature = "miseledger")]
57+
fn build_miseledger_db(root: &Path) -> PathBuf {
58+
let db = root.join("miseledger.db");
59+
let conn = rusqlite::Connection::open(&db).unwrap();
60+
conn.execute_batch(
61+
"CREATE VIRTUAL TABLE item_fts USING fts5(item_id, source_kind, body);\
62+
INSERT INTO item_fts (item_id, source_kind, body) VALUES\
63+
('evidence-helper', 'session', 'helper helper');",
64+
)
65+
.unwrap();
66+
conn.pragma_update(None, "wal_checkpoint", "TRUNCATE")
67+
.unwrap();
68+
db
69+
}
70+
71+
#[cfg(feature = "miseledger")]
72+
fn build_evidence_context_miseledger_db(root: &Path) -> PathBuf {
73+
let db = root.join("miseledger.db");
74+
let conn = rusqlite::Connection::open(&db).unwrap();
75+
conn.execute_batch(
76+
"CREATE VIRTUAL TABLE item_fts USING fts5(item_id, source_kind, body);\
77+
INSERT INTO item_fts (item_id, source_kind, body) VALUES\
78+
('evidence-task', 'session', 'evidence'),\
79+
('evidence-alpha', 'session', 'evidence_alpha'),\
80+
('evidence-beta', 'session', 'evidence_beta');",
81+
)
82+
.unwrap();
83+
conn.pragma_update(None, "wal_checkpoint", "TRUNCATE")
84+
.unwrap();
85+
db
86+
}
87+
88+
#[cfg(feature = "miseledger")]
89+
fn build_evidence_context_db(root: &Path) -> PathBuf {
90+
fs::write(
91+
root.join("evidence.py"),
92+
r#"
93+
def evidence_alpha():
94+
return 1
95+
96+
def evidence_beta():
97+
return evidence_alpha()
98+
"#,
99+
)
100+
.unwrap();
101+
let db = root.join(".graphtrail").join("graphtrail.db");
102+
let conn = open_db(&db).unwrap();
103+
init_schema(&conn).unwrap();
104+
sync_repo(&conn, root).unwrap();
105+
conn.pragma_update(None, "wal_checkpoint", "TRUNCATE")
106+
.unwrap();
107+
db
108+
}
109+
53110
fn snapshot_file(path: &Path) -> (Vec<u8>, SystemTime) {
54111
let bytes = fs::read(path).unwrap();
55112
let modified = fs::metadata(path).unwrap().modified().unwrap();
@@ -367,6 +424,147 @@ fn doctor_does_not_mutate_db_or_tree() {
367424
assert_eq!(before_tree, snapshot_tree(graph_dir.parent().unwrap()));
368425
}
369426

427+
#[cfg(feature = "miseledger")]
428+
#[test]
429+
fn deprecated_miseledger_commands_warn_once_without_changing_json_or_databases() {
430+
let graph_dir = tempfile::tempdir().unwrap();
431+
let evidence_dir = tempfile::tempdir().unwrap();
432+
let links_evidence_dir = tempfile::tempdir().unwrap();
433+
let graph_db = build_evidence_context_db(graph_dir.path());
434+
let evidence_db = build_evidence_context_miseledger_db(evidence_dir.path());
435+
let links_evidence_db = build_miseledger_db(links_evidence_dir.path());
436+
let before_graph = snapshot_tree(graph_dir.path());
437+
let before_evidence = snapshot_tree(evidence_dir.path());
438+
let before_links_evidence = snapshot_tree(links_evidence_dir.path());
439+
440+
let context_without_evidence = Command::new(graphtrail())
441+
.current_dir(graph_dir.path())
442+
.args([
443+
"--db",
444+
&graph_db.display().to_string(),
445+
"context",
446+
"evidence",
447+
"--json",
448+
])
449+
.env("MISELEDGER_DB", &evidence_db)
450+
.output()
451+
.unwrap();
452+
assert!(
453+
context_without_evidence.status.success(),
454+
"context without --evidence failed: {context_without_evidence:?}"
455+
);
456+
assert!(
457+
context_without_evidence.stderr.is_empty(),
458+
"context without --evidence warned: {:?}",
459+
String::from_utf8_lossy(&context_without_evidence.stderr)
460+
);
461+
462+
let context_with_evidence = Command::new(graphtrail())
463+
.current_dir(graph_dir.path())
464+
.args([
465+
"--db",
466+
&graph_db.display().to_string(),
467+
"context",
468+
"evidence",
469+
"--json",
470+
"--evidence",
471+
])
472+
.env("MISELEDGER_DB", &evidence_db)
473+
.output()
474+
.unwrap();
475+
assert!(
476+
context_with_evidence.status.success(),
477+
"context with --evidence failed: {context_with_evidence:?}"
478+
);
479+
assert_eq!(
480+
String::from_utf8(context_with_evidence.stderr).unwrap(),
481+
format!("{MISELEDGER_DEPRECATION_WARNING}\n")
482+
);
483+
assert_eq!(
484+
context_with_evidence.stdout, context_without_evidence.stdout,
485+
"--evidence must not change context JSON output"
486+
);
487+
let context_json: serde_json::Value =
488+
serde_json::from_slice(&context_with_evidence.stdout).unwrap();
489+
assert_eq!(context_json["task"], "evidence");
490+
491+
let context_markdown_with_evidence = Command::new(graphtrail())
492+
.current_dir(graph_dir.path())
493+
.args([
494+
"--db",
495+
&graph_db.display().to_string(),
496+
"context",
497+
"evidence",
498+
"--markdown",
499+
"--evidence",
500+
])
501+
.env("MISELEDGER_DB", &evidence_db)
502+
.output()
503+
.unwrap();
504+
assert!(
505+
context_markdown_with_evidence.status.success(),
506+
"markdown context with --evidence failed: {context_markdown_with_evidence:?}"
507+
);
508+
assert_eq!(
509+
String::from_utf8(context_markdown_with_evidence.stderr).unwrap(),
510+
format!("{MISELEDGER_DEPRECATION_WARNING}\n")
511+
);
512+
let markdown = String::from_utf8(context_markdown_with_evidence.stdout).unwrap();
513+
assert_eq!(markdown.matches("### `").count(), 3, "{markdown}");
514+
for expected_link in [
515+
"### `evidence`",
516+
"- [session] `evidence-task` - [evidence]",
517+
"### `evidence_alpha`",
518+
"- [session] `evidence-alpha` - [evidence-alpha]",
519+
"### `evidence_beta`",
520+
"- [session] `evidence-beta` - [evidence-beta]",
521+
] {
522+
assert!(markdown.contains(expected_link), "{markdown}");
523+
}
524+
525+
let links = Command::new(graphtrail())
526+
.args(["links", "helper", "--json"])
527+
.env("MISELEDGER_DB", &links_evidence_db)
528+
.output()
529+
.unwrap();
530+
assert!(links.status.success(), "links failed: {links:?}");
531+
assert_eq!(
532+
String::from_utf8(links.stderr).unwrap(),
533+
format!("{MISELEDGER_DEPRECATION_WARNING}\n")
534+
);
535+
let links_json: serde_json::Value = serde_json::from_slice(&links.stdout).unwrap();
536+
assert_eq!(links_json[0]["item_id"], "evidence-helper");
537+
538+
assert_eq!(before_graph, snapshot_tree(graph_dir.path()));
539+
assert_eq!(before_evidence, snapshot_tree(evidence_dir.path()));
540+
assert_eq!(
541+
before_links_evidence,
542+
snapshot_tree(links_evidence_dir.path())
543+
);
544+
}
545+
546+
#[cfg(not(feature = "miseledger"))]
547+
#[test]
548+
fn miseledger_commands_are_unavailable_without_the_feature_and_do_not_warn() {
549+
let context_help = Command::new(graphtrail())
550+
.args(["context", "--help"])
551+
.output()
552+
.unwrap();
553+
assert!(context_help.status.success(), "{context_help:?}");
554+
assert!(
555+
!String::from_utf8_lossy(&context_help.stdout).contains("--evidence"),
556+
"{context_help:?}"
557+
);
558+
assert!(context_help.stderr.is_empty(), "{context_help:?}");
559+
560+
let links = Command::new(graphtrail()).arg("links").output().unwrap();
561+
assert!(!links.status.success(), "links unexpectedly succeeded");
562+
assert!(
563+
!String::from_utf8_lossy(&links.stderr).contains("direct MiseLedger adapter is deprecated"),
564+
"miseledger-free build warned: {links:?}"
565+
);
566+
}
567+
370568
#[cfg(all(feature = "codesearch", feature = "miseledger"))]
371569
#[test]
372570
fn context_help_lists_join_layer_flags() {

0 commit comments

Comments
 (0)