taskchef 5.7.2 → 5.9.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.
package/SPEC.md CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
  TaskChef is an interactive Codex dispatcher. It routes independent assignments
6
6
  to real Codex tasks in configured local projects, records each submitted
7
- delegation in a task history, and returns immediately. New tasks append; only a
8
- nullable thread ID may later transition to its durable value.
7
+ delegation in a task history, and returns immediately. New tasks append; locked
8
+ atomic updates may later resolve identity and replace the latest semantic result.
9
9
 
10
- Codex tasks remain authoritative for their progress and results. TaskChef does
11
- not maintain a second lifecycle database.
10
+ Codex tasks remain authoritative for current activity. TaskChef stores only the
11
+ latest useful semantic result, not a second lifecycle or event database.
12
12
 
13
13
  ## Core behavior
14
14
 
@@ -19,14 +19,15 @@ not maintain a second lifecycle database.
19
19
  2. TaskChef separates only outcomes that can proceed independently.
20
20
  3. It selects each target using configured project metadata and validates the
21
21
  selected local path.
22
- 4. It creates an independently openable Codex task in that project.
23
- 5. It embeds a generated TaskChef UUID marker in the initial instruction before
24
- creation, followed by an executor-ownership sentence and the assignment. It
25
- appends one task entry as soon as creation returns, using `threadId: null`
26
- while a provisional client ID is briefly resolved.
22
+ 4. It embeds a generated TaskChef UUID marker, executor-ownership sentence, and
23
+ result-callback instruction, then records the task with `threadId: null`.
24
+ 5. It creates an independently openable Codex task. A durable returned ID is
25
+ resolved immediately; otherwise the initial-prompt hook links the root
26
+ session ID without polling.
27
27
  6. It returns without waiting for executor work to complete.
28
- 7. When requested, TaskChef can read task entries, query the relevant Codex
29
- tasks once, and present a live report without persisting the fetched state.
28
+ 7. Executors report `completed`, `needs_input`, or `failed` through MCP. Reports
29
+ filter old terminal entries, use one live metadata snapshot, and read only
30
+ tasks whose cached result may be stale.
30
31
 
31
32
  A task created by TaskChef owns its delegated initial assignment. It executes
32
33
  that assignment in the current task and does not re-dispatch it merely because
@@ -60,7 +61,10 @@ Every command resolves one workspace in this precedence order:
60
61
  2. `TASKCHEF_WORKSPACE`
61
62
  3. `~/.agents/taskchef`
62
63
 
63
- The current directory is never an implicit workspace. `workspace path` exposes
64
+ `TASKCHEF_WORKSPACE` must be absolute or start with `~/`; this prevents the
65
+ hook, MCP server, and CLI from resolving one relative override against
66
+ different process directories. The current directory is never an implicit
67
+ workspace. `workspace path` exposes
64
68
  the resolved absolute path and its source. Bootstrap compares this canonical
65
69
  path with native Codex projects, and when absent invokes the validated
66
70
  `codex app <path>` command before verifying the native list again. It never
@@ -136,14 +140,14 @@ legacy `null` to `[]`; `workspace init` persists the migration atomically, and
136
140
  any later configuration write emits version 2.
137
141
 
138
142
  The configuration does not store dispatcher identity, execution modes,
139
- schedules, task status, results, host information, or the workspace path.
143
+ schedules, task state, host information, or the workspace path.
140
144
 
141
145
  ## Task entry
142
146
 
143
147
  `tasks.jsonl` contains one compact JSON object per line, in append order:
144
148
 
145
149
  ```json
146
- {"schemaVersion":2,"id":"c0f010ff-84f2-4838-a69d-0ff1f5d721d7","project":{"name":"payments-api","path":"/workspace/payments-api","isGitRepository":true,"githubRepos":["https://github.com/example/payments-api","https://github.com/example/payments-sdk"],"description":"Owns payment authorization, capture, refunds, and provider integrations."},"title":"Add payment retry logs","instruction":"<!-- taskchef_id=c0f010ff-84f2-4838-a69d-0ff1f5d721d7 -->\n\nThis task owns the delegated assignment. Execute it in this task; do not re-dispatch it merely because it concerns TaskChef or a configured project. Explicit requests to delegate separate work remain valid.\n\nAdd structured logs for failed payment retries and test them.","threadId":"019f9d46-f42c-7482-9707-3c107bf241ee","createdAt":"2026-08-08T10:00:00.000Z"}
150
+ {"schemaVersion":3,"id":"c0f010ff-84f2-4838-a69d-0ff1f5d721d7","project":{"name":"payments-api","path":"/workspace/payments-api","isGitRepository":true,"githubRepos":["https://github.com/example/payments-api","https://github.com/example/payments-sdk"],"description":"Owns payment authorization, capture, refunds, and provider integrations."},"title":"Add payment retry logs","instruction":"<!-- taskchef_id=c0f010ff-84f2-4838-a69d-0ff1f5d721d7 -->\n\nThis task owns the delegated assignment. Execute it in this task; do not re-dispatch it merely because it concerns TaskChef or a configured project. Explicit requests to delegate separate work remain valid.\n\nBefore ending, call the TaskChef report_result MCP tool with completed, needs_input, or failed and a concise summary. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.\n\nAdd structured logs for failed payment retries and test them.","threadId":"019f9d46-f42c-7482-9707-3c107bf241ee","createdAt":"2026-08-08T10:00:00.000Z","status":"completed","summary":"Added structured retry logs and regression coverage.","turnId":"019f9d47-result-turn","updatedAt":"2026-08-08T10:08:00.000Z","updatedBy":"mcp"}
147
151
  ```
148
152
 
149
153
  - `schemaVersion` identifies the task entry format.
@@ -154,28 +158,38 @@ schedules, task status, results, host information, or the workspace path.
154
158
  `<!-- taskchef_id=<full UUID> -->` correlation marker, executor-ownership
155
159
  paragraph, and assignment body.
156
160
  - `threadId` identifies the created Codex task, or is `null` when creation was
157
- accepted but bounded marker resolution did not find one durable task ID.
161
+ accepted but its initial identity has not yet been linked.
158
162
  - `createdAt` is the dispatch time as an ISO 8601 timestamp.
159
-
160
- Every entry has exactly these fields. IDs and non-null thread IDs must be
163
+ - `status` is `working`, `needs_input`, `completed`, or `failed`; legacy
164
+ entries normalize it to `null`.
165
+ - `summary` is a nullable, bounded latest-result summary.
166
+ - `turnId` identifies the initial hook turn or latest reported result turn. An
167
+ MCP result for a linked executor requires it; null is allowed only for a
168
+ failed creation before any durable thread exists.
169
+ - `updatedAt` is the server-side update time.
170
+ - `updatedBy` is `dispatcher`, `hook`, or `mcp`.
171
+
172
+ Schema-version-3 entries have exactly these fields. IDs and non-null thread IDs must be
161
173
  unique; any number of unresolved entries may have `threadId: null`. The file is
162
174
  empty or newline terminated, with no blank lines. TaskChef rejects a malformed
163
175
  log instead of skipping bad entries. Writers replace the complete validated
164
176
  file atomically under a workspace lock, so an interrupted write leaves either
165
177
  the old history or the complete new history.
166
178
 
167
- Task creation appends entries. The only permitted mutation is an atomic,
168
- idempotent `task resolve` transition from `threadId: null` to one unique durable
169
- thread ID. Resolution requires the stored instruction's exact marker to match
170
- the task ID. A resolved or mismatched entry cannot be overwritten.
179
+ Task creation appends entries. Identity resolution and result callbacks acquire
180
+ the same cross-process lock, reread and validate the complete log, patch one
181
+ matching entry, and atomically replace the file. Resolution is idempotent and
182
+ one-way from null to one unique durable thread ID. Result callbacks must match
183
+ the recorded task/thread and replace only the latest result snapshot; they do
184
+ not append events.
171
185
 
172
186
  The project snapshot preserves the route even if the project is renamed,
173
- moved, or removed later. Entries never contain status, result, transcript,
174
- hidden reasoning, `hostId`, or update timestamps.
187
+ moved, or removed later. Entries never contain transcripts, hidden reasoning,
188
+ `hostId`, or an event history.
175
189
 
176
- New task entries use schema version 2 and list-valued project snapshots.
177
- Version 1 entries with string or null repository metadata remain readable and
178
- normalize to version 2 in API and CLI output. Historical task entries with the
190
+ New task entries use schema version 3. Version 1 and 2 entries remain readable
191
+ and normalize to version 3 in API and CLI output with nullable result fields.
192
+ Historical task entries with the
179
193
  old heading-style marker also remain readable. New nullable records, candidate
180
194
  matching, and resolution require the exact HTML-comment marker; TaskChef never
181
195
  uses an old marker to correlate a thread. Direct records that already have a
@@ -197,49 +211,29 @@ For each assignment, `$taskchef-delegate`:
197
211
  the already-loaded native projects
198
212
  3. prefixes the instruction with the prepared exact
199
213
  `<!-- taskchef_id=<UUID> -->` marker as the first line, followed by a blank
200
- line, the executor-ownership sentence, another blank line, and the
214
+ line, the executor-ownership sentence, result-callback sentence, and
201
215
  assignment body
202
- 4. creates a real Codex task at the exact configured path
203
- 5. appends a task entry immediately through the structured `record_task` tool
204
- when creation returns a durable thread ID; it never opens a shell, parses
205
- stdin, writes a temporary record file, or probes command-sandbox permission
206
- 6. when creation returns only a provisional client ID, immediately appends the
207
- marked entry with `threadId: null`; the current supported Codex surface has
208
- no provisional-ID resolver, so the skill performs no post-creation tool
209
- availability inspection
210
- 7. takes at most two recent-thread
211
- snapshots on a nominal 10- and 30-second schedule after the provisional
212
- result; a late first checkpoint runs immediately, and the second starts at
213
- least 20 seconds after the first actually started. It filters candidates by
214
- available host/project/time/worktree metadata, uses title only as an advisory
215
- ordering hint, issues all independent candidate reads in exactly one
216
- programmatic batch per snapshot, and accepts only one thread whose structured
217
- delegated input starts with the exact marker; inability to execute that
218
- required batch leaves the nullable record unresolved instead of switching to
219
- serial reads
220
- 8. atomically fills the nullable thread ID with `resolve_task` after an exact
221
- match
222
- 9. returns after recording or after reporting that bounded resolution was
223
- unresolved, without waiting for executor work completion.
224
-
225
- The exact random marker remains the sole correlation proof. Creation-time
226
- filtering allows five seconds of clock skew. Candidate reads use exactly one
227
- programmatic batch per snapshot and inspect only
228
- structured `codexDelegation.input`, never untrusted title, summary, preview, or
229
- plain-text marker echoes. Zero exact matches remain unresolved; multiple exact
230
- matches are ambiguous. Snapshot, candidate-read, or task-resolution errors
231
- leave the already-recorded nullable entry intact. The workflow is bounded by
232
- two snapshots rather than an absolute wall-clock cutoff:
233
- mandatory recording or tool latency can shift both attempts later, but cannot
234
- erase an attempt or reduce the minimum 20-second interval between their start
235
- times.
236
- A `clientThreadId`, `pendingWorktreeId`, or ID in the documented provisional
237
- `local:` namespace remains diagnostic context and is rejected from every path
238
- that could persist the canonical `threadId` field.
216
+ 4. appends a `working` task entry with `threadId: null` through `record_task`
217
+ before executor creation
218
+ 5. creates a real Codex task at the exact configured path
219
+ 6. calls `resolve_task` when creation returns a durable thread ID; otherwise it
220
+ returns immediately and the initial `UserPromptSubmit` hook resolves the root
221
+ session ID against the exact marker
222
+ 7. records executor-creation failure as a `failed` semantic result on the
223
+ already-existing entry
224
+ 8. never lists, reads, waits for, or polls threads during delegation.
225
+
226
+ Recording first closes the creation/hook race. On the exact marked prompt, the
227
+ hook receives the root session ID and initial turn ID and writes only identity
228
+ plus `working`. On later prompts whose session ID matches a recorded executor,
229
+ the same `UserPromptSubmit` hook reads the task history and supplies the current
230
+ turn ID as callback context without writing. No permission, tool, stop,
231
+ notification, or session hook writes task lifecycle state. A provisional
232
+ `local:` ID is diagnostic only and can never be persisted as `threadId`.
239
233
 
240
234
  The plugin bundles a local stdio MCP server with focused `prepare_dispatch`,
241
- `record_task`, and `resolve_task` tools. It accepts no workspace path from the
242
- model and resolves only `TASKCHEF_WORKSPACE` or the canonical
235
+ `record_task`, `resolve_task`, and `report_result` tools. It accepts no workspace path from the
236
+ model and resolves only an absolute (or `~/`-prefixed) `TASKCHEF_WORKSPACE` or the canonical
243
237
  `~/.agents/taskchef` default. The write tools reuse the public workspace APIs,
244
238
  so structured calls cannot bypass exact-field validation, locking, atomic
245
239
  replacement, unique IDs, or one-way nullable resolution. They are closed-world
@@ -247,7 +241,11 @@ local mutations and never create Codex tasks. Desktop thread tools remain
247
241
  available only to the Codex skill; the standalone CLI remains available for
248
242
  bootstrap, manual inspection, and recovery.
249
243
 
250
- ### End-to-end benchmark results
244
+ ### Legacy end-to-end benchmark results
245
+
246
+ Schema-v1 benchmark fixtures below validate historical bounded-resolver
247
+ artifacts only. New benchmarks must measure record-before-create, initial-hook
248
+ identity, and result callbacks without using the removed snapshot resolver.
251
249
 
252
250
  An explicitly requested live benchmark writes one ignored, timestamped JSON
253
251
  artifact through `scripts/e2e-benchmark.js`. Schema version 1 contains:
@@ -298,17 +296,20 @@ up to seven seconds, while permanent permission failures such as `EPERM` or
298
296
  `EACCES` fail immediately so the caller can request the required permission
299
297
  without paying the contention retry budget.
300
298
 
301
- A failed executor creation produces no entry. If executor creation succeeds but
302
- the append fails, the executor remains valid and TaskChef tells the user that
303
- it was not recorded.
299
+ A failed executor creation normally leaves its pre-created entry with `failed`.
300
+ The exported orchestration helper attaches `taskChefTaskId` and
301
+ `taskChefResultReporting` (`recorded`, `failed`, or `unavailable`) to a thrown
302
+ creation error so callers can recover the entry if failure reporting itself was
303
+ not available. A failed pre-creation record stops before executor creation, so
304
+ there is no untracked executor.
304
305
 
305
306
  ## Task history and live reports
306
307
 
307
308
  The CLI reads persisted history without contacting Codex:
308
309
 
309
310
  - `task show <id-or-8-character-prefix>` returns one entry. By default it emits
310
- labeled human-readable lines for title, project name and path, creation time,
311
- full task ID, thread ID, and instruction. A null thread ID renders as `-`.
311
+ labeled human-readable lines for title, project, status, summary, creation and
312
+ update metadata, task/thread/turn IDs, and instruction. A null ID renders as `-`.
312
313
  Carriage returns and newlines in labeled values render as `\\r` and `\\n`.
313
314
  The instruction starts on the line after `Instruction:` and retains its stored
314
315
  line breaks and indentation. `--json` returns the unchanged complete task
@@ -318,7 +319,7 @@ The CLI reads persisted history without contacting Codex:
318
319
  wrong-case prefixes fail without selecting a task.
319
320
  - `task list` returns entries newest-first by creation time, optionally filtered
320
321
  by historical project name or exact path. `--ascending` returns oldest-first.
321
- Human rows include task and thread ID columns, abbreviating UUID-shaped IDs
322
+ Human rows include status, update time, task ID, and thread ID, abbreviating UUID-shaped IDs
322
323
  to their first eight-character section unless `--full-id` is passed. Null
323
324
  thread IDs display as `-`. ID formatting does not alter JSON values, and the
324
325
  selected order applies to both human rows and the JSON `tasks` array.
@@ -326,20 +327,35 @@ The CLI reads persisted history without contacting Codex:
326
327
  - `task resolve <id> --thread-id <thread-id>` atomically fills one nullable
327
328
  thread ID after Codex verifies the exact structured marker match.
328
329
 
329
- When the user requests current state or outcomes, `$taskchef-report` makes one
330
- marker-based discovery pass for nullable entries and uses `task resolve` only
331
- for a single exact match. It reports unmatched entries as unresolved and
332
- queries every durable thread ID exactly once, in batches of no more than eight.
333
- The report does not poll, wait, persist status or results, or create a scheduled
334
- job.
330
+ When the user requests an overview, `$taskchef-report` includes working,
331
+ needs-input, unresolved, legacy, and terminal tasks updated during the last
332
+ seven days. It omits older terminal entries unless explicitly requested or a
333
+ single recent-thread metadata snapshot shows that they are active or awaiting
334
+ approval. That one snapshot is a broad contradiction check: active or awaiting-
335
+ approval metadata overrides a cached result immediately, without one detailed
336
+ read per task. An inactive state does not prove semantic completion; in a broad
337
+ overview it permits an `updatedBy: mcp` result to stand by default.
338
+ Dispatcher- or hook-written `working` snapshots have no semantic callback and
339
+ trigger a live read when selected; an inactive task without an MCP callback has
340
+ an unknown outcome. An MCP-written `failed` snapshot with null thread/turn IDs
341
+ is a fresh executor-creation failure and requires no live lookup. For a focused
342
+ task, title, or project report, any newer matched metadata timestamp triggers at
343
+ most one targeted read per selected inactive task. This distinguishes normal
344
+ same-turn finalization from a quick newer turn by comparing structured turn IDs
345
+ and native turn state. Missing callbacks, uncertain or contradictory identity
346
+ or metadata, and explicitly fully-live requests also trigger one targeted read.
347
+ Reads are batched in groups of eight. Broad overviews do not fan out reads over
348
+ idle terminal tasks solely because their timestamps are newer. Reporting never
349
+ persists inferred live state and does not poll, wait, or schedule work.
335
350
 
336
351
  ## Boundaries
337
352
 
338
353
  TaskChef does not include:
339
354
 
340
- - lifecycle status or result persistence
341
- - task callbacks, hooks, indefinite polling, daemons, heartbeats, or schedules
342
- - arbitrary Codex task discovery beyond bounded marker-based creation recovery
355
+ - lifecycle event history or hook-inferred completion
356
+ - lifecycle event types beyond `UserPromptSubmit`
357
+ - indefinite polling, daemons, heartbeats, dispatcher wakeups, or schedules
358
+ - arbitrary Codex task discovery
343
359
  - remote hosts or `hostId` storage
344
360
  - transcript or hidden-reasoning collection
345
361
  - one-active-task-per-project restrictions
@@ -351,15 +367,16 @@ TaskChef does not include:
351
367
  1. Bootstrap creates `AGENTS.md`, `taskchef.json`, and `tasks.jsonl`, then
352
368
  remains idempotent.
353
369
  2. Project metadata routes an unambiguous request to the correct local project.
354
- 3. A successful delegation creates a visible Codex task and appends its thread
355
- ID with a project snapshot.
356
- 4. A provisional creation with one exact marker match records its durable
357
- thread ID; zero or multiple matches record `threadId: null` for later
358
- recovery.
370
+ 3. A delegation records `working` before creating a visible Codex task.
371
+ 4. A durable creation resolves immediately; a provisional creation is linked
372
+ by the initial exact-marker hook without polling.
359
373
  5. The dispatcher returns without waiting for execution.
360
374
  6. Several independent assignments can create several entries, including
361
375
  multiple entries for the same project.
362
376
  7. Task history commands return deterministic entries and project counts.
363
- 8. A live report queries each relevant task once and writes nothing.
364
- 9. Malformed JSONL, duplicate IDs, duplicate thread IDs, and symlinked managed
377
+ 8. MCP callbacks replace the latest needs-input, completed, or failed snapshot.
378
+ 9. A report skips old terminal tasks by default, checks live metadata once,
379
+ overrides active or approval-waiting tasks directly, and reads only anomalous
380
+ candidates without writing inferred state.
381
+ 10. Malformed JSONL, duplicate IDs, duplicate thread IDs, and symlinked managed
365
382
  files fail safely.