A file-based task manager
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add `tsk reject` for declining inbox items

Removes the inbox blob without creating a local task and writes a
`rejected` event to the source's event log so the assignor can see it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+61
+27
src/main.rs
··· 277 277 key: Option<String>, 278 278 }, 279 279 280 + /// Reject a pending inbox item, removing it without creating a local task. 281 + /// Writes a `rejected` event to the source's event log so the assignor 282 + /// sees it. 283 + Reject { 284 + /// Inbox key (e.g. `alice-3` or `inbox/alice-3`). With no argument, 285 + /// rejects the first item in the inbox. 286 + key: Option<String>, 287 + }, 288 + 280 289 /// Bundle the entire workspace into a zip archive. 281 290 Bundle { 282 291 /// Output path. Defaults to ./tsk.zip. ··· 521 530 Commands::Assign { target, task_id } => command_assign(dir, target, task_id), 522 531 Commands::Inbox { remote } => command_inbox(dir, remote), 523 532 Commands::Accept { key } => command_accept(dir, key), 533 + Commands::Reject { key } => command_reject(dir, key), 524 534 Commands::Bundle { output } => command_bundle(dir, output), 525 535 Commands::Migrate => command_migrate(dir), 526 536 Commands::MigrateHistory => command_migrate_history(dir), ··· 962 972 }; 963 973 let id = ws.accept_inbox(&key)?; 964 974 eprintln!("Accepted as {id}"); 975 + Ok(()) 976 + } 977 + 978 + fn command_reject(dir: PathBuf, key: Option<String>) -> Result<()> { 979 + let ws = Workspace::from_path(dir)?; 980 + let key = match key { 981 + Some(k) => k, 982 + None => { 983 + ws.list_inbox()? 984 + .into_iter() 985 + .next() 986 + .ok_or_else(|| errors::Error::Parse("Inbox is empty".into()))? 987 + .inbox_key 988 + } 989 + }; 990 + let (src_ns, src_id) = ws.reject_inbox(&key)?; 991 + eprintln!("Rejected inbox item from {src_ns}/tsk-{src_id}"); 965 992 Ok(()) 966 993 } 967 994
+34
src/workspace.rs
··· 1770 1770 Ok(new_id) 1771 1771 } 1772 1772 1773 + /// Reject a pending inbox item: write a `rejected` event to the source's 1774 + /// event log so the assignor sees it, then delete the inbox blob without 1775 + /// creating a local task. 1776 + pub fn reject_inbox(&self, inbox_key: &str) -> Result<(String, u32)> { 1777 + let key = if inbox_key.starts_with("inbox/") { 1778 + inbox_key.to_string() 1779 + } else { 1780 + format!("inbox/{inbox_key}") 1781 + }; 1782 + let data = self 1783 + .store() 1784 + .read(&key)? 1785 + .ok_or_else(|| Error::Parse(format!("Inbox item '{inbox_key}' not found")))?; 1786 + let payload = backend::InboxPayload::parse(&String::from_utf8_lossy(&data))?; 1787 + 1788 + let cur = self.namespace(); 1789 + let detail = format!("[[{}/inbox]]", cur); 1790 + let author = self.git_author().unwrap_or_default(); 1791 + let marker = std::fs::read_to_string(self.path.join(backend::GIT_BACKED_MARKER))?; 1792 + let src_store = backend::GitStore::open_namespace( 1793 + PathBuf::from(marker.trim()), 1794 + payload.source_namespace.clone(), 1795 + )?; 1796 + backend::append_log( 1797 + &src_store, 1798 + Id(payload.source_id), 1799 + "rejected", 1800 + Some(&detail), 1801 + &author, 1802 + )?; 1803 + self.store().delete(&key)?; 1804 + Ok((payload.source_namespace, payload.source_id)) 1805 + } 1806 + 1773 1807 pub fn migrate_to_git(&self) -> Result<PathBuf> { 1774 1808 if self.is_git_backed() { 1775 1809 return Err(Error::Parse("Workspace is already git-backed".into()));