Close the clawability backlog with deterministic CLI output and lane lineage

Finish the remaining roadmap work by making direct CLI JSON output deterministic across the non-interactive surface, restoring the degraded-startup MCP test as a real workspace test, and adding branch-lock plus commit-lineage primitives so downstream lane consumers can distinguish superseded worktree commits from canonical lineage.

Constraint: Keep the user-facing config namespace centered on .claw while preserving legacy fallback discovery for compatibility
Constraint: Verification needed to stay clean-room and reproducible from the checked-in workspace alone
Rejected: Leave the output-format contract implied by ad-hoc smoke runs only | too easy for direct CLI regressions to slip back into prose-only output
Rejected: Keep commit provenance as free-form detail text | downstream consumers need structured branch/worktree/supersession metadata
Confidence: medium
Scope-risk: moderate
Directive: Extend the JSON contract through the same direct CLI entrypoints instead of adding one-off serializers on parallel code paths
Tested: python .github/scripts/check_doc_source_of_truth.py
Tested: cd rust && cargo fmt --all --check
Tested: cd rust && cargo test --workspace
Tested: cd rust && cargo clippy -p commands -p tools -p rusty-claude-cli --all-targets --no-deps -- -D warnings
Not-tested: full cargo clippy --workspace --all-targets -- -D warnings still reports unrelated pre-existing runtime lint debt outside this change set
This commit is contained in:
Yeachan-Heo
2026-04-05 18:40:33 +00:00
parent 93e979261e
commit 19c6b29524
14 changed files with 954 additions and 138 deletions

View File

@@ -11,7 +11,8 @@ use api::{
use plugins::PluginTool;
use reqwest::blocking::Client;
use runtime::{
check_freshness, edit_file, execute_bash, glob_search, grep_search, load_system_prompt,
check_freshness, dedupe_superseded_commit_events, edit_file, execute_bash, glob_search,
grep_search, load_system_prompt,
lsp_client::LspRegistry,
mcp_tool_bridge::McpToolRegistry,
permission_enforcer::{EnforcementResult, PermissionEnforcer},
@@ -1704,7 +1705,7 @@ fn run_remote_trigger(input: RemoteTriggerInput) -> Result<String, String> {
"method": method,
"status_code": status,
"body": truncated_body,
"success": status >= 200 && status < 300
"success": (200..300).contains(&status)
}))
}
Err(e) => to_pretty_json(json!({
@@ -3276,9 +3277,11 @@ fn agent_permission_policy() -> PermissionPolicy {
}
fn write_agent_manifest(manifest: &AgentOutput) -> Result<(), String> {
let mut normalized = manifest.clone();
normalized.lane_events = dedupe_superseded_commit_events(&normalized.lane_events);
std::fs::write(
&manifest.manifest_file,
serde_json::to_string_pretty(manifest).map_err(|error| error.to_string())?,
&normalized.manifest_file,
serde_json::to_string_pretty(&normalized).map_err(|error| error.to_string())?,
)
.map_err(|error| error.to_string())
}
@@ -3297,7 +3300,7 @@ fn persist_agent_terminal_state(
let mut next_manifest = manifest.clone();
next_manifest.status = status.to_string();
next_manifest.completed_at = Some(iso8601_now());
next_manifest.current_blocker = blocker.clone();
next_manifest.current_blocker.clone_from(&blocker);
next_manifest.error = error;
if let Some(blocker) = blocker {
next_manifest
@@ -5823,6 +5826,7 @@ mod tests {
}
#[test]
#[allow(clippy::too_many_lines)]
fn agent_fake_runner_can_persist_completion_and_failure() {
let _guard = env_lock()
.lock()