Skip to content
Open
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
76 changes: 76 additions & 0 deletions ledger-cli/tests/repository_store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//! This repository's own store, re-laid out under `.decisions/ns/<namespace>/`
//! in one commit (PRD §3.11, issue 10; AC-82).
//!
//! The history before that commit holds the flat layout of revision v1.8,
//! so this repository is verified by the legacy capability (LP-3.35): with
//! it, `ledger verify --export` is conformant with blame on, which is every
//! acceptance's `L009` author and every entity's immutability verdict as
//! before the re-layout; without it, the repository is refused with exit 2,
//! naming the first flat commit, and never reported conformant. CI checks
//! out the full history, which both runs need.

use std::path::{Path, PathBuf};
use std::process::Output;

fn workspace() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("..")
}

fn ledger(args: &[&str]) -> Output {
let mut cmd = assert_cmd::Command::cargo_bin("ledger").expect("binary");
cmd.arg("--root").arg(workspace()).args(args);
cmd.output().expect("run")
}

fn text(out: &Output) -> String {
format!("{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr))
}

/// Whether the clone carries the flat commits at all: a shallow clone may
/// not, and then there is no flat history to read or to refuse.
fn first_flat_commit() -> Option<String> {
let out = std::process::Command::new("git")
.arg("-C")
.arg(workspace())
.args(["log", "--first-parent", "--reverse", "--format=%H", "--", ".decisions/log", ".decisions/roles", ".decisions/sig"])
.output()
.expect("git");
String::from_utf8_lossy(&out.stdout).lines().next().map(str::to_string)
}

#[test]
fn the_store_is_laid_out_one_directory_per_namespace() {
let store = workspace().join(".decisions");
for flat in ["sets", "roles", "log", "sig", "allowed_signers"] {
assert!(!store.join(flat).exists(), "{flat} is a flat path (LP-3.34)");
}
let namespaces = ledger_core::layout::namespaces(&workspace());
assert_eq!(namespaces, ["hafeok.ddd", "hafeok.ledger"]);
let logs = |ns: &str| std::fs::read_dir(ledger_core::layout::log_dir(&workspace(), ns)).expect("log").count();
assert_eq!((logs("hafeok.ddd"), logs("hafeok.ledger")), (164, 23), "PRD §3.11");
}

#[test]
fn with_the_legacy_capability_this_repository_verifies_conformant_with_blame_on() {
let out = ledger(&["verify", "--export"]);
let report = text(&out);
assert_eq!(out.status.code(), Some(0), "{report}");
assert!(report.contains("conformant"), "{report}");
assert!(report.contains("export: every committed export matches the log byte for byte"), "{report}");
// Blame was on: every acceptance's introducing commit was found along
// the file's lineage, never the re-layout commit (AC-82).
assert!(!report.contains("[L009]") && !report.contains("[L007]"), "{report}");
}

#[test]
fn without_the_capability_this_repository_is_refused_naming_its_first_flat_commit() {
let Some(first) = first_flat_commit() else {
eprintln!("skipped: this clone does not carry the flat history");
return;
};
let out = ledger(&["verify", "--export", "--no-legacy-layout"]);
let report = text(&out);
assert_eq!(out.status.code(), Some(2), "the gate could not run (ruling 82): {report}");
assert!(report.contains(&first), "names the first flat commit {first}: {report}");
assert!(!report.contains("conformant"), "never reported conformant: {report}");
}
2 changes: 1 addition & 1 deletion ledger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ base name, not its path, so the move does not touch it.
`docs/ledger-format-v1.md` and `docs/ledger-format-migrations.md` were absorbed
into `spec/ledger-protocol.md` and deleted (ruling 22). Issue bodies, rulings,
session close-outs, the `.ddd/` store, the hashed basis pointers in
`.decisions/log/` (`format:ledger-format-v1#…`) and other repositories cite the
`.decisions/ns/<ns>/log/` (`format:ledger-format-v1#…`) and other repositories cite the
format document's sections. The one map from those sections to the protocol's
is the protocol's Appendix C.0 (ruling 36). Read "ground" in the earlier
rulings, in the sense of what a version rests on, as "basis" (ruling 23).
9 changes: 6 additions & 3 deletions ledger/spec/ledger-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -1651,9 +1651,12 @@ namespace its entities belong to, each set to the namespace whose versions
name it, each role file to the namespace whose grants and policies name
it. A governed store with more than one namespace is not moved but
re-founded (ruling 68); its note lands with issue 9. This repository's
own store and the committed fixture stores are re-laid out by issues 10
and 9; until then a verifier of this revision refuses them, as LP-3.34
says.
own store was re-laid out in one commit on 9 October 2026 (issue 10):
`hafeok.ddd`'s 164 change-sets and its set to `ns/hafeok.ddd/`,
`hafeok.ledger`'s 23 and its set to `ns/hafeok.ledger/`, every file byte
for byte, both exports unchanged. The committed fixture stores are
re-laid out by issue 9; until then a verifier of this revision refuses
them, as LP-3.34 says.

**The flat layout in history** (rulings 82 and 97; LP-3.35). The
re-layout cannot make the flat paths disappear from history, and
Expand Down
Loading