taskchef 5.2.0 → 5.4.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
package/README.md CHANGED
@@ -356,17 +356,21 @@ taskchef task show t1
356
356
  taskchef task list
357
357
  taskchef task list --project payments
358
358
  taskchef task list --ascending
359
+ taskchef task list --full-id
359
360
  taskchef task summary
360
361
  ```
361
362
 
362
- Human-readable task listings put the scannable fields first and the durable ID
363
- last. Values are kept in full and aligned in columns. Tasks are newest-first by
364
- default; pass `--ascending` to list them from oldest to newest. The same order
365
- applies to the `tasks` array in `--json` output.
363
+ Human-readable task listings include both the task ID and Codex thread ID.
364
+ UUID-shaped IDs use their first eight-character section by default; pass
365
+ `--full-id` to show both IDs in full. Null thread IDs appear as `-`, consistent
366
+ with other empty table cells. Tasks are newest-first by default; pass
367
+ `--ascending` to list them from oldest to newest. ID formatting does not alter
368
+ the complete values in `--json` output, and the selected order applies to its
369
+ `tasks` array.
366
370
 
367
371
  ```text
368
- TITLE PROJECT CREATED ID
369
- Add retry logs payments 2026-08-12T10:00:00.000Z c0f010ff-84f2-4838-a69d-0ff1f5d721d7
372
+ TITLE PROJECT CREATED ID THREAD ID
373
+ Add retry logs payments 2026-08-12T10:00:00.000Z c0f010ff 019f9d46
370
374
  ```
371
375
 
372
376
  The complete data contract is in [SPEC.md](SPEC.md). Deferred ideas are in
package/SPEC.md CHANGED
@@ -179,9 +179,11 @@ cases.
179
179
  For each assignment, `$taskchef-delegate`:
180
180
 
181
181
  1. runs `dispatch prepare` and native Codex project discovery concurrently;
182
- the preparation command resolves the canonical workspace, loads and
183
- validates configured projects, and generates the full UUID, preparation
184
- timestamp, and exact marker in one process
182
+ only when neither a native provisional-ID resolver nor programmatic tool
183
+ orchestration is available, it also takes one recent-thread baseline
184
+ snapshot in that parallel group. The preparation command resolves the
185
+ canonical workspace, loads and validates configured projects, and generates
186
+ the full UUID, preparation timestamp, and exact marker in one process
185
187
  2. selects one unambiguous configured target and matches its exact path against
186
188
  the already-loaded native projects
187
189
  3. prefixes the instruction with the prepared exact
@@ -195,22 +197,27 @@ For each assignment, `$taskchef-delegate`:
195
197
  6. when creation returns only a provisional client ID, immediately appends the
196
198
  marked entry with `threadId: null`, then prefers one native client-ID wait or
197
199
  resolution call with a 30-second timeout when Codex exposes one
198
- 7. when no native operation is available, takes at most two recent-thread
200
+ 7. when no native operation is available, excludes durable Codex IDs from the
201
+ successful pre-creation baseline, then takes at most two recent-thread
199
202
  snapshots on a nominal 10- and 30-second schedule after the provisional
200
203
  result; a late first checkpoint runs immediately, and the second starts at
201
204
  least 20 seconds after the first actually started. It filters candidates by
202
205
  available host/project/time/worktree metadata, uses title only as an advisory
203
- ordering hint, and accepts only one thread whose structured delegated input
204
- starts with the exact marker
206
+ ordering hint, batches independent reads programmatically when supported,
207
+ and accepts only one thread whose structured delegated input starts with the
208
+ exact marker
205
209
  8. atomically fills the nullable thread ID after an exact match
206
210
  9. returns after recording or after reporting that bounded resolution was
207
211
  unresolved, without waiting for executor work completion.
208
212
 
209
- The exact random marker makes a pre-creation thread snapshot unnecessary.
210
- Creation-time filtering allows five seconds of clock skew. Candidate reads run
211
- concurrently where the host permits and inspect only structured
212
- `codexDelegation.input`, never untrusted title, summary, preview, or plain-text
213
- marker echoes. A native resolver result is verified against the same structured
213
+ The exact random marker remains the sole correlation proof. A successful
214
+ pre-creation snapshot is only a candidate-elimination baseline; its failure
215
+ does not block creation or weaken the marker check. Creation-time filtering
216
+ allows five seconds of clock skew. Candidate reads run concurrently where the
217
+ host permits, use one programmatic batch when available, and inspect only
218
+ structured `codexDelegation.input`, never untrusted title, summary, preview, or
219
+ plain-text marker echoes. A native resolver result is verified against the same
220
+ structured
214
221
  marker before persistence. Zero exact matches remain unresolved; multiple exact
215
222
  matches are ambiguous. Snapshot, candidate-read, native-resolution, or
216
223
  task-resolution errors leave the already-recorded nullable entry intact. The
@@ -290,7 +297,10 @@ The CLI reads persisted history without contacting Codex:
290
297
  - `task show <id>` returns one entry.
291
298
  - `task list` returns entries newest-first by creation time, optionally filtered
292
299
  by historical project name or exact path. `--ascending` returns oldest-first.
293
- The selected order applies to both human rows and the JSON `tasks` array.
300
+ Human rows include task and thread ID columns, abbreviating UUID-shaped IDs
301
+ to their first eight-character section unless `--full-id` is passed. Null
302
+ thread IDs display as `-`. ID formatting does not alter JSON values, and the
303
+ selected order applies to both human rows and the JSON `tasks` array.
294
304
  - `task summary` returns the total and per-project counts.
295
305
  - `task resolve <id> --thread-id <thread-id>` atomically fills one nullable
296
306
  thread ID after Codex verifies the exact structured marker match.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -27,15 +27,21 @@ for all deterministic workspace and task-record operations.
27
27
 
28
28
  ## Dispatch
29
29
 
30
- 1. In parallel, run `<plugin-root>/bin/taskchef.js dispatch prepare --json`
31
- and list the native Codex projects once. The prepare command resolves the
30
+ 1. In parallel, run `<plugin-root>/bin/taskchef.js dispatch prepare --json`,
31
+ list the native Codex projects once, and, only when neither a dedicated
32
+ provisional-ID resolver nor programmatic tool orchestration is available,
33
+ take one pre-creation `list_threads` snapshot with limit 50. Do not add this
34
+ snapshot when candidate reads can be issued in one programmatic batch. The
35
+ prepare command resolves the
32
36
  canonical workspace, loads and validates the configured routing targets,
33
37
  generates the lowercase full UUID task ID, and returns `preparedAt` plus the
34
38
  exact first-line marker. Use
35
39
  `$taskchef-bootstrap` if the workspace is missing or unhealthy.
36
40
  The CLI resolves `--workspace`, then `TASKCHEF_WORKSPACE`, then
37
41
  `~/.agents/taskchef`; do not substitute the current project. Reject the
38
- dispatcher workspace itself as a target.
42
+ dispatcher workspace itself as a target. From the pre-creation snapshot,
43
+ retain only durable Codex thread IDs as a candidate-elimination baseline.
44
+ Snapshot failure must not block creation; continue without a baseline.
39
45
  2. Split the request into the smallest independently useful outcomes. Include
40
46
  constraints, expected testing, and reporting in every instruction.
41
47
  3. Classify against configured `name`, every URL in the `githubRepos` list, and
@@ -54,9 +60,9 @@ for all deterministic workspace and task-record operations.
54
60
  Prefix the complete executor instruction with
55
61
  exactly `<!-- taskchef_id=<full UUID> -->` as the first line, followed by a
56
62
  blank line and the instruction body. Preserve this
57
- marked instruction for recording, and note the creation time.
58
- Do not take a pre-creation thread snapshot; the exact random marker is the
59
- correlation key.
63
+ marked instruction for recording, and note the creation time. The exact
64
+ random marker remains the sole correlation proof; the pre-creation IDs only
65
+ eliminate threads that already existed.
60
66
  6. Create one real Codex task using the exact configured project, a local
61
67
  environment on its executor host, the marked instruction, and a short title.
62
68
  7. When `create_thread` returns a durable `threadId`, immediately run
@@ -81,8 +87,10 @@ for all deterministic workspace and task-record operations.
81
87
  accepts the provisional ID and waits for or resolves its durable thread
82
88
  ID, call it exactly once with a timeout of at most 30 seconds. Do not invent
83
89
  an operation or pass the provisional ID to tools that require `threadId`.
84
- - When no native operation is available, take at most two `list_threads`
85
- snapshots with limit 50. The nominal schedule is 10 and 30 seconds after
90
+ - When no native operation is available, take at most two post-creation
91
+ `list_threads` snapshots with limit 50. Exclude every durable Codex ID from
92
+ the successful pre-creation baseline before reading candidates. The
93
+ nominal schedule is 10 and 30 seconds after
86
94
  the provisional result. Treat the first checkpoint as due, not expired: if
87
95
  nullable recording or other required work finishes after 10 seconds, take
88
96
  the first snapshot immediately. Start the second snapshot no sooner than
@@ -96,7 +104,9 @@ for all deterministic workspace and task-record operations.
96
104
  may normalize it, so never exclude a candidate because its title differs.
97
105
  - Read every remaining candidate with `read_thread`, requesting one turn and
98
106
  no command output. Read candidates concurrently when the tool surface
99
- permits. Inspect only the structured
107
+ permits; when programmatic tool orchestration is available, issue the
108
+ independent reads together in one programmatic batch. Inspect only the
109
+ structured
100
110
  `userMessage.content[].codexDelegation.input`; do not trust titles,
101
111
  summaries, previews, plain-text echoes, or assistant output as proof.
102
112
  - Accept a candidate only when the structured input's first line is exactly
package/src/cli.js CHANGED
@@ -154,6 +154,12 @@ function sortTasksByCreatedAt(tasks, ascending) {
154
154
  .map(({ task }) => task);
155
155
  }
156
156
 
157
+ function displayId(value, fullId) {
158
+ if (fullId || value === null || value === undefined) return value;
159
+ const uuidSection = String(value).match(/^[0-9a-fA-F]{8}(?=-)/);
160
+ return uuidSection ? uuidSection[0] : value;
161
+ }
162
+
157
163
  async function initialize(args) {
158
164
  validateCommandArgs(args, 2, {
159
165
  values: ["--workspace", "--codex-cli"],
@@ -315,20 +321,22 @@ async function taskShow(args) {
315
321
  async function taskList(args) {
316
322
  validateCommandArgs(args, 2, {
317
323
  values: ["--workspace", "--project"],
318
- switches: ["--ascending", "--json"],
324
+ switches: ["--ascending", "--full-id", "--json"],
319
325
  });
320
326
  const filtered = await filterTasks(workspaceRoot(args), {
321
327
  project: option(args, "--project", null),
322
328
  });
323
329
  const dispatches = sortTasksByCreatedAt(filtered, args.includes("--ascending"));
324
330
  const result = { taskCount: dispatches.length, tasks: dispatches };
331
+ const fullId = args.includes("--full-id");
325
332
  print(result, args, (value) => table(
326
- ["TITLE", "PROJECT", "CREATED", "ID"],
333
+ ["TITLE", "PROJECT", "CREATED", "ID", "THREAD ID"],
327
334
  value.tasks.map((dispatch) => [
328
335
  dispatch.title,
329
336
  dispatch.project?.name,
330
337
  dispatch.createdAt,
331
- dispatch.id,
338
+ displayId(dispatch.id, fullId),
339
+ displayId(dispatch.threadId, fullId),
332
340
  ]),
333
341
  ));
334
342
  return 0;
@@ -360,7 +368,7 @@ Usage:
360
368
  taskchef task record [--json] [--workspace <path>]
361
369
  taskchef task resolve <task-id> --thread-id <thread-id> [--json] [--workspace <path>]
362
370
  taskchef task show <task-id> [--json] [--workspace <path>]
363
- taskchef task list [--project <name-or-path>] [--ascending] [--json] [--workspace <path>]
371
+ taskchef task list [--project <name-or-path>] [--ascending] [--full-id] [--json] [--workspace <path>]
364
372
  taskchef task summary [--json] [--workspace <path>]
365
373
 
366
374
  Task record reads one JSON value from closed, non-interactive standard input.
package/src/delegation.js CHANGED
@@ -250,6 +250,7 @@ export async function createAndRecordDelegation({
250
250
  recordTask,
251
251
  resolveRecordedTask = null,
252
252
  resolveProvisionalThread = null,
253
+ baselineThreadIds = new Set(),
253
254
  taskId = randomUUID(),
254
255
  checkpointsMs = THREAD_RESOLUTION_CHECKPOINTS_MS,
255
256
  timeoutMs = THREAD_RESOLUTION_TIMEOUT_MS,
@@ -274,6 +275,9 @@ export async function createAndRecordDelegation({
274
275
  if (resolveRecordedTask !== null && typeof resolveRecordedTask !== "function") {
275
276
  throw new Error("resolveRecordedTask must be a function or null");
276
277
  }
278
+ if (!(baselineThreadIds instanceof Set)) {
279
+ throw new Error("baselineThreadIds must be a Set");
280
+ }
277
281
  validateResolutionSchedule(checkpointsMs, timeoutMs);
278
282
 
279
283
  const prepared = prepareDelegation(instruction, { taskId });
@@ -507,6 +511,7 @@ export async function createAndRecordDelegation({
507
511
  try {
508
512
  const snapshot = await listThreads({ limit: recentLimit });
509
513
  candidates = filterThreadCandidates(snapshot, {
514
+ baselineThreadIds,
510
515
  excludedThreadIds: provisionalIds,
511
516
  hostId: expected.hostId ?? null,
512
517
  projectId: expected.projectId ?? target.projectId ?? null,