fix: keep task completion, cancel, pause, and undo honest
Pending earlier-revision jobs no longer skip completion repair; late cancels preserve real exits; begin clears pause; partial undo keeps snapshots; permissions and worktree failure paths are hardened. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+18
-2
@@ -26,6 +26,14 @@ static int locked_work(void *p, volatile int *cancel, cst_tool_result_t *out) {
|
||||
static int instant(void *p, volatile int *cancel, cst_tool_result_t *out) {
|
||||
(void)p; (void)cancel; out->out = strdup("archived output"); out->out_len = strlen(out->out); return 0;
|
||||
}
|
||||
/* Runs to a failed exit even if cancel raced after the side effect. */
|
||||
static int fail_then_note_cancel(void *p, volatile int *cancel, cst_tool_result_t *out) {
|
||||
(void)p;
|
||||
out->out = strdup("command failed"); out->out_len = strlen(out->out);
|
||||
out->exit_code = 7;
|
||||
if (cancel) *cancel = 1; /* late cancel must not rewrite exit 7 → 130 */
|
||||
return 0;
|
||||
}
|
||||
static void write_text(const char *dir, const char *name, const char *text) {
|
||||
char p[8192]; assert(!os_path_join(p, sizeof(p), dir, name));
|
||||
FILE *f = fopen(p, "wb"); assert(f); assert(fputs(text, f) >= 0); assert(!fclose(f));
|
||||
@@ -72,11 +80,19 @@ int main(void) {
|
||||
assert(!cst_runtime_finish(r, "partial", "explicit partial", 0, error, sizeof(error)));
|
||||
assert(!cst_runtime_finish(r, "completed", "must not overwrite partial", 0, error, sizeof(error)));
|
||||
s = cst_runtime_json(r); assert(s && strstr(s, "explicit partial")); free(s);
|
||||
assert(!cst_runtime_controls(r, 1, 0, 1));
|
||||
/* Pause sticks across reopen; begin() clears it so a new turn can run. */
|
||||
assert(!cst_runtime_controls(r, 0, 1, 1));
|
||||
cst_runtime_close(r);
|
||||
r = cst_runtime_open(path); assert(r);
|
||||
assert(!cst_runtime_check(r, 0, 0, 0));
|
||||
assert(!cst_runtime_check(r, 0, 1, 1));
|
||||
s = cst_job_read(r, id, 0); assert(s && strstr(s, "cancelled")); free(s);
|
||||
unsigned long next = cst_runtime_begin(r, "after pause");
|
||||
assert(cst_runtime_check(r, next, 1, 1));
|
||||
assert(!cst_job_start(r, "shell", "", 1, fail_then_note_cancel, NULL, NULL, id,
|
||||
sizeof(id)));
|
||||
s = cst_job_read(r, id, 2000);
|
||||
assert(s && strstr(s, "\"status\":\"failed\"") && strstr(s, "\"exit_code\":7"));
|
||||
free(s);
|
||||
cst_runtime_close(r);
|
||||
write_text(dir, "session.task-v2.json", "{\"revision\":7,\"sequence\":1,\"status\":\"running\",\"jobs\":[{\"job_id\":\"job-7-1\",\"status\":\"running\",\"kind\":\"external\"}]}");
|
||||
r = cst_runtime_open(path); assert(r && !cst_runtime_jobs_pending(r));
|
||||
|
||||
@@ -93,6 +93,21 @@ static void test_undo(void) {
|
||||
EXPECT(cst_undo_snapshot(g_ws, sid, "t4", "../evil") != 0, "reject ..");
|
||||
EXPECT(cst_undo_snapshot(g_ws, sid, "t4", "/etc/passwd") != 0,
|
||||
"reject absolute");
|
||||
|
||||
/* Partial restore must keep the snapshot so a retry is possible. */
|
||||
EXPECT(cst_undo_snapshot(g_ws, sid, "t5", "keep.txt") == 0, "snap keep");
|
||||
wfile("keep.txt", "after\n");
|
||||
{
|
||||
char target[512];
|
||||
snprintf(target, sizeof(target), "%s/keep.txt", g_ws);
|
||||
unlink(target);
|
||||
EXPECT(symlink("elsewhere", target) == 0, "make symlink");
|
||||
EXPECT(cst_undo_restore_last(g_ws, sid, rep, sizeof(rep)) != 0,
|
||||
"partial undo fails");
|
||||
EXPECT(strstr(rep, "symlink") != NULL, "report names symlink skip");
|
||||
EXPECT(cst_undo_count(g_ws, sid) == 1, "snapshot kept after partial fail");
|
||||
unlink(target);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_perms(void) {
|
||||
|
||||
Reference in New Issue
Block a user