Skip to content

Commit 4756204

Browse files
mason: report lineage-scoped Claude Code pending status
Count untagged held parts by full native provider key and ordered reason; persist fenced counters-only hook failures, clear them on answers, and log pending status once per conversation. With explicit parent approval, update only the hook_conflicts_with_setup_descent case: a refusal after the publication fence now records the exact failed_subjects delta. Preserve the existing no-publication assertions and assert hook_failed attribution after raw admission. The old whole-row-unchanged expectation conflicts with the reviewed failure-telemetry contract. Co-authored-by: Alfonso <alfonso-magic-context@users.noreply.github.com>
1 parent 7c1aa18 commit 4756204

4 files changed

Lines changed: 899 additions & 211 deletions

File tree

‎crates/mc-module/src/lib.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9021,9 +9021,14 @@ impl McHandler {
90219021
),
90229022
)
90239023
};
9024+
let cc_pending = match self.claude_code_pending_status(&binding) {
9025+
Ok(pending) => pending,
9026+
Err(outcome) => return outcome,
9027+
};
90249028
let response = json!({
90259029
"ok": true,
90269030
"summary": summary,
9031+
"cc_pending": cc_pending,
90279032
"wrapup_active": wrapup_active.is_some(),
90289033
"wrapup_rounds": wrapup_active,
90299034
"coverage_ordinal": loaded.meta.coverage_ordinal,

‎crates/mc-module/src/providers/setup_descent_tests.rs‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ async fn check_case(name: &str) {
144144
};
145145
assert_eq!(code, refusal["code"], "{name} call {index}");
146146
assert_eq!(field, refusal["detail"]["field"], "{name} call {index}");
147+
let mut before = before;
148+
if name == "hook_conflicts_with_setup_descent" {
149+
let conversation = before.as_mut().unwrap();
150+
let mut counters: Value =
151+
serde_json::from_str(&conversation.hook_counters_json).unwrap();
152+
assert!(counters.get("failed_subjects").is_none());
153+
counters["failed_subjects"] =
154+
json!([{"subject_mid":"next","hook":"pre_user","subject_part":""}]);
155+
conversation.hook_counters_json = counters.to_string();
156+
}
147157
assert_eq!(
148158
serde_json::to_value(store.load_provider_conversation(&key.store_key()).unwrap())
149159
.unwrap(),
@@ -180,6 +190,36 @@ async fn check_case(name: &str) {
180190
"{name} call {index}"
181191
);
182192
}
193+
if name == "hook_conflicts_with_setup_descent" {
194+
let raw = r#"{"role":"user","content":"next"}"#;
195+
let message = super::super::codec_claude_code::from_wire(
196+
&serde_json::from_str(raw).unwrap(),
197+
Some(raw),
198+
)
199+
.unwrap();
200+
let lineage = store
201+
.load_provider_lineage(&key.store_key(), "child")
202+
.unwrap()
203+
.unwrap();
204+
store
205+
.commit_provider_status(
206+
&key.store_key(),
207+
&lineage,
208+
&[ProviderMessage {
209+
mid: "next".into(),
210+
ordinal: 3,
211+
message_bytes: serde_json::to_vec(&message).unwrap(),
212+
}],
213+
None,
214+
&[],
215+
)
216+
.unwrap();
217+
assert_eq!(
218+
h.claude_code_pending_status(&h.facade_binding(7).unwrap())
219+
.unwrap()["reasons"]["hook_failed"],
220+
1
221+
);
222+
}
183223
}
184224

185225
macro_rules! vector_tests {

0 commit comments

Comments
 (0)