taskchef 3.0.2 → 4.0.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": "3.0.2",
3
+ "version": "4.0.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/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
@@ -73,11 +73,13 @@ tasks.jsonl
73
73
  TaskChef scans eligible local Codex projects during setup and adds them to the
74
74
  managed project list in `taskchef.json`.
75
75
 
76
- `taskchef.json` defines the available routes. A project name or GitHub pull
77
- request URL in your request is usually enough for TaskChef to choose the right
78
- project. If a project needs more context, extend its optional `description`
79
- field with responsibilities and keywords that distinguish it from nearby
80
- projects.
76
+ `taskchef.json` defines the available routes. A project name or GitHub issue or
77
+ pull-request URL in your request is usually enough for TaskChef to choose the
78
+ right project. Each project's `githubRepos` field is a list. A managed
79
+ `*-workspace` project lists all child or sub-repositories there, so links into
80
+ any of them route to the workspace. If a project needs more context, extend its
81
+ optional `description` field with responsibilities and keywords that
82
+ distinguish it from nearby projects.
81
83
 
82
84
  The generated `AGENTS.md` turns ordinary requests in this project into
83
85
  delegated work. You do not need to name the delegate skill each time.
@@ -117,9 +119,18 @@ the same project.
117
119
  Open an executor and prompt it like any other Codex task. Its thread is the
118
120
  live source of truth for progress, questions, and results.
119
121
 
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.
122
+ The dispatcher workspace keeps `tasks.jsonl`, a history of submitted
123
+ delegations. New tasks are appended; the only later change allowed is filling
124
+ an unresolved task's nullable thread ID. The log records what TaskChef sent,
125
+ when it sent it, which project it selected, and which Codex task received the
126
+ work.
127
+
128
+ Every delegated instruction includes a unique `# taskchef_id=<UUID>` marker.
129
+ If worktree creation does not return a thread ID immediately, TaskChef records
130
+ the marked delegation as unresolved, then waits briefly for the durable task.
131
+ It prefers a native Codex client-ID resolver when available and otherwise makes
132
+ two exact-marker checks during a short bounded window. If it still cannot
133
+ identify exactly one task, the recorded marker remains available for recovery.
123
134
 
124
135
  ### Ask for a live report
125
136
 
@@ -152,7 +163,8 @@ $taskchef-bootstrap Diagnose this TaskChef workspace and fix any repairable conf
152
163
  Project paths must exist when you add or dispatch to them. Configure the
153
164
  repository root for a Git project. TaskChef also accepts non-Git folders. It
154
165
  detects Git status and the canonical GitHub `origin` when adding or importing
155
- a project.
166
+ a project. Repeat `--github-repo` to configure several repositories, or place a
167
+ `githubRepos` array in an import. URLs are canonicalized and deduplicated.
156
168
 
157
169
  Removing a project does not rewrite old task entries. Each entry keeps the
158
170
  project metadata that TaskChef used when it delegated the work.
@@ -161,8 +173,9 @@ project metadata that TaskChef used when it delegated the work.
161
173
 
162
174
  - TaskChef is an interactive dispatcher. It is not a scheduler, daemon, hook
163
175
  service, or background worker.
164
- - Executors are visible Codex tasks. The dispatcher does not supervise them or
165
- wait for them to finish.
176
+ - Executors are visible Codex tasks. The dispatcher may wait briefly to resolve
177
+ a worktree task's thread ID, but it does not supervise executors or wait for
178
+ them to finish.
166
179
  - TaskChef routes only to projects on the same local execution host.
167
180
  - The task history contains successful delegations, not current task status or
168
181
  task results.
@@ -213,6 +226,7 @@ taskchef project import [<file> | -]
213
226
  taskchef project list
214
227
  taskchef project remove <name>
215
228
  taskchef task record
229
+ taskchef task resolve <task-id> --thread-id <thread-id>
216
230
  taskchef task show <task-id>
217
231
  taskchef task list
218
232
  taskchef task summary
@@ -231,6 +245,8 @@ taskchef doctor --workspace <workspace>
231
245
  taskchef project add /workspace/payments \
232
246
  --name payments \
233
247
  --description "Owns payment authorization, capture, and refunds." \
248
+ --github-repo https://github.com/example/payments-api \
249
+ --github-repo https://github.com/example/payments-sdk \
234
250
  --workspace <workspace>
235
251
 
236
252
  taskchef project list --workspace <workspace>
@@ -244,20 +260,36 @@ taskchef project import projects.json --workspace <workspace>
244
260
  taskchef project import - --workspace <workspace> < projects.json
245
261
  ```
246
262
 
247
- Import merges by canonical path and preserves an existing name or description
248
- when the imported object omits it. `--replace` replaces the configured project
249
- set.
263
+ Import merges by canonical path, preserves an existing name or description
264
+ when the imported object omits it, and unions repository lists without
265
+ duplicates. `--replace` replaces the configured project set.
266
+
267
+ The current configuration schema is version 2. Version 1 remains readable:
268
+ legacy `githubRepo: null` normalizes to `githubRepos: []`, and a legacy string
269
+ normalizes to a one-item `githubRepos` list. `workspace init` persists this
270
+ migration atomically; other configuration writes also emit version 2. Legacy
271
+ task lines remain readable without an eager rewrite of the append-only history.
250
272
 
251
273
  ### Task history
252
274
 
253
- `task record` reads one successful delegation from standard input. The
275
+ `task record` reads one submitted delegation from standard input. The
254
276
  `project` value is the exact configured project path:
255
277
 
256
278
  ```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..."}' |
279
+ 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
280
  taskchef task record --json --workspace <workspace>
259
281
  ```
260
282
 
283
+ If a task has `threadId: null`, Codex can later find its exact marker and pass
284
+ the verified durable ID to the CLI. Resolution is atomic and only permits the
285
+ one-way transition from null to one unique thread ID:
286
+
287
+ ```sh
288
+ taskchef task resolve c0f010ff-84f2-4838-a69d-0ff1f5d721d7 \
289
+ --thread-id 019f9d46-f42c-7482-9707-3c107bf241ee \
290
+ --workspace <workspace>
291
+ ```
292
+
261
293
  Inspect the task history without querying Codex tasks:
262
294
 
263
295
  ```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
@@ -55,20 +54,23 @@ the workspace.
55
54
 
56
55
  ```json
57
56
  {
58
- "schemaVersion": 1,
57
+ "schemaVersion": 2,
59
58
  "projects": [
60
59
  {
61
60
  "name": "payments-api",
62
61
  "path": "/workspace/payments-api",
63
62
  "isGitRepository": true,
64
- "githubRepo": "https://github.com/example/payments-api",
63
+ "githubRepos": [
64
+ "https://github.com/example/payments-api",
65
+ "https://github.com/example/payments-sdk"
66
+ ],
65
67
  "description": "Owns payment authorization, capture, refunds, and provider integrations."
66
68
  },
67
69
  {
68
70
  "name": "local-data-tools",
69
71
  "path": "/workspace/local-data-tools",
70
72
  "isGitRepository": false,
71
- "githubRepo": null
73
+ "githubRepos": []
72
74
  }
73
75
  ]
74
76
  }
@@ -80,18 +82,31 @@ the workspace.
80
82
  - `path` is the normalized, canonical local directory. A Git project must use
81
83
  its repository root.
82
84
  - `isGitRepository` identifies Git and non-Git projects.
83
- - `githubRepo` is a canonical GitHub repository URL or `null`.
85
+ - `githubRepos` is a deduplicated list of canonical GitHub repository URLs. Use
86
+ `[]` when the project advertises no repositories. A managed `*-workspace`
87
+ project lists each child or sub-repository that should route to it.
84
88
  - `description` is optional routing context.
85
89
 
86
- TaskChef classifies work against `name`, `githubRepo`, and `description`. The
87
- path identifies the checkout but is not a routing hint. TaskChef asks the user
88
- when metadata does not produce one clear match.
90
+ TaskChef classifies work against `name`, every URL in `githubRepos`, and
91
+ `description`. The path identifies the checkout but is not a routing hint. For
92
+ a GitHub issue or pull-request URL, TaskChef compares canonical,
93
+ case-insensitive owner/repository identities across every configured list. It
94
+ ignores the issue or PR suffix, `http` versus `https`, optional `www`, trailing
95
+ slashes, and trailing `.git`. It routes only when one configured project
96
+ matches; ambiguous and unmatched URLs are never guessed.
89
97
 
90
98
  `project add` and `project import` detect Git status, exact Git roots, and
91
- canonical GitHub origins. Import merges by canonical path and preserves an
92
- existing name or description when omitted. `--replace` replaces the configured
93
- set. Removing or replacing a project does not alter historical task
94
- entries.
99
+ canonical GitHub origins. `--github-repo` is repeatable. Import merges by
100
+ canonical path, preserves an existing name or description when omitted, and
101
+ unions existing and imported repository lists without duplicates. `--replace`
102
+ replaces the configured set. Removing or replacing a project does not alter
103
+ historical task entries.
104
+
105
+ Schema version 2 replaces the string-or-null `githubRepo` field with the
106
+ list-valued `githubRepos` field. Schema-version-1 configurations remain
107
+ compatible: reads normalize a legacy string to one canonical list item and
108
+ legacy `null` to `[]`; `workspace init` persists the migration atomically, and
109
+ any later configuration write emits version 2.
95
110
 
96
111
  The configuration does not store dispatcher identity, execution modes,
97
112
  schedules, task status, results, host information, or the workspace path.
@@ -101,36 +116,81 @@ schedules, task status, results, host information, or the workspace path.
101
116
  `tasks.jsonl` contains one compact JSON object per line, in append order:
102
117
 
103
118
  ```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"}
119
+ {"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\nAdd structured logs for failed payment retries and test them.","threadId":"019f9d46-f42c-7482-9707-3c107bf241ee","createdAt":"2026-08-08T10:00:00.000Z"}
105
120
  ```
106
121
 
107
122
  - `schemaVersion` identifies the task entry format.
108
123
  - `id` is a unique TaskChef task identifier.
109
124
  - `project` is the complete configured project snapshot used for routing.
110
125
  - `title` is a short task name.
111
- - `instruction` is the complete executor instruction.
112
- - `threadId` identifies the created Codex task.
126
+ - `instruction` is the complete executor instruction, including its first-line
127
+ `# taskchef_id=<full UUID>` correlation marker.
128
+ - `threadId` identifies the created Codex task, or is `null` when creation was
129
+ accepted but bounded marker resolution did not find one durable task ID.
113
130
  - `createdAt` is the dispatch time as an ISO 8601 timestamp.
114
131
 
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.
132
+ Every entry has exactly these fields. IDs and non-null thread IDs must be
133
+ unique; any number of unresolved entries may have `threadId: null`. The file is
134
+ empty or newline terminated, with no blank lines. TaskChef rejects a malformed
135
+ log instead of skipping bad entries. Writers replace the complete validated
136
+ file atomically under a workspace lock, so an interrupted write leaves either
137
+ the old history or the complete new history.
138
+
139
+ Task creation appends entries. The only permitted mutation is an atomic,
140
+ idempotent `task resolve` transition from `threadId: null` to one unique durable
141
+ thread ID. Resolution requires the stored instruction's exact marker to match
142
+ the task ID. A resolved or mismatched entry cannot be overwritten.
120
143
 
121
144
  The project snapshot preserves the route even if the project is renamed,
122
145
  moved, or removed later. Entries never contain status, result, transcript,
123
146
  hidden reasoning, `hostId`, or update timestamps.
124
147
 
148
+ New task entries use schema version 2 and list-valued project snapshots.
149
+ Version 1 entries with string or null repository metadata remain readable and
150
+ normalize to version 2 in API and CLI output. TaskChef does not eagerly rewrite
151
+ legacy history solely for this migration.
152
+
125
153
  ## Dispatch workflow
126
154
 
127
155
  For each assignment, `$taskchef-delegate`:
128
156
 
129
157
  1. loads and validates configured projects
130
158
  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.
159
+ 3. generates a full UUID and prefixes the instruction with its exact
160
+ `# taskchef_id=<UUID>` marker
161
+ 4. creates a real Codex task at the exact configured path
162
+ 5. appends a task entry immediately when creation returns a durable thread ID
163
+ 6. when creation returns only a provisional client ID, immediately appends the
164
+ marked entry with `threadId: null`, then prefers one native client-ID wait or
165
+ resolution call with a 30-second timeout when Codex exposes one
166
+ 7. when no native operation is available, takes at most two recent-thread
167
+ snapshots near 10 and 30 seconds after the provisional result, filters
168
+ candidates by available host/project/time/worktree metadata, uses title only
169
+ as an advisory ordering hint, and accepts only one thread whose structured
170
+ delegated input starts with the exact marker
171
+ 8. atomically fills the nullable thread ID after an exact match
172
+ 9. returns after recording or after reporting that bounded resolution was
173
+ unresolved, without waiting for executor work completion.
174
+
175
+ The exact random marker makes a pre-creation thread snapshot unnecessary.
176
+ Creation-time filtering allows five seconds of clock skew. Candidate reads run
177
+ concurrently where the host permits and inspect only structured
178
+ `codexDelegation.input`, never untrusted title, summary, preview, or plain-text
179
+ marker echoes. A native resolver result is verified against the same structured
180
+ marker before persistence. Zero exact matches time out unresolved; multiple
181
+ exact matches are ambiguous. Snapshot, candidate-read, native-resolution, or
182
+ task-resolution errors leave the already-recorded nullable entry intact. No
183
+ snapshot, candidate read, marker verification, or task-resolution write starts
184
+ after the 30-second deadline, so tool latency can reduce the number of attempts.
185
+ A `clientThreadId`, `pendingWorktreeId`, or ID in the documented provisional
186
+ `local:` namespace remains diagnostic context and is rejected from every path
187
+ that could persist the canonical `threadId` field.
188
+
189
+ Desktop thread tools are available to the Codex skill, not to the standalone
190
+ Node CLI. The package therefore exposes testable marker/filter/orchestration
191
+ helpers with injected thread-tool callbacks, while the skill owns the actual
192
+ desktop-tool calls and the CLI remains responsible only for validated data
193
+ operations.
134
194
 
135
195
  A failed executor creation produces no entry. If executor creation succeeds but
136
196
  the append fails, the executor remains valid and TaskChef tells the user that
@@ -144,19 +204,23 @@ The CLI reads persisted history without contacting Codex:
144
204
  - `task list` returns entries in append order, optionally filtered by
145
205
  historical project name or exact path.
146
206
  - `task summary` returns the total and per-project counts.
207
+ - `task resolve <id> --thread-id <thread-id>` atomically fills one nullable
208
+ thread ID after Codex verifies the exact structured marker match.
147
209
 
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.
210
+ When the user requests current state or outcomes, `$taskchef-report` makes one
211
+ marker-based discovery pass for nullable entries and uses `task resolve` only
212
+ for a single exact match. It reports unmatched entries as unresolved and
213
+ queries every durable thread ID exactly once, in batches of no more than eight.
214
+ The report does not poll, wait, persist status or results, or create a scheduled
215
+ job.
152
216
 
153
217
  ## Boundaries
154
218
 
155
219
  TaskChef does not include:
156
220
 
157
221
  - lifecycle status or result persistence
158
- - task callbacks, hooks, polling, daemons, heartbeats, or schedules
159
- - arbitrary Codex task discovery
222
+ - task callbacks, hooks, indefinite polling, daemons, heartbeats, or schedules
223
+ - arbitrary Codex task discovery beyond bounded marker-based creation recovery
160
224
  - remote hosts or `hostId` storage
161
225
  - transcript or hidden-reasoning collection
162
226
  - one-active-task-per-project restrictions
@@ -170,10 +234,13 @@ TaskChef does not include:
170
234
  2. Project metadata routes an unambiguous request to the correct local project.
171
235
  3. A successful delegation creates a visible Codex task and appends its thread
172
236
  ID with a project snapshot.
173
- 4. The dispatcher returns without waiting for execution.
174
- 5. Several independent assignments can create several entries, including
237
+ 4. A provisional creation with one exact marker match records its durable
238
+ thread ID; zero or multiple matches record `threadId: null` for later
239
+ recovery.
240
+ 5. The dispatcher returns without waiting for execution.
241
+ 6. Several independent assignments can create several entries, including
175
242
  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
243
+ 7. Task history commands return deterministic entries and project counts.
244
+ 8. A live report queries each relevant task once and writes nothing.
245
+ 9. Malformed JSONL, duplicate IDs, duplicate thread IDs, and symlinked managed
179
246
  files fail safely.
@@ -9,6 +9,8 @@ This repository is a TaskChef dispatcher workspace.
9
9
  - For every actionable work request, use `$taskchef-delegate`
10
10
  automatically, even when the user does not explicitly say "delegate" or
11
11
  mention TaskChef.
12
+ - GitHub issue and pull-request URLs may identify any repository advertised by
13
+ a configured project, including child repositories of managed workspaces.
12
14
  - Do not perform delegated work directly in the dispatcher thread.
13
15
  - Return immediately after dispatch, as required by `$taskchef-delegate`.
14
16
  - Answer directly only when the user explicitly asks about TaskChef itself or
package/index.js CHANGED
@@ -14,7 +14,31 @@ 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
+ canonicalGithubRepository,
25
+ matchProjectForGithubUrl,
26
+ normalizeGithubRepositories,
27
+ } from "./src/github.js";
28
+
29
+ export {
30
+ THREAD_RESOLUTION_CHECKPOINTS_MS,
31
+ THREAD_RESOLUTION_CLOCK_SKEW_MS,
32
+ THREAD_RESOLUTION_RECENT_LIMIT,
33
+ THREAD_RESOLUTION_TIMEOUT_MS,
34
+ createAndRecordDelegation,
35
+ filterThreadCandidates,
36
+ hasExactTaskChefMarker,
37
+ listThreadEntries,
38
+ isProvisionalThreadId,
39
+ normalizeDurableThreadId,
40
+ parseTaskChefMarker,
41
+ prepareDelegation,
42
+ structuredDelegatedInputs,
43
+ taskChefMarker,
44
+ } 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": "4.0.0",
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.
@@ -40,12 +39,34 @@ all deterministic workspace operations.
40
39
  projects are outside the v1 contract.
41
40
  2. Add one project with `project add <path>`, normally supplying `--name` and a
42
41
  curated `--description`. The CLI detects Git status, exact Git root, and a
43
- canonical GitHub `origin`. Use `--no-github` or `--github-repo` only to
44
- override detection.
42
+ canonical GitHub `origin`. Repeat `--github-repo <url>` to advertise several
43
+ repositories, or use `--no-github` for an empty list. A managed
44
+ `*-workspace` project must list all of its child or sub-repositories so issue
45
+ and pull-request URLs route to that workspace.
45
46
  3. Bulk import with `project import <file|-> --json`. Input is a JSON array of
46
47
  objects containing `path` plus optional `name`, `description`, and
47
- `githubRepo`. Import merges by canonical path and preserves an existing name
48
- or description when omitted. Use `--replace` only when the user explicitly
49
- requests replacement.
48
+ `githubRepos`, which is always a JSON array of GitHub repository URLs. Import
49
+ merges by canonical path, preserves an existing name or description when
50
+ omitted, and unions existing and imported repository lists without
51
+ duplicates. Use `--replace` only when the user explicitly requests
52
+ replacement.
50
53
  4. Inspect configured projects with `project list --json`. Remove by name with
51
54
  `project remove`. Existing task entries keep their project snapshots.
55
+
56
+ Example managed-workspace import entry:
57
+
58
+ ```json
59
+ {
60
+ "name": "skills-workspace",
61
+ "path": "/workspace/skills-workspace",
62
+ "githubRepos": [
63
+ "https://github.com/example/skill-one",
64
+ "https://github.com/example/skill-two"
65
+ ],
66
+ "description": "Manages the listed child skill repositories."
67
+ }
68
+ ```
69
+
70
+ `workspace init` safely migrates schema-version-1 configuration: a string
71
+ `githubRepo` becomes a one-item `githubRepos` list and `null` becomes
72
+ `githubRepos: []`. All subsequent configuration writes use schema version 2.
@@ -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
 
@@ -29,20 +32,88 @@ for all deterministic workspace and task-record operations.
29
32
  `$taskchef-bootstrap` if the workspace is missing or unhealthy.
30
33
  2. Split the request into the smallest independently useful outcomes. Include
31
34
  constraints, expected testing, and reporting in every instruction.
32
- 3. Classify against configured `name`, `githubRepo`, and `description`. Use
33
- `path` only as checkout identity. Ask when metadata does not produce one
34
- clear project match.
35
+ 3. Classify against configured `name`, every URL in the `githubRepos` list, and
36
+ `description`. Use `path` only as checkout identity. Managed `*-workspace`
37
+ projects advertise their child or sub-repositories in this list.
38
+ When the prompt contains a GitHub issue or pull-request URL, canonicalize
39
+ its case-insensitive owner/repository identity, ignoring `http` versus
40
+ `https`, an optional `www`, a trailing slash or `.git`, and the issue or PR
41
+ suffix. Check that identity against every repository URL of every configured
42
+ project. Route on this evidence only when exactly one configured project
43
+ matches. Ask instead of guessing when no project or several projects match.
35
44
  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
45
+ 5. Generate a lowercase full UUID task ID before creation. Prefix the complete
46
+ executor instruction with exactly `# taskchef_id=<full UUID>`, followed by a
47
+ blank line and the instruction body. Preserve this marked instruction for
48
+ recording, and note the creation time. Do not take a pre-creation thread
49
+ snapshot; the exact random marker is the correlation key.
50
+ 6. Create one real Codex task using the exact configured project, a local
51
+ environment on its executor host, the marked instruction, and a short title.
52
+ 7. When `create_thread` returns a durable `threadId`, immediately run
40
53
  `<plugin-root>/bin/taskchef.js task record --json --workspace <workspace>`.
41
54
  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
55
+ on stdin. Use the configured project path for `project`, and send the marked
56
+ instruction unchanged. Never persist a provisional `clientThreadId` or
57
+ `pendingWorktreeId` as `threadId`. Never persist `hostId`, status, results,
58
+ transcripts, or hidden reasoning.
59
+ 8. When creation returns only `clientThreadId` or `pendingWorktreeId`, keep it
60
+ only for the created-thread directive and diagnostic reporting. It is not a
61
+ durable ID and cannot be passed to thread tools or converted directly.
62
+ Immediately record the marked instruction with `threadId: null` using the
63
+ command from step 7, then resolve the durable ID with this bounded workflow:
64
+
65
+ - If the current Codex tool surface provides a dedicated operation that
66
+ accepts the provisional ID and waits for or resolves its durable thread
67
+ ID, call it exactly once with a timeout of at most 30 seconds. Do not invent
68
+ an operation or pass the provisional ID to tools that require `threadId`.
69
+ - When no native operation is available, take at most two `list_threads`
70
+ snapshots with limit 50, near 10 and 30 seconds after the provisional
71
+ result. Count tool latency against the 30-second deadline. Do not start a
72
+ snapshot, candidate read, marker verification, or task-resolution write
73
+ after it.
74
+ - Filter Codex candidates by the expected host, project, creation time
75
+ (allow five seconds of clock skew), and worktree environment whenever
76
+ those fields are present. Use the title only to prioritize reads; Codex
77
+ may normalize it, so never exclude a candidate because its title differs.
78
+ - Read every remaining candidate with `read_thread`, requesting one turn and
79
+ no command output. Read candidates concurrently when the tool surface
80
+ permits. Inspect only the structured
81
+ `userMessage.content[].codexDelegation.input`; do not trust titles,
82
+ summaries, previews, plain-text echoes, or assistant output as proof.
83
+ - Accept a candidate only when the structured input's first line is exactly
84
+ the task's `# taskchef_id=<full UUID>` marker and exactly one candidate
85
+ matches. Apply the same marker verification to a thread ID returned by a
86
+ native resolver. Reject any returned or discovered thread ID equal to the
87
+ provisional identifier or in its `local:` namespace. Then use
88
+ the task-resolution command under **Later resolution** to atomically fill
89
+ the nullable field.
90
+ - Treat native-resolution, snapshot, candidate-read, wait, and task-resolution
91
+ failures as indeterminate. If the workflow ends with zero exact matches,
92
+ multiple matches, or errors, leave the already-recorded `threadId: null`,
93
+ clearly report the unresolved reason and provisional diagnostic ID, and
94
+ never guess.
95
+
96
+ 9. If executor creation fails, do not record a task. If recording fails
45
97
  after creation, still return the created task and clearly say that it is not
46
98
  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.
99
+ 10. Return immediately after immediate recording or the bounded ID-resolution
100
+ workflow. Emit the appropriate created-thread directive, but label a
101
+ client-thread directive as provisional when resolution failed. Treat a
102
+ nullable record as preserved but unresolved, not as a durable task link. Do
103
+ not read an executor for progress and never wait for executor work
104
+ completion.
105
+
106
+ The package exports pure repository canonicalization and unique URL matching
107
+ helpers from `src/github.js`, plus marker, candidate-filtering, and
108
+ injected-adapter orchestration helpers from `src/delegation.js`, for
109
+ deterministic tests and hosts that can supply thread-tool callbacks. The
110
+ standalone Node CLI cannot call desktop thread tools; perform the tool calls in
111
+ Codex and use the CLI only for validated workspace data operations.
112
+
113
+ ## Later resolution
114
+
115
+ When a later Codex workflow finds exactly one durable thread whose structured
116
+ delegated input contains an unresolved task's exact marker, run
117
+ `<plugin-root>/bin/taskchef.js task resolve <task-id> --thread-id <thread-id> --json --workspace <workspace>`.
118
+ Never edit `tasks.jsonl` directly. The CLI permits only an idempotent one-way
119
+ transition from `threadId: null` to one unique durable thread ID.