feat: complete Epic 1 production foundation

This commit is contained in:
2026-08-25 01:24:11 +03:00
parent 767428436d
commit 182bde8ac0
298 changed files with 35719 additions and 5299 deletions
@@ -1,23 +1,28 @@
use crank_adapter_rest::RestAdapterError;
use crank_community_mcp::tool_error::tool_error_contract_from_runtime;
use crank_community_mcp::tool_error::{
tool_error_contract_from_failure, tool_error_contract_from_runtime,
};
use crank_core::{
CorrelationContext, ExecutionErrorCode, ExecutionFailure, ExecutionLocale,
ProtocolAdapterError, Retryability,
};
use crank_runtime::RuntimeError;
use serde_json::json;
#[test]
fn maps_upstream_429_to_recoverable_structured_tool_error() {
let contract = tool_error_contract_from_runtime(
&RuntimeError::RestAdapter(RestAdapterError::UnexpectedStatus {
&RuntimeError::ProtocolAdapter(ProtocolAdapterError::UnexpectedStatus {
status: 429,
body: json!({
"error": "rate limit exceeded",
"internal_trace": "do not leak"
}),
dispatch: crank_core::DispatchEvidence::MayHaveDispatched,
}),
"req-429",
"0af7651916cd43dd8448eb211c80319c",
ExecutionLocale::Ru,
);
assert_eq!(contract.error_code, "upstream_rate_limited");
assert_eq!(contract.stage, "upstream");
assert_eq!(contract.retryability, "after_delay");
assert_eq!(contract.outcome_certainty, "certain");
assert!(contract.recoverable);
assert_eq!(contract.upstream_status, Some(429));
assert_eq!(contract.request_id, "req-429");
@@ -26,6 +31,22 @@ fn maps_upstream_429_to_recoverable_structured_tool_error() {
assert!(!contract.message.contains("internal_trace"));
}
#[test]
fn maps_upstream_429_to_english_suggested_action() {
let contract = tool_error_contract_from_runtime(
&RuntimeError::ProtocolAdapter(ProtocolAdapterError::UnexpectedStatus {
status: 429,
dispatch: crank_core::DispatchEvidence::MayHaveDispatched,
}),
"req-429",
"0af7651916cd43dd8448eb211c80319c",
ExecutionLocale::En,
);
assert_eq!(contract.error_code, "upstream_rate_limited");
assert_eq!(contract.suggested_action, Some("Retry the request later."));
}
#[test]
fn maps_mapping_error_to_non_recoverable_structured_tool_error() {
let contract = tool_error_contract_from_runtime(
@@ -35,15 +56,91 @@ fn maps_mapping_error_to_non_recoverable_structured_tool_error() {
},
"req-map",
"0af7651916cd43dd8448eb211c80319c",
ExecutionLocale::Ru,
);
assert_eq!(contract.error_code, "runtime_error");
assert_eq!(contract.error_code, "prepared_request_invalid");
assert_eq!(contract.stage, "request_preparation");
assert_eq!(contract.retryability, "never");
assert!(!contract.recoverable);
assert_eq!(contract.upstream_status, None);
assert_eq!(contract.request_id, "req-map");
assert_eq!(contract.trace_id, "0af7651916cd43dd8448eb211c80319c");
assert_eq!(contract.suggested_action, None);
}
#[test]
fn ambiguous_timeout_requires_manual_reconciliation() {
let contract = tool_error_contract_from_runtime(
&RuntimeError::ExecutionDeadlineElapsed {
may_have_dispatched: true,
},
"req-timeout",
"0af7651916cd43dd8448eb211c80319c",
ExecutionLocale::Ru,
);
assert_eq!(contract.error_code, "upstream_timeout");
assert_eq!(contract.retryability, "manual_reconcile");
assert_eq!(contract.outcome_certainty, "outcome_unknown");
assert!(!contract.recoverable);
}
#[test]
fn every_execution_code_projects_the_same_canonical_semantics_as_admin() {
for code in ExecutionErrorCode::ALL {
let contract = tool_error_contract_from_failure(
&ExecutionFailure::new(code, CorrelationContext::generate()),
ExecutionLocale::En,
);
assert_eq!(contract.error_code, code.as_str());
assert_eq!(contract.stage, code.stage().as_str());
assert_eq!(contract.retryability, code.retryability().as_str());
assert_eq!(
contract.outcome_certainty,
code.outcome_certainty().as_str()
);
assert_eq!(
contract.recoverable,
matches!(
code.retryability(),
Retryability::Safe | Retryability::AfterDelay | Retryability::RequiresConfirmation
)
);
assert!(!contract.message.is_empty());
}
}
#[test]
fn confirmation_challenge_is_localized_for_mcp_clients() {
let failure = ExecutionFailure::new(
ExecutionErrorCode::ConfirmationRequired,
CorrelationContext::generate(),
)
.try_with_confirmation("ct_safe", 30_000)
.expect("valid confirmation challenge");
let en = tool_error_contract_from_failure(&failure, ExecutionLocale::En);
assert!(
en.message.contains(
"Repeat the call with _crank_confirmation_token=\"ct_safe\" within 30 seconds."
),
"{:?}",
en.message
);
assert_eq!(
contract.suggested_action,
Some("Проверьте параметры вызова инструмента.")
en.suggested_action,
Some("Repeat the call with the provided confirmation token.")
);
let ru = tool_error_contract_from_failure(&failure, ExecutionLocale::Ru);
assert!(
ru.message
.contains("Повторите вызов с _crank_confirmation_token=\"ct_safe\""),
"{:?}",
ru.message
);
assert_eq!(
ru.suggested_action,
Some("Повторите вызов с указанным токеном подтверждения.")
);
}