@team-agent/installer 0.5.41 → 0.5.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +129 -54
- package/crates/team-agent/src/cli/diagnose.rs +1 -2
- package/crates/team-agent/src/cli/emit.rs +1 -7
- package/crates/team-agent/src/cli/helpers.rs +3 -1
- package/crates/team-agent/src/cli/leader.rs +2 -1
- package/crates/team-agent/src/cli/mod.rs +27 -7
- package/crates/team-agent/src/cli/named_address.rs +14 -5
- package/crates/team-agent/src/cli/profile.rs +19 -7
- package/crates/team-agent/src/cli/send.rs +9 -3
- package/crates/team-agent/src/cli/status.rs +8 -30
- package/crates/team-agent/src/cli/status_port.rs +1341 -1272
- package/crates/team-agent/src/cli/tests/base.rs +738 -660
- package/crates/team-agent/src/cli/tests/compile.rs +45 -18
- package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
- package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
- package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
- package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
- package/crates/team-agent/src/cli/tests/mod.rs +17 -7
- package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
- package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
- package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
- package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
- package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
- package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
- package/crates/team-agent/src/codex_app_server.rs +2 -5
- package/crates/team-agent/src/compiler/tests.rs +139 -33
- package/crates/team-agent/src/compiler.rs +55 -22
- package/crates/team-agent/src/conpty/backend.rs +23 -33
- package/crates/team-agent/src/coordinator/backoff.rs +2 -7
- package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
- package/crates/team-agent/src/coordinator/health.rs +46 -31
- package/crates/team-agent/src/coordinator/mod.rs +3 -3
- package/crates/team-agent/src/coordinator/orphan.rs +22 -10
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +47 -52
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
- package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
- package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
- package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
- package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
- package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
- package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
- package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
- package/crates/team-agent/src/coordinator/tick.rs +19 -1
- package/crates/team-agent/src/db/message_store.rs +138 -30
- package/crates/team-agent/src/db/migration.rs +249 -61
- package/crates/team-agent/src/db/schema.rs +303 -82
- package/crates/team-agent/src/diagnose/comms.rs +9 -2
- package/crates/team-agent/src/diagnose/mod.rs +1 -3
- package/crates/team-agent/src/diagnose/orphans.rs +79 -61
- package/crates/team-agent/src/event_log.rs +70 -16
- package/crates/team-agent/src/layout/manager.rs +15 -4
- package/crates/team-agent/src/layout/mod.rs +4 -4
- package/crates/team-agent/src/layout/overlay.rs +10 -3
- package/crates/team-agent/src/layout/placement.rs +5 -1
- package/crates/team-agent/src/layout/recovery.rs +4 -2
- package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
- package/crates/team-agent/src/layout/sessions.rs +17 -9
- package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
- package/crates/team-agent/src/layout/worker_env.rs +52 -19
- package/crates/team-agent/src/leader/helpers.rs +7 -1
- package/crates/team-agent/src/leader/lease.rs +195 -82
- package/crates/team-agent/src/leader/owner_bind.rs +55 -22
- package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
- package/crates/team-agent/src/leader/rediscover.rs +74 -25
- package/crates/team-agent/src/leader/start.rs +75 -54
- package/crates/team-agent/src/leader/takeover.rs +46 -11
- package/crates/team-agent/src/leader/tests/basics.rs +320 -167
- package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
- package/crates/team-agent/src/leader/tests/identity.rs +428 -356
- package/crates/team-agent/src/leader/tests/idle.rs +285 -254
- package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
- package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
- package/crates/team-agent/src/leader/tests/mod.rs +115 -99
- package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
- package/crates/team-agent/src/lib.rs +4 -4
- package/crates/team-agent/src/lifecycle/display.rs +7 -3
- package/crates/team-agent/src/lifecycle/launch.rs +6 -1
- package/crates/team-agent/src/lifecycle/mod.rs +9 -1
- package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
- package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
- package/crates/team-agent/src/lifecycle/tests.rs +2 -2
- package/crates/team-agent/src/main.rs +4 -4
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
- package/crates/team-agent/src/mcp_server/mod.rs +11 -2
- package/crates/team-agent/src/mcp_server/types.rs +14 -3
- package/crates/team-agent/src/mcp_server/wire.rs +230 -68
- package/crates/team-agent/src/messaging/delivery.rs +20 -18
- package/crates/team-agent/src/messaging/helpers.rs +25 -4
- package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
- package/crates/team-agent/src/messaging/mod.rs +2 -3
- package/crates/team-agent/src/messaging/selftest.rs +46 -26
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
- package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
- package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
- package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
- package/crates/team-agent/src/messaging/trust.rs +25 -2
- package/crates/team-agent/src/messaging/watchers.rs +37 -9
- package/crates/team-agent/src/model/enums.rs +65 -16
- package/crates/team-agent/src/model/ids.rs +12 -3
- package/crates/team-agent/src/model/paths.rs +28 -7
- package/crates/team-agent/src/model/permissions.rs +176 -33
- package/crates/team-agent/src/model/routing.rs +66 -20
- package/crates/team-agent/src/model/spec.rs +365 -69
- package/crates/team-agent/src/model/task_graph.rs +36 -9
- package/crates/team-agent/src/model/yaml/tests.rs +24 -6
- package/crates/team-agent/src/model/yaml.rs +7 -6
- package/crates/team-agent/src/packaging/install.rs +23 -9
- package/crates/team-agent/src/packaging/migrate.rs +5 -7
- package/crates/team-agent/src/packaging/mod.rs +9 -1
- package/crates/team-agent/src/packaging/repair.rs +13 -6
- package/crates/team-agent/src/packaging/tests.rs +63 -16
- package/crates/team-agent/src/packaging/types.rs +22 -7
- package/crates/team-agent/src/platform/argv.rs +4 -1
- package/crates/team-agent/src/platform/file_lock.rs +22 -8
- package/crates/team-agent/src/platform/process.rs +21 -22
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
- package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
- package/crates/team-agent/src/provider/classify.rs +127 -42
- package/crates/team-agent/src/provider/faults.rs +17 -5
- package/crates/team-agent/src/provider/helpers.rs +6 -5
- package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
- package/crates/team-agent/src/state/repository.rs +27 -14
- package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
- package/crates/team-agent/src/tmux_backend.rs +80 -44
- package/crates/team-agent/src/topology.rs +27 -20
- package/crates/team-agent/src/transport/test_support.rs +20 -23
- package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
- package/crates/team-agent/src/transport/tests/mod.rs +178 -187
- package/crates/team-agent/src/transport/tests/wire.rs +561 -525
- package/crates/team-agent/src/transport.rs +12 -17
- package/crates/team-agent/src/transport_factory.rs +29 -14
- package/package.json +4 -4
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
use std::path::Path;
|
|
4
4
|
|
|
5
|
-
use crate::model::ids::TeamKey;
|
|
6
5
|
use crate::message_store::MessageStore;
|
|
6
|
+
use crate::model::ids::TeamKey;
|
|
7
7
|
|
|
8
8
|
use super::helpers::next_run_id;
|
|
9
9
|
use super::{
|
|
@@ -42,11 +42,22 @@ pub fn run_comms_selftest(
|
|
|
42
42
|
.collect::<Vec<_>>()
|
|
43
43
|
})
|
|
44
44
|
.unwrap_or_default();
|
|
45
|
-
let receiver_status = if mismatches.is_empty() {
|
|
45
|
+
let receiver_status = if mismatches.is_empty() {
|
|
46
|
+
CheckStatus::Pass
|
|
47
|
+
} else {
|
|
48
|
+
CheckStatus::Fail
|
|
49
|
+
};
|
|
46
50
|
let calls = driver.provider_sdk_calls();
|
|
47
|
-
let provider_status = if calls.is_zero() {
|
|
51
|
+
let provider_status = if calls.is_zero() {
|
|
52
|
+
CheckStatus::Pass
|
|
53
|
+
} else {
|
|
54
|
+
CheckStatus::Fail
|
|
55
|
+
};
|
|
48
56
|
let contract_checks = run_contract_suite(workspace, team, &run_id);
|
|
49
|
-
let contract_status = if contract_checks
|
|
57
|
+
let contract_status = if contract_checks
|
|
58
|
+
.iter()
|
|
59
|
+
.all(|check| check.status == CheckStatus::Pass)
|
|
60
|
+
{
|
|
50
61
|
CheckStatus::Pass
|
|
51
62
|
} else {
|
|
52
63
|
CheckStatus::Fail
|
|
@@ -54,12 +65,17 @@ pub fn run_comms_selftest(
|
|
|
54
65
|
let receiver_binding = SelftestCheck {
|
|
55
66
|
status: receiver_status,
|
|
56
67
|
verifies: CheckKind::ReceiverBinding,
|
|
57
|
-
evidence: CheckEvidence::Binding {
|
|
68
|
+
evidence: CheckEvidence::Binding {
|
|
69
|
+
mismatches,
|
|
70
|
+
details: binding,
|
|
71
|
+
},
|
|
58
72
|
};
|
|
59
73
|
let contract_suite = SelftestCheck {
|
|
60
74
|
status: contract_status,
|
|
61
75
|
verifies: CheckKind::ContractSuite,
|
|
62
|
-
evidence: CheckEvidence::ContractSuite {
|
|
76
|
+
evidence: CheckEvidence::ContractSuite {
|
|
77
|
+
checks: contract_checks,
|
|
78
|
+
},
|
|
63
79
|
};
|
|
64
80
|
let provider_sdk_calls = SelftestCheck {
|
|
65
81
|
status: provider_status,
|
|
@@ -71,7 +87,11 @@ pub fn run_comms_selftest(
|
|
|
71
87
|
&& provider_sdk_calls.status == CheckStatus::Pass;
|
|
72
88
|
Ok(SelftestReport {
|
|
73
89
|
ok,
|
|
74
|
-
status: if ok {
|
|
90
|
+
status: if ok {
|
|
91
|
+
CheckStatus::Pass
|
|
92
|
+
} else {
|
|
93
|
+
CheckStatus::Fail
|
|
94
|
+
},
|
|
75
95
|
run_id,
|
|
76
96
|
scope: "binding_consistency".to_string(),
|
|
77
97
|
boundary: "messaging".to_string(),
|
|
@@ -86,15 +106,17 @@ fn run_contract_suite(
|
|
|
86
106
|
team: Option<&TeamKey>,
|
|
87
107
|
run_id: &str,
|
|
88
108
|
) -> Vec<ContractSuiteCheck> {
|
|
89
|
-
let scratch =
|
|
90
|
-
"ta-comms-contract-{run_id}-{}",
|
|
91
|
-
std::process::id()
|
|
92
|
-
));
|
|
109
|
+
let scratch =
|
|
110
|
+
std::env::temp_dir().join(format!("ta-comms-contract-{run_id}-{}", std::process::id()));
|
|
93
111
|
let _ = std::fs::remove_dir_all(&scratch);
|
|
94
112
|
let mut checks = Vec::new();
|
|
95
113
|
let mut scratch_store = match MessageStore::open(&scratch) {
|
|
96
114
|
Ok(store) => {
|
|
97
|
-
checks.push(contract_check(
|
|
115
|
+
checks.push(contract_check(
|
|
116
|
+
"message_store_schema",
|
|
117
|
+
CheckStatus::Pass,
|
|
118
|
+
None,
|
|
119
|
+
));
|
|
98
120
|
Some(store)
|
|
99
121
|
}
|
|
100
122
|
Err(error) => {
|
|
@@ -127,7 +149,11 @@ fn run_contract_suite(
|
|
|
127
149
|
let token = format!("[team-agent-token:{message_id}]");
|
|
128
150
|
checks.push(contract_check(
|
|
129
151
|
"message_token_shape",
|
|
130
|
-
if rendered.ends_with(&token) {
|
|
152
|
+
if rendered.ends_with(&token) {
|
|
153
|
+
CheckStatus::Pass
|
|
154
|
+
} else {
|
|
155
|
+
CheckStatus::Fail
|
|
156
|
+
},
|
|
131
157
|
(!rendered.ends_with(&token)).then(|| "rendered token suffix missing".to_string()),
|
|
132
158
|
));
|
|
133
159
|
}
|
|
@@ -195,10 +221,11 @@ fn run_contract_suite(
|
|
|
195
221
|
);
|
|
196
222
|
match outcome {
|
|
197
223
|
Ok(outcome) => {
|
|
198
|
-
let actual_owner = outcome
|
|
199
|
-
.message_id
|
|
200
|
-
|
|
201
|
-
|
|
224
|
+
let actual_owner = outcome.message_id.as_deref().and_then(|message_id| {
|
|
225
|
+
message_owner_team(store.db_path(), message_id)
|
|
226
|
+
.ok()
|
|
227
|
+
.flatten()
|
|
228
|
+
});
|
|
202
229
|
checks.push(contract_check(
|
|
203
230
|
"leader_projection_owner_team",
|
|
204
231
|
if actual_owner.as_deref() == Some(owner_team.as_str()) {
|
|
@@ -237,11 +264,7 @@ fn run_contract_suite(
|
|
|
237
264
|
checks
|
|
238
265
|
}
|
|
239
266
|
|
|
240
|
-
fn contract_check(
|
|
241
|
-
name: &str,
|
|
242
|
-
status: CheckStatus,
|
|
243
|
-
reason: Option<String>,
|
|
244
|
-
) -> ContractSuiteCheck {
|
|
267
|
+
fn contract_check(name: &str, status: CheckStatus, reason: Option<String>) -> ContractSuiteCheck {
|
|
245
268
|
ContractSuiteCheck {
|
|
246
269
|
name: name.to_string(),
|
|
247
270
|
status,
|
|
@@ -249,10 +272,7 @@ fn contract_check(
|
|
|
249
272
|
}
|
|
250
273
|
}
|
|
251
274
|
|
|
252
|
-
fn message_owner_team(
|
|
253
|
-
db_path: &Path,
|
|
254
|
-
message_id: &str,
|
|
255
|
-
) -> Result<Option<String>, MessagingError> {
|
|
275
|
+
fn message_owner_team(db_path: &Path, message_id: &str) -> Result<Option<String>, MessagingError> {
|
|
256
276
|
let conn = crate::db::schema::open_db(db_path)?;
|
|
257
277
|
let owner = conn.query_row(
|
|
258
278
|
"select owner_team_id from messages where message_id = ?1",
|
|
@@ -20,29 +20,46 @@ fn stuck_cancel_snapshot_delivered_message_ids_uses_golden_status_set() {
|
|
|
20
20
|
.unwrap();
|
|
21
21
|
let store = crate::message_store::MessageStore::open(&ws).unwrap();
|
|
22
22
|
// golden-delivered (must be IN delivered_message_ids): acknowledged + visible.
|
|
23
|
-
let m_ack = store
|
|
23
|
+
let m_ack = store
|
|
24
|
+
.create_message(None, "leader", "w1", "ack-me", None, true, Some("teamX"))
|
|
25
|
+
.unwrap();
|
|
24
26
|
store.mark(&m_ack, "acknowledged", None).unwrap();
|
|
25
|
-
let m_vis = store
|
|
27
|
+
let m_vis = store
|
|
28
|
+
.create_message(None, "leader", "w1", "vis", None, true, Some("teamX"))
|
|
29
|
+
.unwrap();
|
|
26
30
|
store.mark(&m_vis, "visible", None).unwrap();
|
|
27
31
|
// NOT golden-delivered (must be EXCLUDED): injected.
|
|
28
|
-
let m_inj = store
|
|
32
|
+
let m_inj = store
|
|
33
|
+
.create_message(None, "leader", "w1", "inj", None, true, Some("teamX"))
|
|
34
|
+
.unwrap();
|
|
29
35
|
store.mark(&m_inj, "injected", None).unwrap();
|
|
30
36
|
drop(store);
|
|
31
37
|
let _ = stuck_cancel(&ws, "w1", None, "leader").unwrap();
|
|
32
38
|
let state = crate::state::persist::load_runtime_state(&ws).unwrap();
|
|
33
|
-
let snapshot =
|
|
34
|
-
|
|
39
|
+
let snapshot =
|
|
40
|
+
&state["coordinator"]["suppressed_idle_alerts"]["teamX"]["w1"]["stuck"]["snapshot"];
|
|
41
|
+
assert!(
|
|
42
|
+
snapshot.get("assigned_task_ids").is_some(),
|
|
43
|
+
"F1: snapshot must carry assigned_task_ids; got {snapshot}"
|
|
44
|
+
);
|
|
35
45
|
let delivered = snapshot
|
|
36
46
|
.get("delivered_message_ids")
|
|
37
47
|
.and_then(serde_json::Value::as_array)
|
|
38
|
-
.map(|a|
|
|
48
|
+
.map(|a| {
|
|
49
|
+
a.iter()
|
|
50
|
+
.filter_map(|v| v.as_str().map(str::to_string))
|
|
51
|
+
.collect::<Vec<_>>()
|
|
52
|
+
})
|
|
39
53
|
.unwrap_or_else(|| panic!("F1: snapshot must carry delivered_message_ids; got {snapshot}"));
|
|
40
54
|
assert!(
|
|
41
55
|
delivered.contains(&m_ack),
|
|
42
56
|
"F1: golden _DELIVERED_MESSAGE_STATUSES includes 'acknowledged' — the acknowledged message must be \
|
|
43
57
|
in delivered_message_ids; the Rust set omits it. got {delivered:?}"
|
|
44
58
|
);
|
|
45
|
-
assert!(
|
|
59
|
+
assert!(
|
|
60
|
+
delivered.contains(&m_vis),
|
|
61
|
+
"F1: 'visible' is delivered (golden); got {delivered:?}"
|
|
62
|
+
);
|
|
46
63
|
assert!(
|
|
47
64
|
!delivered.contains(&m_inj),
|
|
48
65
|
"F1: 'injected' is NOT in golden _DELIVERED_MESSAGE_STATUSES — it must be EXCLUDED; the Rust set \
|
|
@@ -50,7 +67,10 @@ fn stuck_cancel_snapshot_delivered_message_ids_uses_golden_status_set() {
|
|
|
50
67
|
);
|
|
51
68
|
let mut sorted = delivered.clone();
|
|
52
69
|
sorted.sort();
|
|
53
|
-
assert_eq!(
|
|
70
|
+
assert_eq!(
|
|
71
|
+
delivered, sorted,
|
|
72
|
+
"F1: delivered_message_ids must be sorted (golden sorts); got {delivered:?}"
|
|
73
|
+
);
|
|
54
74
|
}
|
|
55
75
|
// ── collect --result-file INGEST SEMANTIC (golden results.py:58-73) ──────────────────────────────
|
|
56
76
|
// Golden `collect(result_file=…)` INDEPENDENTLY INGESTS a standalone result: a valid result_envelope_v1
|
|
@@ -77,7 +97,11 @@ fn collect_result_file_ingests_valid_known_task_envelope_into_results() {
|
|
|
77
97
|
)
|
|
78
98
|
.unwrap();
|
|
79
99
|
let spec = crate::compiler::compile_team(&team).expect("compile collect team");
|
|
80
|
-
std::fs::write(
|
|
100
|
+
std::fs::write(
|
|
101
|
+
team.join("team.spec.yaml"),
|
|
102
|
+
crate::model::yaml::dumps(&spec),
|
|
103
|
+
)
|
|
104
|
+
.unwrap();
|
|
81
105
|
// seed runtime state with a KNOWN task "task-1" so the collection loop accepts the ingested result.
|
|
82
106
|
crate::state::persist::save_runtime_state(
|
|
83
107
|
&team,
|
|
@@ -100,8 +124,14 @@ fn collect_result_file_ingests_valid_known_task_envelope_into_results() {
|
|
|
100
124
|
let out = collect(&team, Some(rf.as_path()), false)
|
|
101
125
|
.expect("collect with a valid --result-file must not error");
|
|
102
126
|
// golden: a valid standalone envelope is ingested + collected, ok=true (NOT a silent exit-1).
|
|
103
|
-
assert_eq!(
|
|
104
|
-
|
|
127
|
+
assert_eq!(
|
|
128
|
+
out["ok"],
|
|
129
|
+
serde_json::json!(true),
|
|
130
|
+
"valid --result-file collect must be ok:true; got {out}"
|
|
131
|
+
);
|
|
132
|
+
let collected = out["collected_results"]
|
|
133
|
+
.as_array()
|
|
134
|
+
.expect("collected_results array");
|
|
105
135
|
assert_eq!(
|
|
106
136
|
collected.len(),
|
|
107
137
|
1,
|
|
@@ -117,6 +147,11 @@ fn collect_result_file_ingests_valid_known_task_envelope_into_results() {
|
|
|
117
147
|
"the ingested result must persist in the results table (counts.total=1); a no-op ingest yields 0. got {}",
|
|
118
148
|
out["results"]
|
|
119
149
|
);
|
|
120
|
-
assert_eq!(
|
|
150
|
+
assert_eq!(
|
|
151
|
+
out["results"]["collected"],
|
|
152
|
+
serde_json::json!(1),
|
|
153
|
+
"the ingested result must be marked collected; got {}",
|
|
154
|
+
out["results"]
|
|
155
|
+
);
|
|
121
156
|
let _ = std::fs::remove_dir_all(&team);
|
|
122
157
|
}
|
|
@@ -1454,13 +1454,33 @@ impl Transport for ReadbackMissingTransport {
|
|
|
1454
1454
|
fn kind(&self) -> BackendKind {
|
|
1455
1455
|
BackendKind::Tmux
|
|
1456
1456
|
}
|
|
1457
|
-
fn spawn_first(
|
|
1457
|
+
fn spawn_first(
|
|
1458
|
+
&self,
|
|
1459
|
+
_: &SessionName,
|
|
1460
|
+
_: &WindowName,
|
|
1461
|
+
_: &[String],
|
|
1462
|
+
_: &Path,
|
|
1463
|
+
_: &BTreeMap<String, String>,
|
|
1464
|
+
) -> Result<SpawnResult, TransportError> {
|
|
1458
1465
|
unimplemented!("not reached in delivery")
|
|
1459
1466
|
}
|
|
1460
|
-
fn spawn_into(
|
|
1467
|
+
fn spawn_into(
|
|
1468
|
+
&self,
|
|
1469
|
+
_: &SessionName,
|
|
1470
|
+
_: &WindowName,
|
|
1471
|
+
_: &[String],
|
|
1472
|
+
_: &Path,
|
|
1473
|
+
_: &BTreeMap<String, String>,
|
|
1474
|
+
) -> Result<SpawnResult, TransportError> {
|
|
1461
1475
|
unimplemented!("not reached in delivery")
|
|
1462
1476
|
}
|
|
1463
|
-
fn inject(
|
|
1477
|
+
fn inject(
|
|
1478
|
+
&self,
|
|
1479
|
+
_: &Target,
|
|
1480
|
+
_: &InjectPayload,
|
|
1481
|
+
_: Key,
|
|
1482
|
+
_: bool,
|
|
1483
|
+
) -> Result<InjectReport, TransportError> {
|
|
1464
1484
|
Ok(InjectReport {
|
|
1465
1485
|
stage_reached: crate::transport::InjectStage::Submit,
|
|
1466
1486
|
// submit verified, but the token never appeared in the pane → readback missing.
|
|
@@ -1477,7 +1497,10 @@ impl Transport for ReadbackMissingTransport {
|
|
|
1477
1497
|
Ok(())
|
|
1478
1498
|
}
|
|
1479
1499
|
fn capture(&self, _: &Target, range: CaptureRange) -> Result<CapturedText, TransportError> {
|
|
1480
|
-
Ok(CapturedText {
|
|
1500
|
+
Ok(CapturedText {
|
|
1501
|
+
text: String::new(),
|
|
1502
|
+
range,
|
|
1503
|
+
})
|
|
1481
1504
|
}
|
|
1482
1505
|
fn query(&self, _: &Target, _: PaneField) -> Result<Option<String>, TransportError> {
|
|
1483
1506
|
Ok(None)
|
|
@@ -1494,7 +1517,12 @@ impl Transport for ReadbackMissingTransport {
|
|
|
1494
1517
|
fn list_windows(&self, _: &SessionName) -> Result<Vec<WindowName>, TransportError> {
|
|
1495
1518
|
Ok(Vec::new())
|
|
1496
1519
|
}
|
|
1497
|
-
fn set_session_env(
|
|
1520
|
+
fn set_session_env(
|
|
1521
|
+
&self,
|
|
1522
|
+
_: &SessionName,
|
|
1523
|
+
_: &str,
|
|
1524
|
+
_: &str,
|
|
1525
|
+
) -> Result<SetEnvOutcome, TransportError> {
|
|
1498
1526
|
Ok(SetEnvOutcome::Applied)
|
|
1499
1527
|
}
|
|
1500
1528
|
fn kill_session(&self, _: &SessionName) -> Result<(), TransportError> {
|
|
@@ -2848,8 +2876,8 @@ fn gate054_rebind_required_status_refuses_leader_delivery_without_cross_server_p
|
|
|
2848
2876
|
// The transport we pass is the "main"/default server backend which does
|
|
2849
2877
|
// have a live `%1`. If the fix regresses and delivery falls back cross-
|
|
2850
2878
|
// server by pane id, it would inject here.
|
|
2851
|
-
let default_backend =
|
|
2852
|
-
.with_targets(vec![pane_info("%1", "team-main", "leader")]);
|
|
2879
|
+
let default_backend =
|
|
2880
|
+
OfflineTransport::new().with_targets(vec![pane_info("%1", "team-main", "leader")]);
|
|
2853
2881
|
|
|
2854
2882
|
let out = deliver_pending_message(&ws, &store, &default_backend, &message_id, &log, &state)
|
|
2855
2883
|
.expect("status guard must be a business refusal, not panic");
|
|
@@ -2904,8 +2932,11 @@ fn gate054_attached_status_still_delivers_when_pane_is_live() {
|
|
|
2904
2932
|
let message_id = store
|
|
2905
2933
|
.create_message(None, "w1", "leader", "hi", None, false, None)
|
|
2906
2934
|
.unwrap();
|
|
2907
|
-
let transport = OfflineTransport::new()
|
|
2908
|
-
|
|
2935
|
+
let transport = OfflineTransport::new().with_targets(vec![pane_info(
|
|
2936
|
+
"%leader",
|
|
2937
|
+
"team-gate054ok",
|
|
2938
|
+
"leader",
|
|
2939
|
+
)]);
|
|
2909
2940
|
|
|
2910
2941
|
let out = deliver_pending_message(&ws, &store, &transport, &message_id, &log, &state)
|
|
2911
2942
|
.expect("attached leader must still deliver");
|
|
@@ -2948,7 +2979,15 @@ fn gate054_fallback_pane_socket_recorded_does_not_cross_to_default_server() {
|
|
|
2948
2979
|
});
|
|
2949
2980
|
crate::state::persist::save_runtime_state(&ws, &state).unwrap();
|
|
2950
2981
|
let message_id = store
|
|
2951
|
-
.create_message(
|
|
2982
|
+
.create_message(
|
|
2983
|
+
None,
|
|
2984
|
+
"coordinator",
|
|
2985
|
+
"leader",
|
|
2986
|
+
"fallback payload",
|
|
2987
|
+
None,
|
|
2988
|
+
false,
|
|
2989
|
+
None,
|
|
2990
|
+
)
|
|
2952
2991
|
.unwrap();
|
|
2953
2992
|
|
|
2954
2993
|
let outcome = deliver_to_leader_fallback_pane(
|
|
@@ -2978,12 +3017,10 @@ fn gate054_fallback_pane_socket_recorded_does_not_cross_to_default_server() {
|
|
|
2978
3017
|
// Audit noise: fallback_pane_attempt + fallback_pane_failed with the
|
|
2979
3018
|
// recorded socket_bound=true forensic field.
|
|
2980
3019
|
let events = log.tail(0).unwrap();
|
|
2981
|
-
let attempt = events
|
|
2982
|
-
.
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
== Some("leader_receiver.fallback_pane_attempt")
|
|
2986
|
-
});
|
|
3020
|
+
let attempt = events.iter().find(|e| {
|
|
3021
|
+
e.get("event").and_then(serde_json::Value::as_str)
|
|
3022
|
+
== Some("leader_receiver.fallback_pane_attempt")
|
|
3023
|
+
});
|
|
2987
3024
|
assert!(
|
|
2988
3025
|
attempt.is_some(),
|
|
2989
3026
|
"fallback_pane_attempt audit must fire even on socket-bounded refusal; events={events:?}"
|
|
@@ -3051,10 +3088,9 @@ fn gate054_rebind_replay_requeues_failed_leader_message_on_attach() {
|
|
|
3051
3088
|
// it must flip the blocked leader row back to accepted.
|
|
3052
3089
|
let team = TeamKey::new(team_id);
|
|
3053
3090
|
let new_pane = PaneId::new("%1"); // new leader window on the private socket
|
|
3054
|
-
let notices =
|
|
3055
|
-
&ws, &store, &log, &team, &new_pane
|
|
3056
|
-
|
|
3057
|
-
.unwrap();
|
|
3091
|
+
let notices =
|
|
3092
|
+
crate::messaging::requeue_delivery_exhausted_watchers(&ws, &store, &log, &team, &new_pane)
|
|
3093
|
+
.unwrap();
|
|
3058
3094
|
assert!(
|
|
3059
3095
|
notices.is_empty(),
|
|
3060
3096
|
"no exhausted watchers seeded → no watcher notices; only the message row should have been requeued"
|
|
@@ -3159,8 +3195,7 @@ fn gate054_status_surfaces_pending_leader_notifications() {
|
|
|
3159
3195
|
assert!(entry
|
|
3160
3196
|
.get("action")
|
|
3161
3197
|
.and_then(serde_json::Value::as_str)
|
|
3162
|
-
.is_some_and(|action| action.contains("attach-leader")
|
|
3163
|
-
|| action.contains("takeover")));
|
|
3198
|
+
.is_some_and(|action| action.contains("attach-leader") || action.contains("takeover")));
|
|
3164
3199
|
}
|
|
3165
3200
|
|
|
3166
3201
|
#[test]
|