1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Subcommand dispatch for `pmat work claim` and `pmat work triage`.
//
// Kept here rather than in the command dispatcher so the whole feature lives
// with the ledger it extends; the dispatcher only needs to know these two
// entry points exist.
use crate::cli::commands::{WorkClaimCommands, WorkTriageCommands};
/// `--force` is an accountability lever, so it must carry its reason. Silent
/// forcing is how a claim system stops meaning anything.
fn force_reason(force: bool, reason: &Option<String>) -> Result<Option<String>> {
match (
force,
reason.as_ref().map(|r| r.trim()).filter(|r| !r.is_empty()),
) {
(false, _) => Ok(None),
(true, Some(r)) => Ok(Some(r.to_string())),
(true, None) => anyhow::bail!(
"--force requires --reason: taking a path from another agent has to be attributable"
),
}
}
/// Route `pmat work claim <sub>`.
pub async fn dispatch_work_claim(command: &WorkClaimCommands) -> Result<()> {
match command {
WorkClaimCommands::Acquire {
paths,
agent,
ttl,
work_item,
note,
force,
reason,
format,
path,
} => {
handle_work_claim_acquire(
paths.clone(),
agent.clone(),
*ttl,
work_item.clone(),
note.clone(),
force_reason(*force, reason)?,
*format,
path.clone(),
)
.await
}
WorkClaimCommands::Release {
paths,
agent,
all,
force,
reason,
format,
path,
} => {
handle_work_claim_release(
paths.clone(),
agent.clone(),
*all,
force_reason(*force, reason)?,
*format,
path.clone(),
)
.await
}
WorkClaimCommands::List {
agent,
include_expired,
format,
path,
} => handle_work_claim_list(agent.clone(), *include_expired, *format, path.clone()).await,
WorkClaimCommands::Check {
paths,
agent,
format,
path,
} => handle_work_claim_check(paths.clone(), agent.clone(), *format, path.clone()).await,
}
}
/// Route `pmat work triage <sub>`.
pub async fn dispatch_work_triage(command: &WorkTriageCommands) -> Result<()> {
match command {
WorkTriageCommands::Record {
agent,
scope,
examined,
acted,
deferred,
reason,
work_item,
format,
path,
} => {
handle_work_triage_record(
agent.clone(),
scope.clone(),
*examined,
*acted,
deferred.clone(),
reason.clone(),
work_item.clone(),
*format,
path.clone(),
)
.await
}
WorkTriageCommands::Verify {
work_item,
agent,
format,
path,
} => {
handle_work_triage_verify(work_item.clone(), agent.clone(), *format, path.clone()).await
}
}
}