taskchef 3.0.2 → 3.0.3

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": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
package/BACKLOG.md CHANGED
@@ -39,3 +39,18 @@ clear data model before implementation.
39
39
  - Evaluate automatic project discovery rules and exclusions.
40
40
  - Consider multiple executor threads for one logical assignment if a real
41
41
  workflow requires it.
42
+
43
+ ## Codex provisional thread lifecycle
44
+
45
+ - Track [openai/codex#26861](https://github.com/openai/codex/issues/26861),
46
+ where worktree creation can return only a provisional `clientThreadId` or
47
+ `pendingWorktreeId` with no supported mapping to the durable `threadId`.
48
+ - Prefer an official bounded operation such as
49
+ `wait_for_thread(clientThreadId, timeoutMs) -> { status, threadId? }` or
50
+ `resolve_client_thread(clientThreadId) -> { status, threadId? }`. Returning a
51
+ reserved durable ID from `create_thread`, or emitting a materialization event
52
+ containing it, would also close the lifecycle gap.
53
+ - Re-evaluate TaskChef's sparse marker-discovery fallback when Codex exposes
54
+ one of these APIs. Keep exact marker verification before persisting the
55
+ returned durable ID unless the official contract provides equivalent
56
+ correlation guarantees.
package/README.md CHANGED
@@ -117,9 +117,18 @@ the same project.
117
117
  Open an executor and prompt it like any other Codex task. Its thread is the
118
118
  live source of truth for progress, questions, and results.
119
119
 
120
- The dispatcher workspace keeps `tasks.jsonl`, an append-only history of
121
- successful delegations. It records what TaskChef sent, when it sent it, which
122
- project it selected, and which Codex task received the work.
120
+ The dispatcher workspace keeps `tasks.jsonl`, a history of submitted
121
+ delegations. New tasks are appended; the only later change allowed is filling
122
+ an unresolved task's nullable thread ID. The log records what TaskChef sent,
123
+ when it sent it, which project it selected, and which Codex task received the
124
+ work.
125
+
126
+ Every delegated instruction includes a unique `# taskchef_id=<UUID>` marker.
127
+ If worktree creation does not return a thread ID immediately, TaskChef records
128
+ the marked delegation as unresolved, then waits briefly for the durable task.
129
+ It prefers a native Codex client-ID resolver when available and otherwise makes
130
+ two exact-marker checks during a short bounded window. If it still cannot
131
+ identify exactly one task, the recorded marker remains available for recovery.
123
132
 
124
133
  ### Ask for a live report
125
134
 
@@ -161,8 +170,9 @@ project metadata that TaskChef used when it delegated the work.
161
170
 
162
171
  - TaskChef is an interactive dispatcher. It is not a scheduler, daemon, hook
163
172
  service, or background worker.
164
- - Executors are visible Codex tasks. The dispatcher does not supervise them or
165
- wait for them to finish.
173
+ - Executors are visible Codex tasks. The dispatcher may wait briefly to resolve
174
+ a worktree task's thread ID, but it does not supervise executors or wait for
175
+ them to finish.
166
176
  - TaskChef routes only to projects on the same local execution host.
167
177
  - The task history contains successful delegations, not current task status or
168
178
  task results.
@@ -213,6 +223,7 @@ taskchef project import [<file> | -]
213
223
  taskchef project list
214
224
  taskchef project remove <name>
215
225
  taskchef task record
226
+ taskchef task resolve <task-id> --thread-id <thread-id>
216
227
  taskchef task show <task-id>
217
228
  taskchef task list
218
229
  taskchef task summary
@@ -250,14 +261,24 @@ set.
250
261
 
251
262
  ### Task history
252
263
 
253
- `task record` reads one successful delegation from standard input. The
264
+ `task record` reads one submitted delegation from standard input. The
254
265
  `project` value is the exact configured project path:
255
266
 
256
267
  ```sh
257
- printf '%s\n' '{"id":"t1","project":"/workspace/payments","title":"Add retry logs","instruction":"Add structured logs for failed retries and test them.","threadId":"019f..."}' |
268
+ printf '%s\n' '{"id":"c0f010ff-84f2-4838-a69d-0ff1f5d721d7","project":"/workspace/payments","title":"Add retry logs","instruction":"# taskchef_id=c0f010ff-84f2-4838-a69d-0ff1f5d721d7\n\nAdd structured logs for failed retries and test them.","threadId":"019f..."}' |
258
269
  taskchef task record --json --workspace <workspace>
259
270
  ```
260
271
 
272
+ If a task has `threadId: null`, Codex can later find its exact marker and pass
273
+ the verified durable ID to the CLI. Resolution is atomic and only permits the
274
+ one-way transition from null to one unique thread ID:
275
+
276
+ ```sh
277
+ taskchef task resolve c0f010ff-84f2-4838-a69d-0ff1f5d721d7 \
278
+ --thread-id 019f9d46-f42c-7482-9707-3c107bf241ee \
279
+ --workspace <workspace>
280
+ ```
281
+
261
282
  Inspect the task history without querying Codex tasks:
262
283
 
263
284
  ```sh
package/SPEC.md CHANGED
@@ -3,8 +3,9 @@
3
3
  ## Purpose
4
4
 
5
5
  TaskChef is an interactive Codex dispatcher. It routes independent assignments
6
- to real Codex tasks in configured local projects, records each successful
7
- delegation in an append-only task history, and returns immediately.
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.
8
9
 
9
10
  Codex tasks remain authoritative for their progress and results. TaskChef does
10
11
  not maintain a second lifecycle database.
@@ -16,8 +17,10 @@ not maintain a second lifecycle database.
16
17
  3. It selects each target using configured project metadata and validates the
17
18
  selected local path.
18
19
  4. It creates an independently openable Codex task in that project.
19
- 5. After creation returns a thread ID, it appends one task entry.
20
- 6. It returns without waiting for the executor.
20
+ 5. It embeds a generated TaskChef UUID marker in the initial instruction before
21
+ creation. It appends one task entry as soon as creation returns, using
22
+ `threadId: null` while a provisional client ID is briefly resolved.
23
+ 6. It returns without waiting for executor work to complete.
21
24
  7. When requested, TaskChef can read task entries, query the relevant Codex
22
25
  tasks once, and present a live report without persisting the fetched state.
23
26
 
@@ -38,15 +41,11 @@ live report requests to `$taskchef-report`. Bootstrap preserves unrelated
38
41
  instructions and refreshes only the managed block.
39
42
 
40
43
  `workspace init` is idempotent. It creates an empty configuration and task
41
- log when missing, refreshes managed instructions, removes legacy TaskChef skill
42
- symlinks, and migrates legacy task records that contain executor thread IDs.
43
- It stops on a legacy pending record with no thread ID rather than discarding
44
- that record. If a legacy record refers to a project that was removed from the
45
- configuration, migration reconstructs its project snapshot from the existing
46
- local project path.
44
+ log when missing, refreshes managed instructions, and removes legacy TaskChef
45
+ skill symlinks.
47
46
 
48
47
  `doctor` validates configuration, project paths, the JSONL log, managed
49
- instructions, and the absence of legacy workspace structures without modifying
48
+ instructions, and the absence of legacy TaskChef skill links without modifying
50
49
  the workspace.
51
50
 
52
51
  ## Project configuration
@@ -101,22 +100,30 @@ schedules, task status, results, host information, or the workspace path.
101
100
  `tasks.jsonl` contains one compact JSON object per line, in append order:
102
101
 
103
102
  ```json
104
- {"schemaVersion":1,"id":"d1-retry-logs","project":{"name":"payments-api","path":"/workspace/payments-api","isGitRepository":true,"githubRepo":"https://github.com/example/payments-api","description":"Owns payment authorization, capture, refunds, and provider integrations."},"title":"Add payment retry logs","instruction":"Add structured logs for failed payment retries and test them.","threadId":"019f9d46-f42c-7482-9707-3c107bf241ee","createdAt":"2026-08-08T10:00:00.000Z"}
103
+ {"schemaVersion":1,"id":"c0f010ff-84f2-4838-a69d-0ff1f5d721d7","project":{"name":"payments-api","path":"/workspace/payments-api","isGitRepository":true,"githubRepo":"https://github.com/example/payments-api","description":"Owns payment authorization, capture, refunds, and provider integrations."},"title":"Add payment retry logs","instruction":"# taskchef_id=c0f010ff-84f2-4838-a69d-0ff1f5d721d7\n\nAdd structured logs for failed payment retries and test them.","threadId":"019f9d46-f42c-7482-9707-3c107bf241ee","createdAt":"2026-08-08T10:00:00.000Z"}
105
104
  ```
106
105
 
107
106
  - `schemaVersion` identifies the task entry format.
108
107
  - `id` is a unique TaskChef task identifier.
109
108
  - `project` is the complete configured project snapshot used for routing.
110
109
  - `title` is a short task name.
111
- - `instruction` is the complete executor instruction.
112
- - `threadId` identifies the created Codex task.
110
+ - `instruction` is the complete executor instruction, including its first-line
111
+ `# taskchef_id=<full UUID>` correlation marker.
112
+ - `threadId` identifies the created Codex task, or is `null` when creation was
113
+ accepted but bounded marker resolution did not find one durable task ID.
113
114
  - `createdAt` is the dispatch time as an ISO 8601 timestamp.
114
115
 
115
- Every entry has exactly these fields. IDs and thread IDs must be unique. The
116
- file is empty or newline terminated, with no blank lines. TaskChef rejects a
117
- malformed log instead of skipping bad entries. Writers replace the complete
118
- validated file atomically under a workspace lock, so an interrupted write
119
- leaves either the old history or the complete new history.
116
+ Every entry has exactly these fields. IDs and non-null thread IDs must be
117
+ unique; any number of unresolved entries may have `threadId: null`. The file is
118
+ empty or newline terminated, with no blank lines. TaskChef rejects a malformed
119
+ log instead of skipping bad entries. Writers replace the complete validated
120
+ file atomically under a workspace lock, so an interrupted write leaves either
121
+ the old history or the complete new history.
122
+
123
+ Task creation appends entries. The only permitted mutation is an atomic,
124
+ idempotent `task resolve` transition from `threadId: null` to one unique durable
125
+ thread ID. Resolution requires the stored instruction's exact marker to match
126
+ the task ID. A resolved or mismatched entry cannot be overwritten.
120
127
 
121
128
  The project snapshot preserves the route even if the project is renamed,
122
129
  moved, or removed later. Entries never contain status, result, transcript,
@@ -128,9 +135,41 @@ For each assignment, `$taskchef-delegate`:
128
135
 
129
136
  1. loads and validates configured projects
130
137
  2. selects one unambiguous target
131
- 3. creates a real Codex task at the exact configured path
132
- 4. appends a task entry only after receiving the task's thread ID
133
- 5. returns the created task link without reading or waiting for that task.
138
+ 3. generates a full UUID and prefixes the instruction with its exact
139
+ `# taskchef_id=<UUID>` marker
140
+ 4. creates a real Codex task at the exact configured path
141
+ 5. appends a task entry immediately when creation returns a durable thread ID
142
+ 6. when creation returns only a provisional client ID, immediately appends the
143
+ marked entry with `threadId: null`, then prefers one native client-ID wait or
144
+ resolution call with a 30-second timeout when Codex exposes one
145
+ 7. when no native operation is available, takes at most two recent-thread
146
+ snapshots near 10 and 30 seconds after the provisional result, filters
147
+ candidates by available host/project/time/worktree metadata, uses title only
148
+ as an advisory ordering hint, and accepts only one thread whose structured
149
+ delegated input starts with the exact marker
150
+ 8. atomically fills the nullable thread ID after an exact match
151
+ 9. returns after recording or after reporting that bounded resolution was
152
+ unresolved, without waiting for executor work completion.
153
+
154
+ The exact random marker makes a pre-creation thread snapshot unnecessary.
155
+ Creation-time filtering allows five seconds of clock skew. Candidate reads run
156
+ concurrently where the host permits and inspect only structured
157
+ `codexDelegation.input`, never untrusted title, summary, preview, or plain-text
158
+ marker echoes. A native resolver result is verified against the same structured
159
+ marker before persistence. Zero exact matches time out unresolved; multiple
160
+ exact matches are ambiguous. Snapshot, candidate-read, native-resolution, or
161
+ task-resolution errors leave the already-recorded nullable entry intact. No
162
+ snapshot, candidate read, marker verification, or task-resolution write starts
163
+ after the 30-second deadline, so tool latency can reduce the number of attempts.
164
+ A `clientThreadId`, `pendingWorktreeId`, or ID in the documented provisional
165
+ `local:` namespace remains diagnostic context and is rejected from every path
166
+ that could persist the canonical `threadId` field.
167
+
168
+ Desktop thread tools are available to the Codex skill, not to the standalone
169
+ Node CLI. The package therefore exposes testable marker/filter/orchestration
170
+ helpers with injected thread-tool callbacks, while the skill owns the actual
171
+ desktop-tool calls and the CLI remains responsible only for validated data
172
+ operations.
134
173
 
135
174
  A failed executor creation produces no entry. If executor creation succeeds but
136
175
  the append fails, the executor remains valid and TaskChef tells the user that
@@ -144,19 +183,23 @@ The CLI reads persisted history without contacting Codex:
144
183
  - `task list` returns entries in append order, optionally filtered by
145
184
  historical project name or exact path.
146
185
  - `task summary` returns the total and per-project counts.
186
+ - `task resolve <id> --thread-id <thread-id>` atomically fills one nullable
187
+ thread ID after Codex verifies the exact structured marker match.
147
188
 
148
- When the user requests current state or outcomes, `$taskchef-report` loads the
149
- relevant entries and queries every recorded Codex task exactly once, in batches
150
- of no more than eight. It reports the snapshot and discards it. The report does
151
- not update `tasks.jsonl`, poll, wait, or create a scheduled job.
189
+ When the user requests current state or outcomes, `$taskchef-report` makes one
190
+ marker-based discovery pass for nullable entries and uses `task resolve` only
191
+ for a single exact match. It reports unmatched entries as unresolved and
192
+ queries every durable thread ID exactly once, in batches of no more than eight.
193
+ The report does not poll, wait, persist status or results, or create a scheduled
194
+ job.
152
195
 
153
196
  ## Boundaries
154
197
 
155
198
  TaskChef does not include:
156
199
 
157
200
  - lifecycle status or result persistence
158
- - task callbacks, hooks, polling, daemons, heartbeats, or schedules
159
- - arbitrary Codex task discovery
201
+ - task callbacks, hooks, indefinite polling, daemons, heartbeats, or schedules
202
+ - arbitrary Codex task discovery beyond bounded marker-based creation recovery
160
203
  - remote hosts or `hostId` storage
161
204
  - transcript or hidden-reasoning collection
162
205
  - one-active-task-per-project restrictions
@@ -170,10 +213,13 @@ TaskChef does not include:
170
213
  2. Project metadata routes an unambiguous request to the correct local project.
171
214
  3. A successful delegation creates a visible Codex task and appends its thread
172
215
  ID with a project snapshot.
173
- 4. The dispatcher returns without waiting for execution.
174
- 5. Several independent assignments can create several entries, including
216
+ 4. A provisional creation with one exact marker match records its durable
217
+ thread ID; zero or multiple matches record `threadId: null` for later
218
+ recovery.
219
+ 5. The dispatcher returns without waiting for execution.
220
+ 6. Several independent assignments can create several entries, including
175
221
  multiple entries for the same project.
176
- 6. Task history commands return deterministic entries and project counts.
177
- 7. A live report queries each relevant task once and writes nothing.
178
- 8. Malformed JSONL, duplicate IDs, duplicate thread IDs, and symlinked managed
222
+ 7. Task history commands return deterministic entries and project counts.
223
+ 8. A live report queries each relevant task once and writes nothing.
224
+ 9. Malformed JSONL, duplicate IDs, duplicate thread IDs, and symlinked managed
179
225
  files fail safely.
package/index.js CHANGED
@@ -14,7 +14,25 @@ export {
14
14
  listTasks,
15
15
  readTask,
16
16
  recordTask,
17
+ resolveTask,
17
18
  requireSafeId,
18
19
  removeProject,
19
20
  validateConfig,
20
21
  } from "./src/workspace.js";
22
+
23
+ export {
24
+ THREAD_RESOLUTION_CHECKPOINTS_MS,
25
+ THREAD_RESOLUTION_CLOCK_SKEW_MS,
26
+ THREAD_RESOLUTION_RECENT_LIMIT,
27
+ THREAD_RESOLUTION_TIMEOUT_MS,
28
+ createAndRecordDelegation,
29
+ filterThreadCandidates,
30
+ hasExactTaskChefMarker,
31
+ listThreadEntries,
32
+ isProvisionalThreadId,
33
+ normalizeDurableThreadId,
34
+ parseTaskChefMarker,
35
+ prepareDelegation,
36
+ structuredDelegatedInputs,
37
+ taskChefMarker,
38
+ } from "./src/delegation.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -24,9 +24,8 @@ all deterministic workspace operations.
24
24
 
25
25
  1. Run `workspace init --json`. It takes no stdin, creates an empty
26
26
  configuration when missing, creates the append-only task log, refreshes
27
- managed instructions, and migrates legacy task records that have executor
28
- thread IDs. The installed plugin provides all three TaskChef skills outside
29
- the dispatcher workspace.
27
+ managed instructions, and removes legacy TaskChef skill links. The installed
28
+ plugin provides all three TaskChef skills outside the dispatcher workspace.
30
29
  2. Run `doctor --json` after setup or when the user asks to diagnose the
31
30
  workspace. Doctor is read-only. Rerun `workspace init --json` to repair the
32
31
  managed scaffold.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: taskchef-delegate
3
- description: "Dispatch actionable requests from an initialized TaskChef workspace into independently openable Codex project tasks. Use for ordinary work requests in a TaskChef workspace, explicit delegation, or splitting independent work across projects. Record successful dispatches, return immediately, and never use subagents, hooks, schedules, or foreground waiting."
3
+ description: "Dispatch actionable requests from an initialized TaskChef workspace into independently openable Codex project tasks. Use for ordinary work requests in a TaskChef workspace, explicit delegation, or splitting independent work across projects. Preserve unresolved delegations for later marker-based recovery, and never use subagents, hooks, schedules, daemons, or executor-completion waiting."
4
4
  ---
5
5
 
6
6
  # TaskChef Delegate
@@ -17,7 +17,10 @@ for all deterministic workspace and task-record operations.
17
17
  - Keep only `AGENTS.md`, `taskchef.json`, and `tasks.jsonl` in a dispatcher
18
18
  workspace.
19
19
  - Use real Codex tasks, never collaboration or subagent tools.
20
- - Never use hooks, callbacks, schedules, polling, or daemons.
20
+ - Never use hooks, callbacks, schedules, daemons, indefinite polling, or
21
+ background monitors.
22
+ - Use only bounded provisional-ID resolution after `create_thread` returns a
23
+ provisional client ID. Prefer a native Codex wait or resolver when available.
21
24
  - Never wait for delegated work after executor creation.
22
25
  - Never collect transcripts or hidden reasoning.
23
26
 
@@ -33,16 +36,77 @@ for all deterministic workspace and task-record operations.
33
36
  `path` only as checkout identity. Ask when metadata does not produce one
34
37
  clear project match.
35
38
  4. Resolve native projects once and require the exact configured path.
36
- 5. Create one real Codex task per assignment using the exact configured project
37
- and a local environment on its executor host. Generate a unique task ID
38
- before creation, but do not write anything yet.
39
- 6. After executor creation returns a thread ID, immediately run
39
+ 5. Generate a lowercase full UUID task ID before creation. Prefix the complete
40
+ executor instruction with exactly `# taskchef_id=<full UUID>`, followed by a
41
+ blank line and the instruction body. Preserve this marked instruction for
42
+ recording, and note the creation time. Do not take a pre-creation thread
43
+ snapshot; the exact random marker is the correlation key.
44
+ 6. Create one real Codex task using the exact configured project, a local
45
+ environment on its executor host, the marked instruction, and a short title.
46
+ 7. When `create_thread` returns a durable `threadId`, immediately run
40
47
  `<plugin-root>/bin/taskchef.js task record --json --workspace <workspace>`.
41
48
  Send exactly `id`, `project`, `title`, `instruction`, and `threadId` as JSON
42
- on stdin. Use the configured project path for `project`. Never persist
43
- `hostId`, status, results, transcripts, or hidden reasoning.
44
- 7. If executor creation fails, do not record a task. If recording fails
49
+ on stdin. Use the configured project path for `project`, and send the marked
50
+ instruction unchanged. Never persist a provisional `clientThreadId` or
51
+ `pendingWorktreeId` as `threadId`. Never persist `hostId`, status, results,
52
+ transcripts, or hidden reasoning.
53
+ 8. When creation returns only `clientThreadId` or `pendingWorktreeId`, keep it
54
+ only for the created-thread directive and diagnostic reporting. It is not a
55
+ durable ID and cannot be passed to thread tools or converted directly.
56
+ Immediately record the marked instruction with `threadId: null` using the
57
+ command from step 7, then resolve the durable ID with this bounded workflow:
58
+
59
+ - If the current Codex tool surface provides a dedicated operation that
60
+ accepts the provisional ID and waits for or resolves its durable thread
61
+ ID, call it exactly once with a timeout of at most 30 seconds. Do not invent
62
+ an operation or pass the provisional ID to tools that require `threadId`.
63
+ - When no native operation is available, take at most two `list_threads`
64
+ snapshots with limit 50, near 10 and 30 seconds after the provisional
65
+ result. Count tool latency against the 30-second deadline. Do not start a
66
+ snapshot, candidate read, marker verification, or task-resolution write
67
+ after it.
68
+ - Filter Codex candidates by the expected host, project, creation time
69
+ (allow five seconds of clock skew), and worktree environment whenever
70
+ those fields are present. Use the title only to prioritize reads; Codex
71
+ may normalize it, so never exclude a candidate because its title differs.
72
+ - Read every remaining candidate with `read_thread`, requesting one turn and
73
+ no command output. Read candidates concurrently when the tool surface
74
+ permits. Inspect only the structured
75
+ `userMessage.content[].codexDelegation.input`; do not trust titles,
76
+ summaries, previews, plain-text echoes, or assistant output as proof.
77
+ - Accept a candidate only when the structured input's first line is exactly
78
+ the task's `# taskchef_id=<full UUID>` marker and exactly one candidate
79
+ matches. Apply the same marker verification to a thread ID returned by a
80
+ native resolver. Reject any returned or discovered thread ID equal to the
81
+ provisional identifier or in its `local:` namespace. Then use
82
+ the task-resolution command under **Later resolution** to atomically fill
83
+ the nullable field.
84
+ - Treat native-resolution, snapshot, candidate-read, wait, and task-resolution
85
+ failures as indeterminate. If the workflow ends with zero exact matches,
86
+ multiple matches, or errors, leave the already-recorded `threadId: null`,
87
+ clearly report the unresolved reason and provisional diagnostic ID, and
88
+ never guess.
89
+
90
+ 9. If executor creation fails, do not record a task. If recording fails
45
91
  after creation, still return the created task and clearly say that it is not
46
92
  in the task log. Do not delete the executor.
47
- 8. Return immediately with a created-thread directive for every success. Do
48
- not read or wait for a newly created executor.
93
+ 10. Return immediately after immediate recording or the bounded ID-resolution
94
+ workflow. Emit the appropriate created-thread directive, but label a
95
+ client-thread directive as provisional when resolution failed. Treat a
96
+ nullable record as preserved but unresolved, not as a durable task link. Do
97
+ not read an executor for progress and never wait for executor work
98
+ completion.
99
+
100
+ The package exports pure marker, candidate-filtering, and injected-adapter
101
+ orchestration helpers from `src/delegation.js` for deterministic tests and
102
+ hosts that can supply thread-tool callbacks. The standalone Node CLI cannot
103
+ call desktop thread tools; perform the tool calls in Codex and use the CLI only
104
+ for validated workspace data operations.
105
+
106
+ ## Later resolution
107
+
108
+ When a later Codex workflow finds exactly one durable thread whose structured
109
+ delegated input contains an unresolved task's exact marker, run
110
+ `<plugin-root>/bin/taskchef.js task resolve <task-id> --thread-id <thread-id> --json --workspace <workspace>`.
111
+ Never edit `tasks.jsonl` directly. The CLI permits only an idempotent one-way
112
+ transition from `threadId: null` to one unique durable thread ID.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: taskchef-report
3
- description: "Report the live state of Codex tasks recorded in a TaskChef task history. Use only when the user asks for status, outcomes, or a report about delegated work. Queries each relevant task once, never polls or waits, and never persists status or results."
3
+ description: "Report the live state of Codex tasks recorded in a TaskChef task history. Use only when the user asks for status, outcomes, or a report about delegated work. Queries each relevant task once, may resolve a nullable thread ID from one exact marker match, never polls or waits, and never persists status or results."
4
4
  ---
5
5
 
6
6
  # TaskChef Report
@@ -23,16 +23,26 @@ all deterministic task-log operations.
23
23
  `<plugin-root>/bin/taskchef.js task list --json --workspace <workspace>`
24
24
  once, then select matching entries. Ask the user if the match is ambiguous.
25
25
  - Use the full list only when the user asks for an overview of the task history.
26
- 2. Query every selected thread exactly once using immediate native snapshots,
27
- with no more than eight targets per call.
28
- 3. Summarize the live state and any reported outcome for each requested task.
26
+ 2. Separate entries whose `threadId` is `null`. For those entries, take one
27
+ `list_threads` snapshot with limit 50, filter by available project metadata,
28
+ and inspect candidate structured delegated inputs. Use title only to
29
+ prioritize candidates, never to exclude them. When exactly one candidate
30
+ starts with the task's exact marker, run
31
+ `<plugin-root>/bin/taskchef.js task resolve <task-id> --thread-id <thread-id> --json --workspace <workspace>`.
32
+ Do not resolve zero or multiple matches. Report unmatched entries as
33
+ recorded but unresolved and do not pass them to native thread tools.
34
+ 3. Query every resolved or previously durable thread exactly once using
35
+ immediate native snapshots, with no more than eight targets per call.
36
+ 4. Summarize the live state and any reported outcome for each requested task.
29
37
  Distinguish active work, requests for user input, completed work, and failed
30
38
  or partial attempts.
31
- 4. Treat each Codex task as the source of truth. The task log proves that
32
- TaskChef created the task, but it does not contain the task's current state.
33
- 5. Never update `tasks.jsonl`. Never persist status, results, transcripts,
34
- or hidden reasoning. Do not poll or wait for future activity.
39
+ 5. Treat each Codex task as the source of truth. The task log records what
40
+ TaskChef submitted, but it does not contain the task's current state.
41
+ 6. Never edit `tasks.jsonl` directly. Use `task resolve` only for one exact
42
+ marker match. Never persist status, results, transcripts, or hidden
43
+ reasoning. Do not poll or wait for future activity.
35
44
 
36
45
  If the task history is empty, say that TaskChef has not recorded any tasks. If
37
- a recorded task cannot be read, identify it by task ID and thread ID, then
38
- continue with the remaining entries.
46
+ a task has no durable thread ID, identify it by task ID and say that its marker
47
+ remains available for later recovery. If a recorded thread cannot be read,
48
+ identify it by task ID and thread ID, then continue with the remaining entries.
package/src/cli.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  readTask,
13
13
  recordTask,
14
14
  removeProject,
15
+ resolveTask,
15
16
  } from "./workspace.js";
16
17
 
17
18
  async function readStdin() {
@@ -92,7 +93,6 @@ async function initialize(args) {
92
93
  `Workspace: ${value.workspace}`,
93
94
  `Configuration: ${value.config.action}`,
94
95
  `Task log: ${value.tasks.action}`,
95
- `Legacy tasks: ${value.legacyTasks.action}`,
96
96
  `Instructions: ${value.instructions.action}`,
97
97
  `Legacy skill links removed: ${value.legacySkills.removed.length}`,
98
98
  ].join("\n"));
@@ -180,6 +180,22 @@ async function taskRecord(args) {
180
180
  return 0;
181
181
  }
182
182
 
183
+ async function taskResolve(args) {
184
+ if (!args[2] || args[2].startsWith("--")) throw new Error("task resolve requires a task ID");
185
+ validateCommandArgs(args, 3, {
186
+ values: ["--thread-id", "--workspace"],
187
+ switches: ["--json"],
188
+ });
189
+ if (!args.includes("--thread-id")) throw new Error("task resolve requires --thread-id");
190
+ const task = await resolveTask(
191
+ workspaceRoot(args),
192
+ args[2],
193
+ option(args, "--thread-id"),
194
+ );
195
+ print(task, args, (value) => `Resolved ${value.id}: ${value.threadId}`);
196
+ return 0;
197
+ }
198
+
183
199
  async function taskShow(args) {
184
200
  validateCommandArgs(args, 3, { values: ["--workspace"], switches: ["--json"] });
185
201
  print(await readTask(workspaceRoot(args), args[2]), args);
@@ -229,6 +245,7 @@ Usage:
229
245
  taskchef project list [--json] [--workspace <path>]
230
246
  taskchef project remove <name> [--json] [--workspace <path>]
231
247
  taskchef task record [--json] [--workspace <path>]
248
+ taskchef task resolve <task-id> --thread-id <thread-id> [--json] [--workspace <path>]
232
249
  taskchef task show <task-id> [--json] [--workspace <path>]
233
250
  taskchef task list [--project <name-or-path>] [--json] [--workspace <path>]
234
251
  taskchef task summary [--json] [--workspace <path>]
@@ -250,6 +267,7 @@ export async function runCli(args) {
250
267
  if (args[0] === "project" && args[1] === "list") return projectList(args);
251
268
  if (args[0] === "project" && args[1] === "remove") return projectRemove(args);
252
269
  if (args[0] === "task" && args[1] === "record") return taskRecord(args);
270
+ if (args[0] === "task" && args[1] === "resolve") return taskResolve(args);
253
271
  if (args[0] === "task" && args[1] === "show" && args[2]) return taskShow(args);
254
272
  if (args[0] === "task" && args[1] === "list") return taskList(args);
255
273
  if (args[0] === "task" && args[1] === "summary") return taskSummary(args);