use aristo_core::critique::CritiqueFile;
use aristo_core::hash::body_hash;
use aristo_core::index::{AnnotationId, IndexFile};
use crate::commands::critique::pending::{critique_path_for, PIPELINE_NAME};
use crate::commands::critique::validator::{stamp_derived, validate};
use crate::commands::index::atomic_write;
use crate::pipeline::queue::{self, QueueDir};
use crate::{CliError, CliResult, Workspace};
#[aristo::intent(
"`aristo critique --submit-findings` is the only path that creates \
a `.aristo/critiques/<id>.critique` file. On accept it prints \
`accepted: sha256:<hex>` to stdout for the orchestrator's integrity \
check. Every validation gate — the schema enums, the focal id \
existing in the current index, the text staleness anchor, and a \
non-empty rationale on each finding — runs first; if any fails, \
nothing is written.",
verify = "neural",
id = "submit_findings_is_only_write_path_for_critiques"
)]
pub(crate) fn run_submit_findings(
ws: &Workspace,
index: &IndexFile,
id_str: &str,
json_str: &str,
) -> CliResult<()> {
let id = AnnotationId::parse(id_str).map_err(|e| CliError::Other {
message: format!("--id {id_str:?}: {e}"),
exit_code: 2,
})?;
if !index.entries.contains_key(&id) {
return Err(CliError::Other {
message: format!(
"--id {id_str}: no such annotation in current index (run `aristo stamp` if you just added it)"
),
exit_code: 2,
});
}
let mut cf: CritiqueFile = serde_json::from_str(json_str).map_err(|e| CliError::Other {
message: format!("--json: parse error: {e}"),
exit_code: 2,
})?;
let report = validate(&id, &cf, index);
if !report.is_empty() {
eprintln!("error: {}", report.render());
return Err(CliError::Silent { exit_code: 1 });
}
stamp_derived(&mut cf);
let path = critique_path_for(ws, &id);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let toml_text = cf.to_toml().map_err(|e| CliError::Other {
message: format!("serializing critique for {}: {e}", id.as_str()),
exit_code: 1,
})?;
atomic_write(&path, &toml_text)?;
let written = std::fs::read_to_string(&path).map_err(|e| CliError::Other {
message: format!("read-back {}: {e}", path.display()),
exit_code: 1,
})?;
let h = body_hash(&written);
let qdir = QueueDir::for_pipeline(ws, PIPELINE_NAME);
queue::submit_done(&qdir, &id)?;
println!("accepted: {h}");
Ok(())
}