use std::process::ExitCode;
use anyhow::{Context, Result};
use clap::ValueEnum;
use serde::Serialize;
use crate::handoff::GitState;
#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
pub enum OutputFormat {
Text,
Json,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
pub enum MemoryDepth {
Content,
Metadata,
}
pub trait CommandReport: Serialize {
fn exit_code(&self) -> ExitCode;
fn render_text(&self);
}
#[derive(Serialize)]
struct ErrorReport {
ok: bool,
error: String,
}
pub struct FieldFilterSpec {
valid_fields: &'static [&'static str],
expensive_fields: &'static [&'static str],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DefaultJsonContract {
Start,
Status,
RadarState,
}
#[derive(Serialize)]
pub struct WorkStreamView {
status: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
}
pub fn work_stream_view(git: Option<&GitState>) -> WorkStreamView {
match git {
Some(git) if git.branch != "HEAD" => WorkStreamView {
status: "available",
name: Some(git.branch.clone()),
},
Some(_) => WorkStreamView {
status: "detached",
name: None,
},
None => WorkStreamView {
status: "missing",
name: None,
},
}
}
pub fn render_report<T: CommandReport>(format: OutputFormat, report: &T) -> Result<ExitCode> {
match format {
OutputFormat::Text => report.render_text(),
OutputFormat::Json => println!("{}", serde_json::to_string_pretty(report)?),
}
Ok(report.exit_code())
}
pub const START_FIELD_FILTER_SPEC: FieldFilterSpec = FieldFilterSpec {
valid_fields: &[
"command",
"ok",
"disposition",
"session_boundary",
"path",
"profile",
"project_id",
"workspace_path",
"work_stream",
"locality_id",
"session_id",
"compact_summary",
"source_order",
"manifest",
"machine_identity",
"machine_presence",
"execution_context",
"takeover_preconditions",
"coordination_scope",
"sources",
"effective_policy",
"policy_projection",
"effective_memory",
"memory_recall",
"memory_provider",
"startup_recall",
"execution_gates",
"handoff",
"continuity",
"recovery",
"session_state",
"escalation",
"compiled_state",
"alerts",
"refresh_actions",
"warnings",
"activation",
],
expensive_fields: &[
"source_order",
"manifest",
"sources",
"effective_policy",
"policy_projection",
"effective_memory",
"memory_recall",
"execution_gates",
"handoff",
],
};
pub const STATUS_FIELD_FILTER_SPEC: FieldFilterSpec = FieldFilterSpec {
valid_fields: &[
"command",
"ok",
"path",
"profile",
"project_id",
"locality_id",
"workspace_path",
"work_stream",
"phase",
"phase_reason",
"blocked",
"recommended_next_command",
"bootstrap",
"machine_identity",
"machine_presence",
"execution_context",
"takeover_preconditions",
"coordination_scope",
"git",
"checkout_state",
"continuity",
"session",
"execution_gates",
"escalation",
"telemetry",
"review",
"warnings",
],
expensive_fields: &[],
};
pub const DOCTOR_FIELD_FILTER_SPEC: FieldFilterSpec = FieldFilterSpec {
valid_fields: &[
"command",
"ok",
"repo_root",
"failures",
"warnings",
"host_integrations",
"checks",
],
expensive_fields: &[],
};
pub const RADAR_STATE_FIELD_FILTER_SPEC: FieldFilterSpec = FieldFilterSpec {
valid_fields: &[
"command",
"mode",
"schema_version",
"ok",
"path",
"profile",
"project_id",
"locality_id",
"delta_warning",
"session_delta",
"handoff",
"execution_gates",
"effective_memory",
"repo_native_checks",
"git",
"checkout_state",
"session_state",
"recovery",
"escalation",
"projection_telemetry",
"work_stream_decay",
"context_health",
"session_boundary",
"behavioral_drift",
"evaluation",
"candidate_updates",
"approval_intents",
"approval_steps",
"commit",
],
expensive_fields: &["effective_memory", "repo_native_checks"],
};
pub const CONTEXT_CHECK_FIELD_FILTER_SPEC: FieldFilterSpec = FieldFilterSpec {
valid_fields: &[
"command",
"schema_version",
"ok",
"path",
"profile",
"project_id",
"locality_id",
"trigger",
"action",
"reason",
"recommendation",
"urgency",
"throttle",
"payload",
"evidence",
"session_state",
"execution_gates",
"recovery",
"escalation",
"context_health",
"session_boundary",
"behavioral_drift",
],
expensive_fields: &[],
};
pub fn validate_fields(spec: &FieldFilterSpec, fields: &[String]) -> Result<()> {
for field in fields {
if !spec.valid_fields.contains(&field.as_str()) {
anyhow::bail!(
"unknown field: `{field}`; valid fields: {}",
spec.valid_fields.join(", ")
);
}
}
Ok(())
}
pub fn try_filter_json_fields(
mut value: serde_json::Value,
fields: &[String],
spec: &FieldFilterSpec,
) -> Result<serde_json::Value> {
let obj = value
.as_object_mut()
.context("report is not a JSON object")?;
validate_fields(spec, fields)?;
let keys_to_remove: Vec<String> = obj
.keys()
.filter(|key| !fields.iter().any(|f| f == *key))
.cloned()
.collect();
for key in keys_to_remove {
obj.remove(&key);
}
Ok(value)
}
pub fn render_report_with_fields<T: CommandReport>(
report: T,
fields: &[String],
spec: &FieldFilterSpec,
) -> Result<ExitCode> {
let exit_code = report.exit_code();
let value = serde_json::to_value(&report)?;
let filtered = try_filter_json_fields(value, fields, spec)?;
println!("{}", serde_json::to_string_pretty(&filtered)?);
Ok(exit_code)
}
pub fn apply_default_json_contract(
value: serde_json::Value,
contract: DefaultJsonContract,
) -> Result<serde_json::Value> {
let spec = match contract {
DefaultJsonContract::Start => &START_FIELD_FILTER_SPEC,
DefaultJsonContract::Status => &STATUS_FIELD_FILTER_SPEC,
DefaultJsonContract::RadarState => &RADAR_STATE_FIELD_FILTER_SPEC,
};
let fields = spec
.valid_fields
.iter()
.map(|field| (*field).to_owned())
.collect::<Vec<_>>();
try_filter_json_fields(value, &fields, spec)
}
pub fn needs_expensive_rendering(spec: &FieldFilterSpec, fields: &[String]) -> bool {
fields
.iter()
.any(|f| spec.expensive_fields.contains(&f.as_str()))
}
pub fn validate_start_fields(fields: &[String]) -> Result<()> {
validate_fields(&START_FIELD_FILTER_SPEC, fields)
}
pub fn needs_source_rendering(fields: &[String]) -> bool {
needs_expensive_rendering(&START_FIELD_FILTER_SPEC, fields)
}
pub fn render_error(format: OutputFormat, error: &anyhow::Error) {
match format {
OutputFormat::Text => eprintln!("error: {error:#}"),
OutputFormat::Json => {
let report = ErrorReport {
ok: false,
error: format!("{error:#}"),
};
println!(
"{}",
serde_json::to_string_pretty(&report).expect("error report serialization")
);
}
}
}
pub fn strip_memory_content(mut value: serde_json::Value) -> serde_json::Value {
if let Some(mem) = value.pointer_mut("/effective_memory/content") {
*mem = serde_json::Value::String(String::new());
}
for layer in &[
"profile_entries",
"repo_entries",
"pod_entries",
"branch_entries",
"clone_entries",
] {
let pointer = format!("/effective_memory/structured/{layer}");
if let Some(serde_json::Value::Array(entries)) = value.pointer_mut(&pointer) {
for entry in entries.iter_mut() {
if let Some(c) = entry.get_mut("content") {
*c = serde_json::Value::String(String::new());
}
}
}
}
value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn filter_fields_retains_only_requested_keys() {
let value = serde_json::json!({
"command": "start",
"ok": true,
"path": "/tmp/repo",
"compact_summary": {"current_state": {"title": "Next Session"}},
"compiled_state": {"schema_version": 3},
"effective_memory": {"content": "big payload"},
"sources": [{"kind": "project_truth"}]
});
let filtered = try_filter_json_fields(
value,
&[
"command".to_owned(),
"compact_summary".to_owned(),
"compiled_state".to_owned(),
],
&START_FIELD_FILTER_SPEC,
)
.unwrap();
assert_eq!(filtered["command"], "start");
assert!(filtered["compact_summary"].is_object());
assert!(filtered["compiled_state"].is_object());
assert!(filtered.get("effective_memory").is_none());
assert!(filtered.get("sources").is_none());
assert!(filtered.get("ok").is_none());
}
#[test]
fn filter_fields_rejects_unknown_field() {
let value = serde_json::json!({"command": "start", "ok": true});
let err = try_filter_json_fields(
value,
&["command".to_owned(), "nonexistent".to_owned()],
&START_FIELD_FILTER_SPEC,
);
assert!(err.is_err());
assert!(err.unwrap_err().to_string().contains("nonexistent"));
}
#[test]
fn radar_state_filter_fields_retains_only_requested_keys() {
let value = serde_json::json!({
"command": "radar-state",
"ok": true,
"schema_version": 20,
"path": "/tmp/repo",
"profile": "main",
"project_id": "ccdrepo_abc",
"locality_id": "ccdrepo_abc",
"session_boundary": {"action": "continue"},
"effective_memory": {"content": "big payload"},
"evaluation": {"summary": "all good"},
});
let filtered = try_filter_json_fields(
value,
&[
"command".to_owned(),
"ok".to_owned(),
"session_boundary".to_owned(),
],
&RADAR_STATE_FIELD_FILTER_SPEC,
)
.unwrap();
assert_eq!(filtered["command"], "radar-state");
assert!(filtered["session_boundary"].is_object());
assert!(filtered.get("effective_memory").is_none());
assert!(filtered.get("evaluation").is_none());
}
#[test]
fn radar_state_filter_fields_rejects_unknown_field() {
let value = serde_json::json!({"command": "radar-state", "ok": true});
let err = try_filter_json_fields(
value,
&["command".to_owned(), "bogus_field".to_owned()],
&RADAR_STATE_FIELD_FILTER_SPEC,
);
assert!(err.is_err());
assert!(err.unwrap_err().to_string().contains("bogus_field"));
}
#[test]
fn strip_memory_content_removes_content_fields() {
let value = serde_json::json!({
"command": "start",
"effective_memory": {
"status": "loaded",
"content": "profile memory block\nlocality memory block",
"parts": [{"kind": "profile", "path": "/p", "status": "loaded"}],
"structured": {
"status": "loaded",
"profile_entries": [
{"id": "mem_1", "type": "rule", "content": "some rule text"}
],
"repo_entries": [],
"pod_entries": [],
"branch_entries": [],
"clone_entries": [],
"diagnostics": []
}
}
});
let stripped = strip_memory_content(value.clone());
assert_eq!(stripped["effective_memory"]["content"], "");
assert_eq!(
stripped["effective_memory"]["structured"]["profile_entries"][0]["content"],
""
);
assert_eq!(stripped["command"], "start");
assert_eq!(stripped["effective_memory"]["status"], "loaded");
assert_eq!(
stripped["effective_memory"]["structured"]["profile_entries"][0]["id"],
"mem_1"
);
}
#[test]
fn strip_memory_content_is_idempotent_without_memory() {
let value = serde_json::json!({"command": "start", "ok": true});
let stripped = strip_memory_content(value.clone());
assert_eq!(stripped, value);
}
#[test]
fn default_start_json_contract_preserves_kernel_shape() {
let value = serde_json::json!({
"command": "start",
"session_boundary": {"action": "continue"},
});
let filtered = apply_default_json_contract(value, DefaultJsonContract::Start).unwrap();
assert_eq!(filtered["command"], "start");
assert_eq!(filtered["session_boundary"]["action"], "continue");
}
#[test]
fn default_status_json_contract_preserves_kernel_shape() {
let value = serde_json::json!({
"command": "status",
"review": {"status": "idle"},
});
let filtered = apply_default_json_contract(value, DefaultJsonContract::Status).unwrap();
assert_eq!(filtered["command"], "status");
assert_eq!(filtered["review"]["status"], "idle");
}
#[test]
fn default_radar_json_contract_preserves_kernel_shape() {
let value = serde_json::json!({
"command": "radar-state",
"evaluation": {"next_step": {"status": "continue"}},
"candidate_updates": {"handoff": [], "memory": []}
});
let filtered = apply_default_json_contract(value, DefaultJsonContract::RadarState).unwrap();
assert!(filtered["evaluation"].get("next_step").is_some());
assert!(filtered["candidate_updates"].get("memory").is_some());
}
#[test]
fn needs_expensive_rendering_detects_expensive_fields() {
assert!(needs_expensive_rendering(
&START_FIELD_FILTER_SPEC,
&["effective_memory".to_owned()]
));
assert!(needs_expensive_rendering(
&START_FIELD_FILTER_SPEC,
&["compiled_state".to_owned(), "handoff".to_owned()]
));
assert!(needs_expensive_rendering(
&START_FIELD_FILTER_SPEC,
&["memory_recall".to_owned()]
));
assert!(!needs_expensive_rendering(
&START_FIELD_FILTER_SPEC,
&[
"compact_summary".to_owned(),
"compiled_state".to_owned(),
"alerts".to_owned()
]
));
assert!(!needs_expensive_rendering(
&STATUS_FIELD_FILTER_SPEC,
&["review".to_owned()]
));
}
}