@team-agent/installer 0.5.60 → 0.5.62
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 +49 -3
- package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
- package/crates/team-agent/src/cli/diagnose.rs +184 -0
- package/crates/team-agent/src/cli/emit.rs +104 -21
- package/crates/team-agent/src/cli/helpers.rs +1 -0
- package/crates/team-agent/src/cli/leader.rs +14 -2
- package/crates/team-agent/src/cli/leaders.rs +2 -0
- package/crates/team-agent/src/cli/mod.rs +296 -8
- package/crates/team-agent/src/cli/named_address.rs +26 -2
- package/crates/team-agent/src/cli/profile.rs +2 -1
- package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
- package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
- package/crates/team-agent/src/cli/send/persist.rs +1 -0
- package/crates/team-agent/src/cli/send/presentation.rs +3 -0
- package/crates/team-agent/src/cli/send/resolve.rs +206 -1
- package/crates/team-agent/src/cli/send.rs +2 -1
- package/crates/team-agent/src/cli/spec.rs +5 -1
- package/crates/team-agent/src/cli/status.rs +2 -1
- package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
- package/crates/team-agent/src/cli/status_port/format.rs +1 -0
- package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
- package/crates/team-agent/src/cli/status_port/store.rs +7 -3
- package/crates/team-agent/src/cli/tests/named_address.rs +65 -0
- package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
- package/crates/team-agent/src/cli/types.rs +36 -2
- package/crates/team-agent/src/communication_mode/mod.rs +54 -0
- package/crates/team-agent/src/compiler/tests.rs +5 -5
- package/crates/team-agent/src/compiler.rs +54 -1
- package/crates/team-agent/src/conpty/backend.rs +1 -0
- package/crates/team-agent/src/conpty/mod.rs +1 -0
- package/crates/team-agent/src/coordinator/backoff.rs +5 -8
- package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
- package/crates/team-agent/src/coordinator/health.rs +1 -0
- package/crates/team-agent/src/coordinator/mod.rs +2 -2
- package/crates/team-agent/src/coordinator/orphan.rs +4 -116
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +6 -5
- package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
- package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +0 -9
- package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
- package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -42
- package/crates/team-agent/src/coordinator/tick.rs +24 -28
- package/crates/team-agent/src/coordinator/types.rs +3 -176
- package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
- package/crates/team-agent/src/db/message_store.rs +47 -2
- package/crates/team-agent/src/db/migration.rs +5 -4
- package/crates/team-agent/src/db/mod.rs +1 -0
- package/crates/team-agent/src/db/schema.rs +1 -0
- package/crates/team-agent/src/diagnose/comms.rs +2 -1
- package/crates/team-agent/src/diagnose/mod.rs +1 -0
- package/crates/team-agent/src/diagnose/orphans.rs +1 -0
- package/crates/team-agent/src/fake_worker.rs +21 -1
- package/crates/team-agent/src/layout/manager.rs +3 -7
- package/crates/team-agent/src/layout/mod.rs +2 -2
- package/crates/team-agent/src/layout/overlay.rs +6 -1
- package/crates/team-agent/src/layout/placement.rs +1 -0
- package/crates/team-agent/src/layout/recovery.rs +3 -0
- package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
- package/crates/team-agent/src/layout/sessions.rs +8 -1
- package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
- package/crates/team-agent/src/layout/worker_env.rs +3 -0
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/helpers.rs +1 -0
- package/crates/team-agent/src/leader/incident.rs +1 -0
- package/crates/team-agent/src/leader/lease.rs +27 -22
- package/crates/team-agent/src/leader/mod.rs +2 -3
- package/crates/team-agent/src/leader/owner_bind.rs +13 -3
- package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
- package/crates/team-agent/src/leader/rediscover.rs +4 -17
- package/crates/team-agent/src/leader/registry.rs +3 -15
- package/crates/team-agent/src/leader/start.rs +1072 -186
- package/crates/team-agent/src/leader/takeover.rs +10 -232
- package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
- package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
- package/crates/team-agent/src/leader/tests/identity.rs +71 -59
- package/crates/team-agent/src/leader/tests/identity_session_names.rs +42 -0
- package/crates/team-agent/src/leader/tests/idle.rs +0 -193
- package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
- package/crates/team-agent/src/leader/tests/mod.rs +4 -41
- package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
- package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
- package/crates/team-agent/src/leader/types.rs +1 -28
- package/crates/team-agent/src/lib.rs +7 -0
- package/crates/team-agent/src/lifecycle/display.rs +7 -1
- package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +1 -1
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -28
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch.rs +11 -12
- package/crates/team-agent/src/lifecycle/mod.rs +2 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +4 -4
- package/crates/team-agent/src/lifecycle/restart/common.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
- package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
- package/crates/team-agent/src/lifecycle/restart.rs +13 -11
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
- package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
- package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
- package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
- package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
- package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
- package/crates/team-agent/src/lifecycle/tests.rs +59 -0
- package/crates/team-agent/src/lifecycle/types.rs +0 -9
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +29 -11
- package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
- package/crates/team-agent/src/mcp_server/mod.rs +1 -0
- package/crates/team-agent/src/mcp_server/normalize.rs +5 -3
- package/crates/team-agent/src/mcp_server/tests/send.rs +26 -0
- package/crates/team-agent/src/mcp_server/tests/wire.rs +1 -1
- package/crates/team-agent/src/mcp_server/tools.rs +101 -66
- package/crates/team-agent/src/mcp_server/types.rs +6 -18
- package/crates/team-agent/src/mcp_server/wire.rs +32 -26
- package/crates/team-agent/src/messaging/activity.rs +1 -0
- package/crates/team-agent/src/messaging/address.rs +1 -0
- package/crates/team-agent/src/messaging/delivery.rs +109 -41
- package/crates/team-agent/src/messaging/helpers.rs +2 -1
- package/crates/team-agent/src/messaging/leader_channel.rs +34 -10
- package/crates/team-agent/src/messaging/leader_receiver.rs +42 -24
- package/crates/team-agent/src/messaging/mod.rs +10 -14
- package/crates/team-agent/src/messaging/peers.rs +2 -0
- package/crates/team-agent/src/messaging/persist.rs +3 -1
- package/crates/team-agent/src/messaging/presentation.rs +111 -1
- package/crates/team-agent/src/messaging/results.rs +166 -17
- package/crates/team-agent/src/messaging/scheduler.rs +1 -0
- package/crates/team-agent/src/messaging/selftest.rs +1 -1
- package/crates/team-agent/src/messaging/send.rs +135 -89
- package/crates/team-agent/src/messaging/tests/leader_channel.rs +6 -6
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +187 -0
- package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +13 -6
- package/crates/team-agent/src/messaging/types.rs +7 -2
- package/crates/team-agent/src/messaging/watchers.rs +24 -13
- package/crates/team-agent/src/model/enums.rs +1 -0
- package/crates/team-agent/src/model/errors.rs +1 -0
- package/crates/team-agent/src/model/ids.rs +1 -0
- package/crates/team-agent/src/model/mod.rs +2 -0
- package/crates/team-agent/src/model/name_similarity.rs +1 -0
- package/crates/team-agent/src/model/pane_authority_refusal.rs +359 -0
- package/crates/team-agent/src/model/paths.rs +1 -0
- package/crates/team-agent/src/model/permissions.rs +3 -2
- package/crates/team-agent/src/model/routing.rs +1 -0
- package/crates/team-agent/src/model/spec.rs +17 -0
- package/crates/team-agent/src/model/task_graph.rs +1 -0
- package/crates/team-agent/src/model/yaml/tests.rs +1 -0
- package/crates/team-agent/src/model/yaml.rs +1 -0
- package/crates/team-agent/src/packaging/install.rs +9 -147
- package/crates/team-agent/src/packaging/migrate.rs +2 -0
- package/crates/team-agent/src/packaging/mod.rs +1 -0
- package/crates/team-agent/src/packaging/repair.rs +2 -0
- package/crates/team-agent/src/packaging/tests.rs +29 -218
- package/crates/team-agent/src/packaging/types.rs +8 -15
- package/crates/team-agent/src/platform/argv.rs +4 -0
- package/crates/team-agent/src/platform/errors.rs +10 -1
- package/crates/team-agent/src/platform/file_lock.rs +4 -157
- package/crates/team-agent/src/platform/mod.rs +0 -61
- package/crates/team-agent/src/platform/process.rs +1 -0
- package/crates/team-agent/src/provider/adapter.rs +1 -0
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
- package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
- package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
- package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
- package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
- package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
- package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
- package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
- package/crates/team-agent/src/provider/classify.rs +1 -0
- package/crates/team-agent/src/provider/faults.rs +1 -26
- package/crates/team-agent/src/provider/helpers.rs +1 -0
- package/crates/team-agent/src/provider/mod.rs +1 -1
- package/crates/team-agent/src/provider/session/capture.rs +41 -22
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
- package/crates/team-agent/src/provider/session/mod.rs +2 -4
- package/crates/team-agent/src/provider/session/resume.rs +2 -209
- package/crates/team-agent/src/provider/session_scan/claude.rs +93 -3
- package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
- package/crates/team-agent/src/provider/session_scan/common.rs +24 -7
- package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
- package/crates/team-agent/src/provider/session_scan.rs +6 -42
- package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
- package/crates/team-agent/src/provider/types.rs +1 -0
- package/crates/team-agent/src/provider/wire.rs +1 -0
- package/crates/team-agent/src/state/identity.rs +16 -5
- package/crates/team-agent/src/state/identity_keys.rs +1 -0
- package/crates/team-agent/src/state/mod.rs +2 -0
- package/crates/team-agent/src/state/owner_gate.rs +5 -0
- package/crates/team-agent/src/state/ownership.rs +10 -4
- package/crates/team-agent/src/state/paths.rs +12 -0
- package/crates/team-agent/src/state/persist.rs +9 -3
- package/crates/team-agent/src/state/projection.rs +15 -2
- package/crates/team-agent/src/state/repository/intent.rs +1 -0
- package/crates/team-agent/src/state/repository/tests.rs +1 -0
- package/crates/team-agent/src/state/repository.rs +7 -0
- package/crates/team-agent/src/state/selector.rs +1 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
- package/crates/team-agent/src/tmux_backend.rs +63 -0
- package/crates/team-agent/src/topology.rs +3 -0
- package/crates/team-agent/src/transport.rs +15 -0
- package/package.json +4 -4
- package/skills/team-agent/command-coverage.json +379 -0
- package/crates/team-agent/src/leader/inject.rs +0 -33
- package/crates/team-agent/src/provider/command.rs +0 -80
|
@@ -1,179 +1,5 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
|
-
// ═════════════════════════════════════════════════════════════════════════
|
|
4
|
-
// GROUP E — abnormal_track classify / dedup (abnormal_track.py) — RED
|
|
5
|
-
// provider-neutral:断言 ZERO adapter_for 调用(MUST-NOT-13)。
|
|
6
|
-
// ASSUMPTION:传入 `records` 是 provider reader 已产出的结构化 fault facts
|
|
7
|
-
// ({signature, turn_id?, kind?, raw}),即 read_fault_facts 的输出形;abnormal_track
|
|
8
|
-
// 只对它做 catch-bias + (signature, turn_id|fingerprint) 去重(error_lists 来自注入
|
|
9
|
-
// registry,绝不经 adapter_for 触碰 provider client)。
|
|
10
|
-
// ═════════════════════════════════════════════════════════════════════════
|
|
11
|
-
|
|
12
|
-
#[test]
|
|
13
|
-
fn abnormal_default_notify_for_structured_fault() {
|
|
14
|
-
// abnormal_track.py:204 — C9 catch-bias: 无黑白名单命中 → notify_default.
|
|
15
|
-
let reg = MockRegistry::new(&[], &[]);
|
|
16
|
-
let records = vec![serde_json::json!({"raw": "session line"})];
|
|
17
|
-
let out = process_abnormal_records(
|
|
18
|
-
&records,
|
|
19
|
-
®,
|
|
20
|
-
Provider::Codex,
|
|
21
|
-
&AbnormalNotificationState::default(),
|
|
22
|
-
)
|
|
23
|
-
.unwrap();
|
|
24
|
-
assert_eq!(out.notifications.len(), 1);
|
|
25
|
-
assert_eq!(
|
|
26
|
-
out.notifications[0].decision,
|
|
27
|
-
AbnormalDecision::NotifyDefault
|
|
28
|
-
);
|
|
29
|
-
// §84:绝不触碰 provider adapter client。
|
|
30
|
-
assert_eq!(
|
|
31
|
-
reg.adapter_calls.get(),
|
|
32
|
-
0,
|
|
33
|
-
"MUST NOT call adapter_for (provider client)"
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
#[test]
|
|
38
|
-
fn abnormal_blacklist_hit_yields_notify_blacklist() {
|
|
39
|
-
// abnormal_track.py:203 — blacklist 命中 → notify_blacklist.
|
|
40
|
-
let reg = MockRegistry::new(&[], &["boom"]);
|
|
41
|
-
let records = vec![serde_json::json!({"raw": "kaboom: boom happened"})];
|
|
42
|
-
let out = process_abnormal_records(
|
|
43
|
-
&records,
|
|
44
|
-
®,
|
|
45
|
-
Provider::Codex,
|
|
46
|
-
&AbnormalNotificationState::default(),
|
|
47
|
-
)
|
|
48
|
-
.unwrap();
|
|
49
|
-
assert_eq!(out.notifications.len(), 1);
|
|
50
|
-
assert_eq!(
|
|
51
|
-
out.notifications[0].decision,
|
|
52
|
-
AbnormalDecision::NotifyBlacklist
|
|
53
|
-
);
|
|
54
|
-
assert_eq!(reg.adapter_calls.get(), 0);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
#[test]
|
|
58
|
-
fn abnormal_whitelist_beats_blacklist_and_skips() {
|
|
59
|
-
// abnormal_track.py:200-201 — whitelist > blacklist > default. 同命中 → skip, 0 notify.
|
|
60
|
-
let reg = MockRegistry::new(&["boom"], &["boom"]);
|
|
61
|
-
let records = vec![serde_json::json!({"raw": "boom transient"})];
|
|
62
|
-
let out = process_abnormal_records(
|
|
63
|
-
&records,
|
|
64
|
-
®,
|
|
65
|
-
Provider::Codex,
|
|
66
|
-
&AbnormalNotificationState::default(),
|
|
67
|
-
)
|
|
68
|
-
.unwrap();
|
|
69
|
-
assert_eq!(
|
|
70
|
-
out.notifications.len(),
|
|
71
|
-
0,
|
|
72
|
-
"whitelist wins → skip (probe confirmed)"
|
|
73
|
-
);
|
|
74
|
-
assert_eq!(reg.adapter_calls.get(), 0);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
#[test]
|
|
78
|
-
fn abnormal_approval_kind_maps_to_blocked_on_human() {
|
|
79
|
-
// abnormal_track.py:74 — kind=="approval" → state blocked_on_human, else abnormal.
|
|
80
|
-
let reg = MockRegistry::new(&[], &[]);
|
|
81
|
-
let records = vec![serde_json::json!({"raw": "approve?", "kind": "approval"})];
|
|
82
|
-
let out = process_abnormal_records(
|
|
83
|
-
&records,
|
|
84
|
-
®,
|
|
85
|
-
Provider::Codex,
|
|
86
|
-
&AbnormalNotificationState::default(),
|
|
87
|
-
)
|
|
88
|
-
.unwrap();
|
|
89
|
-
assert_eq!(out.notifications.len(), 1);
|
|
90
|
-
assert_eq!(out.notifications[0].state, TurnState::BlockedOnHuman);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
#[test]
|
|
94
|
-
fn abnormal_same_turn_id_folds_to_one_notification() {
|
|
95
|
-
// abnormal_track.py:60-68 — C8 dedup: same (signature, turn_id) → one notify.
|
|
96
|
-
let reg = MockRegistry::new(&[], &[]);
|
|
97
|
-
let records = vec![
|
|
98
|
-
serde_json::json!({"signature": "sig_a", "turn_id": "t1", "raw": "boom"}),
|
|
99
|
-
serde_json::json!({"signature": "sig_a", "turn_id": "t1", "raw": "boom"}),
|
|
100
|
-
];
|
|
101
|
-
let out = process_abnormal_records(
|
|
102
|
-
&records,
|
|
103
|
-
®,
|
|
104
|
-
Provider::Codex,
|
|
105
|
-
&AbnormalNotificationState::default(),
|
|
106
|
-
)
|
|
107
|
-
.unwrap();
|
|
108
|
-
assert_eq!(
|
|
109
|
-
out.notifications.len(),
|
|
110
|
-
1,
|
|
111
|
-
"retry-loop in SAME turn folds (probe: 1)"
|
|
112
|
-
);
|
|
113
|
-
// seen 状态回存,key = "sig_a\0t1"。
|
|
114
|
-
assert!(out.notification_state.seen.contains("sig_a\u{0}t1"));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
#[test]
|
|
118
|
-
fn abnormal_missing_turn_id_distinct_faults_each_notify() {
|
|
119
|
-
// abnormal_track.py:64 — turn_id None → per-record content fingerprint bucket:
|
|
120
|
-
// 不同 fault 各自 notify(绝不折叠进全局桶,probe: 2)。
|
|
121
|
-
let reg = MockRegistry::new(&[], &[]);
|
|
122
|
-
let records = vec![
|
|
123
|
-
serde_json::json!({"signature": "sig_b", "raw": "alpha"}),
|
|
124
|
-
serde_json::json!({"signature": "sig_b", "raw": "beta"}),
|
|
125
|
-
];
|
|
126
|
-
let out = process_abnormal_records(
|
|
127
|
-
&records,
|
|
128
|
-
®,
|
|
129
|
-
Provider::Codex,
|
|
130
|
-
&AbnormalNotificationState::default(),
|
|
131
|
-
)
|
|
132
|
-
.unwrap();
|
|
133
|
-
assert_eq!(
|
|
134
|
-
out.notifications.len(),
|
|
135
|
-
2,
|
|
136
|
-
"distinct raw → 2 notifies (probe)"
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
#[test]
|
|
141
|
-
fn abnormal_missing_turn_id_identical_faults_fold() {
|
|
142
|
-
// abnormal_track.py:64-68 — identical raw + missing turn_id → same fingerprint → fold (probe: 1).
|
|
143
|
-
let reg = MockRegistry::new(&[], &[]);
|
|
144
|
-
let records = vec![
|
|
145
|
-
serde_json::json!({"signature": "sig_c", "raw": "same"}),
|
|
146
|
-
serde_json::json!({"signature": "sig_c", "raw": "same"}),
|
|
147
|
-
];
|
|
148
|
-
let out = process_abnormal_records(
|
|
149
|
-
&records,
|
|
150
|
-
®,
|
|
151
|
-
Provider::Codex,
|
|
152
|
-
&AbnormalNotificationState::default(),
|
|
153
|
-
)
|
|
154
|
-
.unwrap();
|
|
155
|
-
assert_eq!(
|
|
156
|
-
out.notifications.len(),
|
|
157
|
-
1,
|
|
158
|
-
"identical raw folds even w/o turn_id (probe)"
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
#[test]
|
|
163
|
-
fn abnormal_preseeded_seen_suppresses_duplicate_across_calls() {
|
|
164
|
-
// abnormal_track.py:67 — key already in seen → skip notification (cross-tick dedup).
|
|
165
|
-
let reg = MockRegistry::new(&[], &[]);
|
|
166
|
-
let mut state = AbnormalNotificationState::default();
|
|
167
|
-
state.seen.insert("sig_a\u{0}t1".to_string());
|
|
168
|
-
let records = vec![serde_json::json!({"signature": "sig_a", "turn_id": "t1", "raw": "boom"})];
|
|
169
|
-
let out = process_abnormal_records(&records, ®, Provider::Codex, &state).unwrap();
|
|
170
|
-
assert_eq!(
|
|
171
|
-
out.notifications.len(),
|
|
172
|
-
0,
|
|
173
|
-
"pre-seeded seen suppresses (probe: 0)"
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
3
|
#[test]
|
|
178
4
|
fn abnormal_explicit_error_classifier_boundaries_stay_stable() {
|
|
179
5
|
let claude_api = line(serde_json::json!({
|
|
@@ -447,7 +447,6 @@ impl Drop for RecoveryCase {
|
|
|
447
447
|
#[derive(Clone)]
|
|
448
448
|
struct CounterState {
|
|
449
449
|
adapter_calls: Arc<AtomicU32>,
|
|
450
|
-
error_list_calls: Arc<AtomicU32>,
|
|
451
450
|
}
|
|
452
451
|
|
|
453
452
|
struct CountingRegistry {
|
|
@@ -459,7 +458,6 @@ impl CountingRegistry {
|
|
|
459
458
|
Self {
|
|
460
459
|
counters: CounterState {
|
|
461
460
|
adapter_calls: Arc::new(AtomicU32::new(0)),
|
|
462
|
-
error_list_calls: Arc::new(AtomicU32::new(0)),
|
|
463
461
|
},
|
|
464
462
|
}
|
|
465
463
|
}
|
|
@@ -474,13 +472,6 @@ impl ProviderRegistry for CountingRegistry {
|
|
|
474
472
|
self.counters.adapter_calls.fetch_add(1, Ordering::SeqCst);
|
|
475
473
|
crate::provider::get_adapter(provider)
|
|
476
474
|
}
|
|
477
|
-
|
|
478
|
-
fn error_lists(&self, _provider: Provider) -> ErrorLists {
|
|
479
|
-
self.counters
|
|
480
|
-
.error_list_calls
|
|
481
|
-
.fetch_add(1, Ordering::SeqCst);
|
|
482
|
-
ErrorLists::default()
|
|
483
|
-
}
|
|
484
475
|
}
|
|
485
476
|
|
|
486
477
|
fn claude_api_error_line(uuid: &str, status: i64, error: &str) -> String {
|
|
@@ -98,18 +98,6 @@ fn status_enums_serialize_to_exact_python_strings() {
|
|
|
98
98
|
serde_json::to_string(&MetadataSource::Start).unwrap(),
|
|
99
99
|
"\"start\"",
|
|
100
100
|
),
|
|
101
|
-
(
|
|
102
|
-
serde_json::to_string(&AbnormalDecision::Skip).unwrap(),
|
|
103
|
-
"\"skip\"",
|
|
104
|
-
),
|
|
105
|
-
(
|
|
106
|
-
serde_json::to_string(&AbnormalDecision::NotifyBlacklist).unwrap(),
|
|
107
|
-
"\"notify_blacklist\"",
|
|
108
|
-
),
|
|
109
|
-
(
|
|
110
|
-
serde_json::to_string(&AbnormalDecision::NotifyDefault).unwrap(),
|
|
111
|
-
"\"notify_default\"",
|
|
112
|
-
),
|
|
113
101
|
(
|
|
114
102
|
serde_json::to_string(&WholeTeamGoneClass::Alive).unwrap(),
|
|
115
103
|
"\"alive\"",
|
|
@@ -132,178 +120,6 @@ fn status_enums_serialize_to_exact_python_strings() {
|
|
|
132
120
|
}
|
|
133
121
|
}
|
|
134
122
|
|
|
135
|
-
#[test]
|
|
136
|
-
fn coordinator_event_tags_are_byte_stable() {
|
|
137
|
-
// 逐一钉 events.jsonl tag(events.py 稳定契约)。
|
|
138
|
-
let pid = Pid(4242);
|
|
139
|
-
let cases: Vec<(CoordinatorEvent, &str)> = vec![
|
|
140
|
-
(
|
|
141
|
-
CoordinatorEvent::Boot {
|
|
142
|
-
workspace: "/w".into(),
|
|
143
|
-
once: true,
|
|
144
|
-
},
|
|
145
|
-
"coordinator.boot",
|
|
146
|
-
),
|
|
147
|
-
(
|
|
148
|
-
CoordinatorEvent::Started {
|
|
149
|
-
pid,
|
|
150
|
-
log: "/l".into(),
|
|
151
|
-
},
|
|
152
|
-
"coordinator.started",
|
|
153
|
-
),
|
|
154
|
-
(CoordinatorEvent::Stopped { pid }, "coordinator.stopped"),
|
|
155
|
-
(CoordinatorEvent::Exit { stop: true }, "coordinator.exit"),
|
|
156
|
-
(
|
|
157
|
-
CoordinatorEvent::SessionMissing {
|
|
158
|
-
session: "s".into(),
|
|
159
|
-
},
|
|
160
|
-
"coordinator.session_missing",
|
|
161
|
-
),
|
|
162
|
-
(
|
|
163
|
-
CoordinatorEvent::OrphanSelfTerminate {
|
|
164
|
-
initial_ppid: 9,
|
|
165
|
-
current_ppid: 1,
|
|
166
|
-
workspace: "/w".into(),
|
|
167
|
-
},
|
|
168
|
-
"coordinator.orphan_self_terminate",
|
|
169
|
-
),
|
|
170
|
-
(
|
|
171
|
-
CoordinatorEvent::TickError {
|
|
172
|
-
error: "boom".into(),
|
|
173
|
-
exc_type: "OSError".into(),
|
|
174
|
-
consecutive_failures: 1,
|
|
175
|
-
next_sleep_sec: 5.0,
|
|
176
|
-
},
|
|
177
|
-
"coordinator.tick_error",
|
|
178
|
-
),
|
|
179
|
-
(
|
|
180
|
-
CoordinatorEvent::TickErrorSuppressed {
|
|
181
|
-
consecutive_failures: 2,
|
|
182
|
-
next_sleep_sec: 10.0,
|
|
183
|
-
},
|
|
184
|
-
"coordinator.tick_error.suppressed",
|
|
185
|
-
),
|
|
186
|
-
(
|
|
187
|
-
CoordinatorEvent::TickRecovered {
|
|
188
|
-
consecutive_failures: 3,
|
|
189
|
-
},
|
|
190
|
-
"coordinator.tick_recovered",
|
|
191
|
-
),
|
|
192
|
-
(
|
|
193
|
-
CoordinatorEvent::RestartIncompatible {
|
|
194
|
-
pid: Some(pid),
|
|
195
|
-
expected_protocol: 2,
|
|
196
|
-
expected_schema: 3,
|
|
197
|
-
},
|
|
198
|
-
"coordinator.restart_incompatible",
|
|
199
|
-
),
|
|
200
|
-
(
|
|
201
|
-
CoordinatorEvent::RestartIncompatibleStopFailed { pid: Some(pid) },
|
|
202
|
-
"coordinator.restart_incompatible_stop_failed",
|
|
203
|
-
),
|
|
204
|
-
(
|
|
205
|
-
CoordinatorEvent::SchemaIncompatible {
|
|
206
|
-
table: Some("messages".into()),
|
|
207
|
-
missing_columns: vec!["owner_team_id".into()],
|
|
208
|
-
},
|
|
209
|
-
"coordinator.schema_incompatible",
|
|
210
|
-
),
|
|
211
|
-
(
|
|
212
|
-
CoordinatorEvent::IdleTakeoverUnknownPersistent {
|
|
213
|
-
node_id: "w1".into(),
|
|
214
|
-
provider: Some(Provider::Codex),
|
|
215
|
-
auth_mode: None,
|
|
216
|
-
consecutive_ticks: 60,
|
|
217
|
-
rollout_path: None,
|
|
218
|
-
},
|
|
219
|
-
"idle_takeover.unknown_persistent",
|
|
220
|
-
),
|
|
221
|
-
(
|
|
222
|
-
CoordinatorEvent::AbnormalNotify {
|
|
223
|
-
signature: "sig".into(),
|
|
224
|
-
turn_id: None,
|
|
225
|
-
decision: AbnormalDecision::NotifyDefault,
|
|
226
|
-
},
|
|
227
|
-
"abnormal.notify",
|
|
228
|
-
),
|
|
229
|
-
(
|
|
230
|
-
CoordinatorEvent::AbnormalWholeTeamGone {
|
|
231
|
-
classification: WholeTeamGoneClass::UnexpectedExit,
|
|
232
|
-
},
|
|
233
|
-
"abnormal.whole_team_gone",
|
|
234
|
-
),
|
|
235
|
-
(
|
|
236
|
-
CoordinatorEvent::LeaderNotificationLogPruned { removed: 7 },
|
|
237
|
-
"leader_notification.log_pruned",
|
|
238
|
-
),
|
|
239
|
-
(
|
|
240
|
-
CoordinatorEvent::LeaderNotificationPruneFailed { error: "io".into() },
|
|
241
|
-
"leader_notification.prune_failed",
|
|
242
|
-
),
|
|
243
|
-
(
|
|
244
|
-
CoordinatorEvent::RuntimeStateSaveFailed {
|
|
245
|
-
phase: "tick_end".into(),
|
|
246
|
-
error: "replace".into(),
|
|
247
|
-
exc_type: "OSError".into(),
|
|
248
|
-
},
|
|
249
|
-
"runtime.state.save_failed",
|
|
250
|
-
),
|
|
251
|
-
];
|
|
252
|
-
for (evt, want_tag) in &cases {
|
|
253
|
-
let json = serde_json::to_value(evt).unwrap();
|
|
254
|
-
assert_eq!(json["event"], *want_tag, "tag mismatch for {want_tag}");
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
#[test]
|
|
259
|
-
fn tick_error_event_carries_exact_python_fields() {
|
|
260
|
-
// __main__.py:69-74 — error / exc_type / consecutive_failures / next_sleep_sec.
|
|
261
|
-
let evt = CoordinatorEvent::TickError {
|
|
262
|
-
error: "os.replace failed".into(),
|
|
263
|
-
exc_type: "OSError".into(),
|
|
264
|
-
consecutive_failures: 4,
|
|
265
|
-
next_sleep_sec: 40.0,
|
|
266
|
-
};
|
|
267
|
-
let json = serde_json::to_value(&evt).unwrap();
|
|
268
|
-
assert_eq!(json["event"], "coordinator.tick_error");
|
|
269
|
-
assert_eq!(json["error"], "os.replace failed");
|
|
270
|
-
assert_eq!(json["exc_type"], "OSError");
|
|
271
|
-
assert_eq!(json["consecutive_failures"], 4);
|
|
272
|
-
assert_eq!(json["next_sleep_sec"], 40.0);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
#[test]
|
|
276
|
-
fn unknown_persistent_event_provider_none_is_null_not_missing() {
|
|
277
|
-
// lifecycle.py:407 rollout_path=node.get(...) — None 漏穿堵死(bug-085):
|
|
278
|
-
// provider/rollout_path 缺失序列化为 JSON null,绝不省略键。
|
|
279
|
-
let evt = CoordinatorEvent::IdleTakeoverUnknownPersistent {
|
|
280
|
-
node_id: "w7".into(),
|
|
281
|
-
provider: None,
|
|
282
|
-
auth_mode: None,
|
|
283
|
-
consecutive_ticks: 72,
|
|
284
|
-
rollout_path: None,
|
|
285
|
-
};
|
|
286
|
-
let json = serde_json::to_value(&evt).unwrap();
|
|
287
|
-
assert_eq!(json["event"], "idle_takeover.unknown_persistent");
|
|
288
|
-
assert_eq!(json["node_id"], "w7");
|
|
289
|
-
assert!(
|
|
290
|
-
json.get("provider").is_some(),
|
|
291
|
-
"provider key MUST be present (None→null)"
|
|
292
|
-
);
|
|
293
|
-
assert!(json["provider"].is_null(), "provider None → JSON null");
|
|
294
|
-
assert!(
|
|
295
|
-
json.get("auth_mode").is_some(),
|
|
296
|
-
"auth_mode key MUST be present"
|
|
297
|
-
);
|
|
298
|
-
assert!(json["auth_mode"].is_null(), "auth_mode None → JSON null");
|
|
299
|
-
assert!(
|
|
300
|
-
json.get("rollout_path").is_some(),
|
|
301
|
-
"rollout_path key MUST be present"
|
|
302
|
-
);
|
|
303
|
-
assert!(json["rollout_path"].is_null());
|
|
304
|
-
assert_eq!(json["consecutive_ticks"], 72);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
123
|
#[test]
|
|
308
124
|
fn coordinator_metadata_json_field_names_are_stable() {
|
|
309
125
|
// metadata.py:50-57 — 稳定 coordinator.json 契约。
|
|
@@ -25,23 +25,15 @@ fn meta(pid: u32, proto: u32, schema: i64) -> CoordinatorMetadata {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/// `ProviderRegistry` mock —
|
|
29
|
-
/// `adapter_for`/`error_lists` 各记一次调用计数;abnormal-track 只允许触碰 `error_lists`,
|
|
30
|
-
/// 绝不触碰 `adapter_for`(那会走真实 provider client crate)。
|
|
28
|
+
/// `ProviderRegistry` mock — 记录 provider-client adapter 调用 (MUST-NOT-13 / §84).
|
|
31
29
|
struct MockRegistry {
|
|
32
|
-
whitelist: Vec<String>,
|
|
33
|
-
blacklist: Vec<String>,
|
|
34
30
|
adapter_calls: std::cell::Cell<u32>,
|
|
35
|
-
error_list_calls: std::cell::Cell<u32>,
|
|
36
31
|
}
|
|
37
32
|
|
|
38
33
|
impl MockRegistry {
|
|
39
|
-
fn new(
|
|
34
|
+
fn new(_whitelist: &[&str], _blacklist: &[&str]) -> Self {
|
|
40
35
|
Self {
|
|
41
|
-
whitelist: whitelist.iter().map(|s| s.to_string()).collect(),
|
|
42
|
-
blacklist: blacklist.iter().map(|s| s.to_string()).collect(),
|
|
43
36
|
adapter_calls: std::cell::Cell::new(0),
|
|
44
|
-
error_list_calls: std::cell::Cell::new(0),
|
|
45
37
|
}
|
|
46
38
|
}
|
|
47
39
|
}
|
|
@@ -52,13 +44,6 @@ impl ProviderRegistry for MockRegistry {
|
|
|
52
44
|
self.adapter_calls.set(self.adapter_calls.get() + 1);
|
|
53
45
|
crate::provider::get_adapter(_provider)
|
|
54
46
|
}
|
|
55
|
-
fn error_lists(&self, _provider: Provider) -> ErrorLists {
|
|
56
|
-
self.error_list_calls.set(self.error_list_calls.get() + 1);
|
|
57
|
-
ErrorLists {
|
|
58
|
-
whitelist: self.whitelist.clone(),
|
|
59
|
-
blacklist: self.blacklist.clone(),
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
47
|
}
|
|
63
48
|
|
|
64
49
|
/// in-memory `MarkerStore` —— durable marker 落盘探针。
|
|
@@ -260,45 +260,3 @@ fn p2_render_result_received_truncates_summary_to_80_chars() {
|
|
|
260
260
|
"summary must be truncated to 80 chars"
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
|
-
|
|
264
|
-
// P1 — idle_takeover.unknown_persistent must carry the auth_mode field (null when absent)
|
|
265
|
-
// (lifecycle.py:401-408). NOTE: the variant currently lacks the field; the porter adds
|
|
266
|
-
// `auth_mode` between provider and consecutive_ticks and updates this construction.
|
|
267
|
-
#[test]
|
|
268
|
-
fn p2_unknown_persistent_event_serializes_auth_mode_key() {
|
|
269
|
-
let evt = CoordinatorEvent::IdleTakeoverUnknownPersistent {
|
|
270
|
-
node_id: "w7".into(),
|
|
271
|
-
provider: None,
|
|
272
|
-
auth_mode: None,
|
|
273
|
-
consecutive_ticks: 72,
|
|
274
|
-
rollout_path: None,
|
|
275
|
-
};
|
|
276
|
-
let json = serde_json::to_value(&evt).unwrap();
|
|
277
|
-
assert!(
|
|
278
|
-
json.get("auth_mode").is_some(),
|
|
279
|
-
"idle_takeover.unknown_persistent must serialize an auth_mode key (null when absent)"
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// P1 — process_abnormal_records matches a LOWERCASED signature too (abnormal_track.py:49,198):
|
|
284
|
-
// raw has no needle but signature 'TimeoutError' matches blacklist 'timeout'. Current is
|
|
285
|
-
// case-sensitive on `raw` only and ignores the signature → NotifyDefault.
|
|
286
|
-
#[test]
|
|
287
|
-
fn p2_abnormal_matches_lowercased_signature_too() {
|
|
288
|
-
let reg = MockRegistry::new(&[], &["timeout"]);
|
|
289
|
-
let records =
|
|
290
|
-
vec![serde_json::json!({"raw":"nothing here","signature":"TimeoutError","kind":"error"})];
|
|
291
|
-
let out = process_abnormal_records(
|
|
292
|
-
&records,
|
|
293
|
-
®,
|
|
294
|
-
Provider::Codex,
|
|
295
|
-
&AbnormalNotificationState::default(),
|
|
296
|
-
)
|
|
297
|
-
.unwrap();
|
|
298
|
-
assert_eq!(out.notifications.len(), 1);
|
|
299
|
-
assert_eq!(
|
|
300
|
-
out.notifications[0].decision,
|
|
301
|
-
AbnormalDecision::NotifyBlacklist,
|
|
302
|
-
"blacklist 'timeout' must match the lowercased signature 'TimeoutError'"
|
|
303
|
-
);
|
|
304
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! Coordinator core:daemon lifecycle 宿主 + 单次 tick 编排(19 步固定顺序)+ health/start/stop。
|
|
2
3
|
|
|
3
4
|
use std::collections::BTreeMap;
|
|
@@ -19,6 +20,7 @@ use super::health::{
|
|
|
19
20
|
pid_is_running, read_coordinator_metadata, write_coordinator_metadata,
|
|
20
21
|
};
|
|
21
22
|
use super::runtime_observation::{self, CapturedRuntimeFact};
|
|
23
|
+
use super::steps::TickStepGroup;
|
|
22
24
|
use super::types::{
|
|
23
25
|
AgentId, CoordinatorHealthStatus, HealthReport, MetadataSource, Pid, ProviderRegistry,
|
|
24
26
|
SchemaHealth, StartError, StartOutcome, StartReport, StopError, StopOutcome, StopReport,
|
|
@@ -225,7 +227,7 @@ impl Coordinator {
|
|
|
225
227
|
/// `if let Some(rec) = &self.order_recorder { rec.lock()...push(STEP_NAME) }`(tick
|
|
226
228
|
/// 副作用 ORDER 测试断言固定序列)。生产两者均 `None`,零开销。
|
|
227
229
|
pub fn tick(&self) -> Result<TickReport, TickError> {
|
|
228
|
-
self.record_step("load_state");
|
|
230
|
+
self.record_step(TickStepGroup::SessionGate, "load_state");
|
|
229
231
|
let raw_state = crate::state::persist::load_runtime_state(self.workspace.as_path())?;
|
|
230
232
|
// Issue 2 (Round 3b gate review §6): when the runtime carries
|
|
231
233
|
// `active_team_key` AND `teams.<key>` exists, project the team-scoped
|
|
@@ -246,7 +248,7 @@ impl Coordinator {
|
|
|
246
248
|
let event_log = EventLog::new(self.workspace.as_path());
|
|
247
249
|
increment_coordinator_tick_iteration_count(&self.workspace);
|
|
248
250
|
|
|
249
|
-
self.record_step("tmux_session_gate");
|
|
251
|
+
self.record_step(TickStepGroup::SessionGate, "tmux_session_gate");
|
|
250
252
|
if let Some(session_name) = state
|
|
251
253
|
.get("session_name")
|
|
252
254
|
.and_then(Value::as_str)
|
|
@@ -268,7 +270,7 @@ impl Coordinator {
|
|
|
268
270
|
}
|
|
269
271
|
}
|
|
270
272
|
|
|
271
|
-
self.record_step("capture_missing");
|
|
273
|
+
self.record_step(TickStepGroup::SessionGate, "capture_missing");
|
|
272
274
|
let pending_context_fork_audits =
|
|
273
275
|
match self.capture_missing_sessions(&mut state, &event_log) {
|
|
274
276
|
Ok(audits) => audits,
|
|
@@ -293,10 +295,10 @@ impl Coordinator {
|
|
|
293
295
|
.unwrap_or_default();
|
|
294
296
|
let has_work_obligation = tick_has_work_obligation(&store);
|
|
295
297
|
|
|
296
|
-
self.record_step("refresh_statuses");
|
|
298
|
+
self.record_step(TickStepGroup::HealthSync, "refresh_statuses");
|
|
297
299
|
// TODO(spine slice 2b): split lightweight runtime status refresh from health sync.
|
|
298
300
|
|
|
299
|
-
self.record_step("startup_prompts");
|
|
301
|
+
self.record_step(TickStepGroup::RuntimePrompts, "startup_prompts");
|
|
300
302
|
self.handle_startup_prompts(&mut state, &event_log, &pane_snapshot, &window_snapshot);
|
|
301
303
|
|
|
302
304
|
// #229 step2-retry: once an agent's `startup_prompts` flipped to `handled`
|
|
@@ -318,7 +320,7 @@ impl Coordinator {
|
|
|
318
320
|
// 否则 deliver_pending(下行投递主干)够不到,消息卡 accepted。
|
|
319
321
|
// bug-084 哲学 + A-6 同族:每步独立 try,失败写 `coordinator.tick.<step>_failed`
|
|
320
322
|
// 事件后继续走下一步;tick 本身仍返 Ok。
|
|
321
|
-
self.record_step("runtime_prompts");
|
|
323
|
+
self.record_step(TickStepGroup::RuntimePrompts, "runtime_prompts");
|
|
322
324
|
if let Err(error) = self.handle_runtime_approval_prompts(
|
|
323
325
|
&mut state,
|
|
324
326
|
&event_log,
|
|
@@ -331,7 +333,7 @@ impl Coordinator {
|
|
|
331
333
|
);
|
|
332
334
|
}
|
|
333
335
|
|
|
334
|
-
self.record_step("sync_health");
|
|
336
|
+
self.record_step(TickStepGroup::HealthSync, "sync_health");
|
|
335
337
|
// P5 (C-P5-1, N3): ONE pane snapshot per tick, shared by sync_health and the
|
|
336
338
|
// abnormal-exit pass (same-tick reuse only — the snapshot does not outlive
|
|
337
339
|
// this tick; every tick re-reads).
|
|
@@ -365,7 +367,7 @@ impl Coordinator {
|
|
|
365
367
|
);
|
|
366
368
|
}
|
|
367
369
|
|
|
368
|
-
self.record_step("deliver_pending");
|
|
370
|
+
self.record_step(TickStepGroup::Delivery, "deliver_pending");
|
|
369
371
|
let delivered = crate::messaging::deliver_pending_messages(
|
|
370
372
|
self.workspace.as_path(),
|
|
371
373
|
&state,
|
|
@@ -376,7 +378,7 @@ impl Coordinator {
|
|
|
376
378
|
.map(|message_id| DeliveredMessage { message_id })
|
|
377
379
|
.collect::<Vec<_>>();
|
|
378
380
|
|
|
379
|
-
self.record_step("fire_scheduled");
|
|
381
|
+
self.record_step(TickStepGroup::Delivery, "fire_scheduled");
|
|
380
382
|
let scheduled = crate::messaging::fire_due_scheduled_events(
|
|
381
383
|
self.workspace.as_path(),
|
|
382
384
|
&store,
|
|
@@ -394,18 +396,18 @@ impl Coordinator {
|
|
|
394
396
|
// (deliver_pending / fire_scheduled / collect_results) above and below this
|
|
395
397
|
// block continue to flow unchanged. `_state` / `_store` here are intentionally
|
|
396
398
|
// unused (the lookups they powered were nag inputs only).
|
|
397
|
-
self.record_step("detect_stuck");
|
|
399
|
+
self.record_step(TickStepGroup::Abnormal, "detect_stuck");
|
|
398
400
|
let stuck: Vec<AgentId> = Vec::new();
|
|
399
|
-
self.record_step("record_unknown_idle");
|
|
400
|
-
self.record_step("evaluate_takeover");
|
|
401
|
+
self.record_step(TickStepGroup::Abnormal, "record_unknown_idle");
|
|
402
|
+
self.record_step(TickStepGroup::Abnormal, "evaluate_takeover");
|
|
401
403
|
let idle_alerts: Vec<IdleAlert> = Vec::new();
|
|
402
|
-
self.record_step("detect_deadlocks");
|
|
404
|
+
self.record_step(TickStepGroup::Abnormal, "detect_deadlocks");
|
|
403
405
|
let deadlock_alerts: Vec<DeadlockAlert> = Vec::new();
|
|
404
406
|
let _ = &store;
|
|
405
407
|
|
|
406
|
-
self.record_step("detect_compaction");
|
|
407
|
-
self.record_step("detect_drift");
|
|
408
|
-
self.record_step("detect_api_errors");
|
|
408
|
+
self.record_step(TickStepGroup::Abnormal, "detect_compaction");
|
|
409
|
+
self.record_step(TickStepGroup::Abnormal, "detect_drift");
|
|
410
|
+
self.record_step(TickStepGroup::Abnormal, "detect_api_errors");
|
|
409
411
|
let leader_capture = self.capture_leader_receiver(&state);
|
|
410
412
|
let observations = runtime_observation::observe(
|
|
411
413
|
self.workspace.as_path(),
|
|
@@ -425,7 +427,7 @@ impl Coordinator {
|
|
|
425
427
|
results: Vec::new(),
|
|
426
428
|
};
|
|
427
429
|
|
|
428
|
-
self.record_step("atomic_save");
|
|
430
|
+
self.record_step(TickStepGroup::Persist, "atomic_save");
|
|
429
431
|
let saved = match &self.save_hook {
|
|
430
432
|
Some(hook) => hook(&self.workspace, &state),
|
|
431
433
|
None => {
|
|
@@ -474,20 +476,20 @@ impl Coordinator {
|
|
|
474
476
|
// step, and drives the lifecycle helper. Runs OUTSIDE the pre-save
|
|
475
477
|
// window so nested lifecycle saves cannot be clobbered by this
|
|
476
478
|
// tick's final in-memory save (R6 boundary guard).
|
|
477
|
-
self.record_step("attempt_api_error_recoveries");
|
|
479
|
+
self.record_step(TickStepGroup::Abnormal, "attempt_api_error_recoveries");
|
|
478
480
|
crate::coordinator::steps::abnormal::attempt_due_recoveries(
|
|
479
481
|
self.workspace.as_path(),
|
|
480
482
|
&event_log,
|
|
481
483
|
self.transport.as_ref(),
|
|
482
484
|
);
|
|
483
485
|
|
|
484
|
-
self.record_step("collect_results");
|
|
486
|
+
self.record_step(TickStepGroup::Delivery, "collect_results");
|
|
485
487
|
collections.results =
|
|
486
488
|
collect_results(crate::messaging::collect_results_and_notify_watchers(
|
|
487
489
|
self.workspace.as_path(),
|
|
488
490
|
&event_log,
|
|
489
491
|
)?);
|
|
490
|
-
self.record_step("prune_dedupe_log");
|
|
492
|
+
self.record_step(TickStepGroup::Persist, "prune_dedupe_log");
|
|
491
493
|
Ok(base_tick_report(true, false, None, Some(true), collections))
|
|
492
494
|
}
|
|
493
495
|
|
|
@@ -1430,7 +1432,8 @@ impl Coordinator {
|
|
|
1430
1432
|
super::health::message_store_schema_health(&self.workspace)
|
|
1431
1433
|
}
|
|
1432
1434
|
|
|
1433
|
-
fn record_step(&self, step: &'static str) {
|
|
1435
|
+
fn record_step(&self, group: TickStepGroup, step: &'static str) {
|
|
1436
|
+
debug_assert!(TickStepGroup::ordered().contains(&group));
|
|
1434
1437
|
if let Some(recorder) = &self.order_recorder {
|
|
1435
1438
|
if let Ok(mut guard) = recorder.lock() {
|
|
1436
1439
|
guard.push(step);
|
|
@@ -2757,13 +2760,6 @@ mod u1_tests {
|
|
|
2757
2760
|
),
|
|
2758
2761
|
)
|
|
2759
2762
|
}
|
|
2760
|
-
|
|
2761
|
-
fn error_lists(
|
|
2762
|
-
&self,
|
|
2763
|
-
_provider: crate::provider::Provider,
|
|
2764
|
-
) -> super::super::types::ErrorLists {
|
|
2765
|
-
super::super::types::ErrorLists::default()
|
|
2766
|
-
}
|
|
2767
2763
|
}
|
|
2768
2764
|
|
|
2769
2765
|
#[test]
|