taskchef 7.14.2 → 7.15.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/.codex-plugin/plugin.json +1 -1
- package/README.md +10 -5
- package/docs/firstmate-taskchef-comparison.md +5 -5
- package/docs/spec.md +44 -29
- package/docs/workflows.md +38 -29
- package/package.json +1 -1
- package/skills/taskchef-delegate/SKILL.md +4 -2
- package/skills/taskchef-executor/SKILL.md +18 -12
- package/src/cli.js +8 -3
- package/src/dashboard/app.js +7 -7
- package/src/dashboard/state.js +61 -9
- package/src/dashboard.js +5 -0
- package/src/delegation.js +18 -3
- package/src/mcp.js +9 -2
- package/src/workspace.js +282 -109
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ The canonical workspace is `~/.agents/taskchef`. TaskChef owns only:
|
|
|
49
49
|
```text
|
|
50
50
|
AGENTS.md managed dispatcher instructions plus user additions
|
|
51
51
|
taskchef.json schema-2 configured projects and routing metadata
|
|
52
|
-
tasks.jsonl one task snapshot per line (schema
|
|
52
|
+
tasks.jsonl one task snapshot per line (schema 9; schema 4-8 migration supported)
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
List or change routing targets conversationally:
|
|
@@ -124,6 +124,10 @@ appends a `turns` entry that pairs that request with a null result while working
|
|
|
124
124
|
then fills the same entry with the semantic outcome. A follow-up therefore shows
|
|
125
125
|
its own request with “In progress,” never the preceding turn's result. Returned
|
|
126
126
|
tasks still derive `results` and `lastResult` as compatibility projections.
|
|
127
|
+
Every entry has a required `turnRef`, which is the lifecycle identity. When
|
|
128
|
+
Codex exposes a native turn ID, both `turnRef` and `turnId` contain that value.
|
|
129
|
+
Otherwise the executor retains a fresh UUID in `turnRef` and stores
|
|
130
|
+
`turnId: null`; native turn lookup is never a prerequisite for work or reporting.
|
|
127
131
|
If Codex crashes, an MCP call is lost, or the app restarts before that terminal
|
|
128
132
|
report, the next newer `working` report atomically marks the unfinished turn
|
|
129
133
|
`interrupted` and appends the new active turn. `interrupted` is TaskChef-authored
|
|
@@ -246,8 +250,9 @@ taskchef doctor
|
|
|
246
250
|
|
|
247
251
|
`doctor` is read-only. `workspace init` creates missing files and refreshes
|
|
248
252
|
managed instructions. `workspace migrate` explicitly upgrades supported schema
|
|
249
|
-
4
|
|
250
|
-
source and converted log before writing, creates an
|
|
253
|
+
4-8 task lines to schema 9 under the workspace lock. It validates task and turn
|
|
254
|
+
counts plus the complete source and converted log before writing, creates an
|
|
255
|
+
exclusive `tasks.jsonl.pre-v9-*.bak`
|
|
251
256
|
backup, atomically replaces the log, validates the result, and becomes an
|
|
252
257
|
idempotent no-op after migration. If replacement fails, the original remains
|
|
253
258
|
or the reported backup can be restored; unsupported or invalid input is rejected
|
|
@@ -256,9 +261,9 @@ before a backup or rewrite.
|
|
|
256
261
|
If a new record has no thread ID, the executor is link-pending. Reopen that
|
|
257
262
|
executor so its first action can retry `link_task`. Do not guess an identity
|
|
258
263
|
or edit `tasks.jsonl`. If native task creation failed, the record is retained
|
|
259
|
-
as `failed` with null thread and turn IDs.
|
|
264
|
+
as `failed` with a retained fallback `turnRef` and null thread and Codex turn IDs.
|
|
260
265
|
|
|
261
|
-
Schemas other than 4, 5, 6, 7, and
|
|
266
|
+
Schemas other than 4, 5, 6, 7, 8, and 9 remain unsupported. Retain such a workspace
|
|
262
267
|
unchanged and create a current workspace; the migration command deliberately
|
|
263
268
|
does not guess how to convert unknown formats.
|
|
264
269
|
|
|
@@ -115,10 +115,10 @@ creation. The executor then reads its own `CODEX_THREAD_ID` and calls
|
|
|
115
115
|
The task record keeps one immutable intent/project snapshot plus the latest
|
|
116
116
|
identity and semantic result fields.
|
|
117
117
|
|
|
118
|
-
**Fact:**
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
native turn ID
|
|
118
|
+
**Fact:** Linking accepts one atomic null-to-canonical-Codex-UUIDv7 transition.
|
|
119
|
+
Exact retries are idempotent, conflicts fail, and link interruption remains
|
|
120
|
+
visibly pending. Schema 9 lifecycle callbacks use a required `turnRef`: the
|
|
121
|
+
native Codex turn ID when available, or a retained UUID with `turnId: null`.
|
|
122
122
|
|
|
123
123
|
### FirstMate
|
|
124
124
|
|
|
@@ -162,7 +162,7 @@ backends have different verification or experimental status.
|
|
|
162
162
|
|
|
163
163
|
**Fact:** Persisted statuses are `working`, `needs_input`, `completed`, and
|
|
164
164
|
`failed`. Executors report
|
|
165
|
-
`working` at turn start and a semantic state before ending. Schema
|
|
165
|
+
`working` at turn start and a semantic state before ending. Schema 9 preserves
|
|
166
166
|
the paired turn timeline and semantic-only compatibility results while a newer
|
|
167
167
|
turn is working. If a terminal report is lost, the next valid working start
|
|
168
168
|
closes the predecessor with a timeline-only `interrupted` outcome rather than
|
package/docs/spec.md
CHANGED
|
@@ -26,7 +26,8 @@ is dated research, not contract.
|
|
|
26
26
|
| **Turn timeline** | The ordered collection pairing each turn's concise request summary with its semantic result, TaskChef-generated interruption outcome, or current in-progress state. |
|
|
27
27
|
| **Interrupted turn** | A formerly active turn that lacked a terminal report when a newer valid turn started; TaskChef closes it with the fixed timeline-only `interrupted` outcome. |
|
|
28
28
|
| **Last semantic result** | The final result-history entry, exposed through the derived `lastResult` compatibility alias. |
|
|
29
|
-
| **
|
|
29
|
+
| **Turn reference** | Required lifecycle identity for one executor prompt. It is the native Codex turn ID when available, otherwise a retained client-generated UUID. |
|
|
30
|
+
| **Current turn ID** | Optional Codex metadata for the reported prompt; null when native turn reading is unavailable. |
|
|
30
31
|
| **Dashboard** | The loopback, read-only UI derived from validated workspace snapshots and bounded native actions. |
|
|
31
32
|
| **Skill** | One packaged agent procedure: `taskchef-bootstrap`, `taskchef-delegate`, `taskchef-executor`, or `taskchef-copilot`. |
|
|
32
33
|
|
|
@@ -71,8 +72,8 @@ Repository URLs MUST canonicalize to `https://github.com/<owner>/<repository>`
|
|
|
71
72
|
and be case-insensitively deduplicated.
|
|
72
73
|
|
|
73
74
|
`tasks.jsonl` MUST contain zero or more newline-terminated schema-4 through
|
|
74
|
-
schema-
|
|
75
|
-
migration/read formats; every new record and state mutation MUST write schema
|
|
75
|
+
schema-9 records, one per line. Schemas 4 through 8 are supported
|
|
76
|
+
migration/read formats; every new record and state mutation MUST write schema 9. Other schemas
|
|
76
77
|
or unsupported fields MUST be rejected without conversion.
|
|
77
78
|
Reads and writes MUST reject symlinked managed files. Mutations
|
|
78
79
|
MUST hold the shared workspace lock and replace state atomically; read-only
|
|
@@ -84,7 +85,7 @@ Every record MUST contain exactly these fields:
|
|
|
84
85
|
|
|
85
86
|
| Field | Contract |
|
|
86
87
|
| --- | --- |
|
|
87
|
-
| `schemaVersion` | Integer `
|
|
88
|
+
| `schemaVersion` | Integer `9`; schema-4/5/6/7/8 records remain readable until explicit migration or their next mutation. |
|
|
88
89
|
| `id` | Unique safe TaskChef ID; delegation uses a lowercase full UUID. |
|
|
89
90
|
| `project` | Immutable configured-project snapshot. |
|
|
90
91
|
| `title` | Non-empty display title. |
|
|
@@ -93,16 +94,17 @@ Every record MUST contain exactly these fields:
|
|
|
93
94
|
| `createdAt` | ISO 8601 creation timestamp. |
|
|
94
95
|
| `status` | `working`, `needs_input`, `completed`, or `failed`. |
|
|
95
96
|
| `summary` | Null while working; otherwise the current semantic state's non-empty summary of at most 2,000 characters. |
|
|
96
|
-
| `
|
|
97
|
+
| `turnRef` | Null before reporting begins; otherwise the required lifecycle identity of the current turn. Self-linking journeys use a native or fallback UUID. Migrated low-level `report_result` compatibility records may retain an opaque ref equal to their opaque `turnId`. |
|
|
98
|
+
| `turnId` | Optional Codex metadata. When non-null it equals `turnRef`; null indicates fallback identity. |
|
|
97
99
|
| `updatedAt` | ISO 8601 timestamp not earlier than `createdAt` or the prior `updatedAt`; clock rollback cannot backdate a transition. |
|
|
98
100
|
| `updatedBy` | `dispatcher` or `mcp`. |
|
|
99
|
-
| `turns` | Ordered oldest-first array of `{turnId, requestSummary, startedAt, result}`. `requestSummary` is null only for migrated/compatibility turns. `result` is null only for the latest working turn, a semantic `{status, summary, updatedAt}` result, or the TaskChef
|
|
101
|
+
| `turns` | Ordered oldest-first array of `{turnRef, turnId, requestSummary, startedAt, result}`. Every `turnRef` is required. `requestSummary` is null only for migrated/compatibility turns. `result` is null only for the latest working turn, a semantic `{status, summary, updatedAt}` result, or the fixed TaskChef `interrupted` outcome. New self-linking turn refs are unique. A migrated low-level opaque record may retain one final reused native-derived ref for its legacy ambiguity. |
|
|
100
102
|
|
|
101
103
|
Returned Task objects MUST additionally expose `latestTurn` as null for an empty
|
|
102
104
|
timeline or the final `turns` entry. They MUST derive `results` only from
|
|
103
105
|
semantic `needs_input`, `completed`, and `failed` turn results and `lastResult`
|
|
104
106
|
from the final derived semantic result. Interrupted outcomes MUST be excluded.
|
|
105
|
-
These projections MUST NOT be persisted in schema
|
|
107
|
+
These projections MUST NOT be persisted in schema 9 and remain compatibility
|
|
106
108
|
aliases for existing callers.
|
|
107
109
|
|
|
108
110
|
Task IDs and non-null thread identities MUST be unique. The immutable intent
|
|
@@ -129,12 +131,15 @@ the final link, preserving the delegate skill's immediate-return contract.
|
|
|
129
131
|
5. It MUST create exactly one native Codex executor and return immediately.
|
|
130
132
|
6. The executor MUST read its own `CODEX_THREAD_ID` and call `link_task`
|
|
131
133
|
before substantive work. It MUST NOT use parent/session identity or guess.
|
|
132
|
-
7. After initial linking, the executor MUST
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
7. After initial linking, the executor MUST establish a `turnRef`. It MUST use
|
|
135
|
+
the native Codex turn ID for both `turnRef` and `turnId` when available;
|
|
136
|
+
otherwise it MUST retain a fresh UUID `turnRef` and use `turnId: null`.
|
|
137
|
+
It MUST call `report_state` with that identity, `working`, and no summary before work.
|
|
138
|
+
8. Before ending, it MUST call `report_state` with the same `turnRef` and
|
|
139
|
+
`turnId` metadata as the working report and a
|
|
135
140
|
semantic status and concise summary.
|
|
136
|
-
9. A follow-up MUST report `working` with
|
|
137
|
-
It MUST NOT reuse a prior
|
|
141
|
+
9. A follow-up MUST report `working` with a new `turnRef` before work.
|
|
142
|
+
It MUST NOT reuse a prior prompt's ref. If the preceding turn is still unfinished,
|
|
138
143
|
TaskChef MUST atomically close it as `interrupted` before appending the new
|
|
139
144
|
working turn; the executor MUST NOT report semantic `failed` for recovery.
|
|
140
145
|
|
|
@@ -146,7 +151,8 @@ managed child repository and its workspace/root repository MUST include both
|
|
|
146
151
|
pull-request URLs. Executors MUST NOT guess unresolved repository identity.
|
|
147
152
|
|
|
148
153
|
If native creation fails after recording, the dispatcher MUST call
|
|
149
|
-
`report_state` with `failed`,
|
|
154
|
+
`report_state` with `failed`, a retained fallback UUID `turnRef`, null thread
|
|
155
|
+
and Codex turn IDs, and a bounded summary.
|
|
150
156
|
A link failure MUST remain visible and retryable; the executor MUST report it
|
|
151
157
|
visibly and MUST NOT continue substantive work.
|
|
152
158
|
|
|
@@ -254,7 +260,7 @@ new preparation values, though it writes no state.
|
|
|
254
260
|
|
|
255
261
|
**Structured output:** `{ task: Task }`.
|
|
256
262
|
|
|
257
|
-
The returned task has schema
|
|
263
|
+
The returned task has schema 9, `working`, null summary/turn/thread/latestTurn/lastResult,
|
|
258
264
|
empty `turns` and derived `results` arrays,
|
|
259
265
|
`updatedBy: dispatcher`, and equal creation/update timestamps. Duplicate IDs,
|
|
260
266
|
unknown projects, malformed markers, and invalid input fail. Repeating a
|
|
@@ -292,23 +298,29 @@ marker, or ineligible state fails.
|
|
|
292
298
|
| --- | --- |
|
|
293
299
|
| `taskId` | Non-empty string. |
|
|
294
300
|
| `threadId` | Matching non-empty ID for a linked task; null only for creation failure. |
|
|
295
|
-
| `
|
|
301
|
+
| `turnRef` | Stable UUID for this prompt. It may be omitted only by legacy callers that supply a non-null `turnId`, in which case TaskChef derives the same value. |
|
|
302
|
+
| `turnId` | Optional Codex metadata. When present it MUST equal `turnRef`; null is valid for fallback UUID refs. Maximum 256 characters. |
|
|
296
303
|
| `status` | `working`, `needs_input`, `completed`, or `failed`. |
|
|
297
304
|
| `summary` | Omitted or null for `working`; required non-empty string of at most 2,000 characters otherwise. Known GitHub issues and pull requests use canonical URLs. |
|
|
298
305
|
| `requestSummary` | Concise current request of at most 1,000 characters for `working`; optional for backward compatibility and omitted for semantic states. A known selected GitHub repository uses its canonical URL. |
|
|
299
306
|
|
|
300
307
|
**Structured output:** `{ task: Task }`.
|
|
301
308
|
|
|
302
|
-
For a linked self-linking journey, a new `working` state MUST
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
For a linked self-linking journey, a new `working` state MUST use a `turnRef`
|
|
310
|
+
not already assigned to a different turn. An exact retry of the current
|
|
311
|
+
working start MUST return the current task without mutation.
|
|
312
|
+
When `turnId` is present, its native-backed `turnRef` MUST also be newer than
|
|
313
|
+
every previously stored native-backed ref. Fallback UUID refs are opaque and
|
|
314
|
+
MUST NOT be lexically ordered.
|
|
305
315
|
A semantic state MUST match the current working turn. Conflicting or stale
|
|
306
|
-
state fails. A null-
|
|
307
|
-
|
|
316
|
+
state fails. A null-thread record accepts only a fresh executor creation
|
|
317
|
+
`failed` state with a retained `turnRef` and null thread/turn IDs. Starting work appends one turn with
|
|
308
318
|
its request and a null result. When the previous latest turn is unfinished, the
|
|
309
319
|
same locked atomic rewrite MUST first fill it with the fixed TaskChef-generated
|
|
310
320
|
`interrupted` outcome. The semantic report fills the active turn's result.
|
|
311
|
-
An identical retry for
|
|
321
|
+
An identical retry for the current settled turn returns success without an
|
|
322
|
+
append. Once a newer turn starts, every callback for a historical ref is stale
|
|
323
|
+
and fails, even when its content exactly matches the stored callback. A
|
|
312
324
|
different request or result for the same turn, a stale turn, or a semantic
|
|
313
325
|
result that does not match the active working turn MUST fail.
|
|
314
326
|
|
|
@@ -325,8 +337,9 @@ MUST contain no crash output, transcript, user text, or inferred failure cause.
|
|
|
325
337
|
temporary compatibility alias. It implicitly accepts a fresh supplied turn and
|
|
326
338
|
stores its semantic result in a request-unknown turn, including for supported schema-4/5/6 records and
|
|
327
339
|
low-level opaque direct records. It does not accept `working`. New executor
|
|
328
|
-
instructions MUST use `report_state`. Successful mutation upgrades schema 4
|
|
329
|
-
to schema
|
|
340
|
+
instructions MUST use `report_state`. Successful mutation upgrades schema 4-8
|
|
341
|
+
to schema 9; unsupported schemas remain rejected. Legacy callers that omit
|
|
342
|
+
`turnRef` remain compatible when `turnId` is non-null.
|
|
330
343
|
|
|
331
344
|
## Copilot and dashboard
|
|
332
345
|
|
|
@@ -377,10 +390,10 @@ Snapshot and SSE list payloads MUST omit full `turns` and derived `results`
|
|
|
377
390
|
history and include `latestTurn`. The bounded per-task detail endpoint MAY
|
|
378
391
|
return the full validated task so the dialog can render the paired timeline newest first.
|
|
379
392
|
Dashboard notifications MUST capture an immutable event-time projection of the
|
|
380
|
-
task title, lifecycle state and event, turn
|
|
393
|
+
task title, lifecycle state and event, turn ref and optional Codex turn ID, event timestamp,
|
|
381
394
|
and relevant concise summary. Rendering MUST NOT resolve historical notice text
|
|
382
395
|
from the task's later current state. Notice identity and deduplication MUST use
|
|
383
|
-
task ID, turn
|
|
396
|
+
task ID, turn ref, and lifecycle event; a creation notice without a turn ref MUST
|
|
384
397
|
fall back to task ID plus the immutable creation timestamp. Dashboard revision
|
|
385
398
|
MUST NOT be the sole identity. Identical snapshots, SSE reconnects, idempotent
|
|
386
399
|
reports, schema normalization, and non-semantic rewrites MUST NOT add notices.
|
|
@@ -407,13 +420,15 @@ its displayed summary when present, event time, and missing-task state.
|
|
|
407
420
|
|
|
408
421
|
## Task-log migration
|
|
409
422
|
|
|
410
|
-
`workspace migrate` MUST explicitly convert every supported schema-4/5/6/7 record
|
|
423
|
+
`workspace migrate` MUST explicitly convert every supported schema-4/5/6/7/8 record
|
|
411
424
|
under the shared lock. Each legacy semantic result becomes a request-unknown
|
|
412
425
|
completed turn; a newer working state becomes a final unfinished turn, and a
|
|
413
|
-
schema-7 timeline is preserved
|
|
414
|
-
|
|
426
|
+
schema-7/8 timeline is preserved. Each non-null legacy `turnId` becomes the
|
|
427
|
+
same `turnRef`; each null legacy `turnId` receives one durably persisted UUID.
|
|
428
|
+
Migration MUST validate the complete source, record/turn counts, turn-ref
|
|
429
|
+
invariants, and complete schema-9 candidate before changing the task log,
|
|
415
430
|
create and read back an exclusive recovery backup, atomically replace the log,
|
|
416
|
-
and validate the installed result. A fully schema-
|
|
431
|
+
and validate the installed result. A fully schema-9 log MUST be an idempotent
|
|
417
432
|
no-op without another backup. Invalid/unsupported input MUST remain untouched;
|
|
418
433
|
failures after backup creation MUST report the backup path and MUST never
|
|
419
434
|
partially rewrite individual lines.
|
package/docs/workflows.md
CHANGED
|
@@ -89,7 +89,7 @@ sequenceDiagram
|
|
|
89
89
|
D->>D: Choose one configured and native project
|
|
90
90
|
D->>M: record_task(id, project, title, instruction, null)
|
|
91
91
|
M->>W: recordTask()
|
|
92
|
-
W->>W: Lock, validate, append schema-
|
|
92
|
+
W->>W: Lock, validate, append schema-9 snapshot
|
|
93
93
|
W-->>M: working link-pending task
|
|
94
94
|
M-->>D: task
|
|
95
95
|
D->>C: Create executor with marked instruction
|
|
@@ -116,9 +116,10 @@ semantic callbacks.
|
|
|
116
116
|
|
|
117
117
|
## State reporting
|
|
118
118
|
|
|
119
|
-
The executor
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
The executor always establishes a `turnRef` before work. An exact native read
|
|
120
|
+
supplies the same value as optional `turnId` metadata when available; otherwise
|
|
121
|
+
the executor retains a fresh UUID and reports `turnId: null`. `report_state`
|
|
122
|
+
records live turn state as a paired request/result timeline.
|
|
122
123
|
|
|
123
124
|
That timeline is also the durable GitHub-link source. A working request names a
|
|
124
125
|
known selected repository with its canonical GitHub URL. A terminal result
|
|
@@ -134,13 +135,16 @@ sequenceDiagram
|
|
|
134
135
|
participant C as Native Codex task API
|
|
135
136
|
participant M as TaskChef MCP
|
|
136
137
|
participant W as workspace.js
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
opt Native turn metadata available
|
|
139
|
+
E->>C: Exact read of linked executor
|
|
140
|
+
C-->>E: Current turn ID
|
|
141
|
+
end
|
|
142
|
+
E->>E: Retain native ID or generate fallback UUID turnRef
|
|
143
|
+
E->>M: report_state(..., turnRef, turnId, working, requestSummary)
|
|
140
144
|
M->>W: reportTaskState()
|
|
141
145
|
W->>W: Append turn with request and null result
|
|
142
146
|
E->>E: Work, finish, or reach semantic decision
|
|
143
|
-
E->>M: report_state(..., semantic status, summary)
|
|
147
|
+
E->>M: report_state(..., same turnRef, same turnId, semantic status, summary)
|
|
144
148
|
M->>W: reportTaskState()
|
|
145
149
|
W->>W: Lock and validate identity and freshness
|
|
146
150
|
alt Same current working turn
|
|
@@ -161,8 +165,10 @@ for a user decision or fact required to proceed.
|
|
|
161
165
|
|
|
162
166
|
## Follow-up turns
|
|
163
167
|
|
|
164
|
-
Turn
|
|
165
|
-
|
|
168
|
+
Turn refs are freshness tokens for semantic callbacks. The workspace rejects a
|
|
169
|
+
callback whose ref belongs to a historical turn. Fallback UUID refs are opaque
|
|
170
|
+
and unordered; native-backed UUIDv7 refs retain native ordering so a delayed,
|
|
171
|
+
previously unseen older start cannot replace newer native state.
|
|
166
172
|
|
|
167
173
|
```mermaid
|
|
168
174
|
sequenceDiagram
|
|
@@ -181,7 +187,7 @@ sequenceDiagram
|
|
|
181
187
|
C-->>E: turnB
|
|
182
188
|
E->>M: report_state(..., turnB, working, requestB)
|
|
183
189
|
M->>W: reportTaskState()
|
|
184
|
-
W->>W: Require turnB
|
|
190
|
+
W->>W: Require a fresh turnB ref and append request B
|
|
185
191
|
W-->>M: working snapshot plus paired timeline
|
|
186
192
|
M-->>E: working snapshot plus paired timeline
|
|
187
193
|
E->>M: report_state(..., turnB, completed, summaryB)
|
|
@@ -190,12 +196,13 @@ sequenceDiagram
|
|
|
190
196
|
M-->>E: completed snapshot plus result B
|
|
191
197
|
E->>M: report_state(..., turnA, completed, staleSummary)
|
|
192
198
|
M->>W: Validate freshness
|
|
193
|
-
W-->>M: Error:
|
|
199
|
+
W-->>M: Error: stale turnRef
|
|
194
200
|
M-->>E: Visible tool error
|
|
195
201
|
```
|
|
196
202
|
|
|
197
|
-
The executor contract therefore requires a new
|
|
198
|
-
cached or inherited
|
|
203
|
+
The executor contract therefore requires a new `turnRef` on every follow-up.
|
|
204
|
+
Native turn reads are preferred metadata but optional; cached or inherited IDs
|
|
205
|
+
and replacement fallback UUIDs for the same prompt are invalid.
|
|
199
206
|
|
|
200
207
|
## Interrupted-turn recovery
|
|
201
208
|
|
|
@@ -211,12 +218,12 @@ sequenceDiagram
|
|
|
211
218
|
participant M as TaskChef MCP
|
|
212
219
|
participant W as workspace.js
|
|
213
220
|
participant F as tasks.jsonl
|
|
214
|
-
E->>M: report_state(...,
|
|
221
|
+
E->>M: report_state(..., turnRefB, working, requestB)
|
|
215
222
|
M->>W: reportTaskState()
|
|
216
|
-
W->>W: Acquire workspace lock and validate
|
|
223
|
+
W->>W: Acquire workspace lock and validate turnRefB is new
|
|
217
224
|
W->>W: Close unfinished turnA as interrupted
|
|
218
225
|
W->>W: Append turnB with requestB and null result
|
|
219
|
-
W->>F: One atomic schema-
|
|
226
|
+
W->>F: One atomic schema-9 replacement
|
|
220
227
|
W-->>M: working task projected from turnB
|
|
221
228
|
M-->>E: Idempotent recovery success
|
|
222
229
|
E->>M: late semantic result for turnA
|
|
@@ -277,10 +284,11 @@ sequenceDiagram
|
|
|
277
284
|
W-->>D: Recorded task
|
|
278
285
|
D->>C: Create executor
|
|
279
286
|
C--xD: Creation error
|
|
280
|
-
D->>
|
|
287
|
+
D->>D: Generate and retain fallback UUID turnRef
|
|
288
|
+
D->>M: report_state(taskId, null, turnRef, null, failed, boundedSummary)
|
|
281
289
|
M->>W: Lock and store creation failure
|
|
282
|
-
W-->>D: Failed task with null IDs
|
|
283
|
-
D-->>D: Preserve original creation error
|
|
290
|
+
W-->>D: Failed task with turnRef and null native IDs
|
|
291
|
+
D-->>D: Preserve original creation error, task ID, and turnRef
|
|
284
292
|
```
|
|
285
293
|
|
|
286
294
|
The summary is bounded and excludes secrets, transcripts, and raw command
|
|
@@ -331,8 +339,8 @@ against current configuration.
|
|
|
331
339
|
|
|
332
340
|
The browser derives one immutable Updates-panel snapshot when it first observes
|
|
333
341
|
a semantic lifecycle transition. The snapshot keeps its event-time title,
|
|
334
|
-
state/event, turn ID, timestamp, and relevant summary.
|
|
335
|
-
task ID, turn
|
|
342
|
+
state/event, turn ref, optional Codex turn ID, timestamp, and relevant summary.
|
|
343
|
+
Its identity combines task ID, turn ref, and event, with creation time as the fallback when creation
|
|
336
344
|
has no turn. A separate seen-identity set outlives the bounded visible notices,
|
|
337
345
|
individual dismissals, and Clear all, preventing replay after reconnect,
|
|
338
346
|
normalization, or disappearance and reappearance. Per-task semantic signatures
|
|
@@ -351,15 +359,16 @@ all, and ordinary rerendering do not re-announce retained history. Toast action
|
|
|
351
359
|
labels remain concise while `aria-describedby` connects the visible summary,
|
|
352
360
|
event time, and missing-task explanation for assistive technology.
|
|
353
361
|
|
|
354
|
-
## Schema 4
|
|
362
|
+
## Schema 4-8 migration
|
|
355
363
|
|
|
356
364
|
`taskchef workspace migrate` acquires the same workspace lock as lifecycle
|
|
357
365
|
writers, validates the complete legacy log, converts schema-4/5/6 results into
|
|
358
366
|
request-unknown completed turns and preserves a newer working turn, then validates the
|
|
359
367
|
complete candidate. Before replacement it writes and reads back an exclusive
|
|
360
|
-
`tasks.jsonl.pre-
|
|
361
|
-
|
|
362
|
-
|
|
368
|
+
`tasks.jsonl.pre-v9-*.bak` file. Schema-7/8 timelines gain a durable `turnRef`:
|
|
369
|
+
non-null `turnId` values are copied exactly and null IDs receive persisted UUIDs.
|
|
370
|
+
Task and turn counts plus all refs are validated before and after the atomic
|
|
371
|
+
replacement. A second run sees only schema 9 and returns unchanged without
|
|
363
372
|
another backup. Unsupported or malformed input fails before backup/rewrite;
|
|
364
373
|
after a later filesystem failure, the reported backup is the recovery source.
|
|
365
374
|
|
|
@@ -376,9 +385,9 @@ summary is cryptographically authenticated; this is a local single-user trust
|
|
|
376
385
|
model. Managed files, instructions, project snapshots, MCP inputs, and dashboard
|
|
377
386
|
requests are validated at every action boundary.
|
|
378
387
|
|
|
379
|
-
Configuration schema 2 and task schemas 4
|
|
380
|
-
4
|
|
381
|
-
mutation upgrades each record to schema
|
|
388
|
+
Configuration schema 2 and task schemas 4 through 9 are accepted. Schemas
|
|
389
|
+
4-8 are read/migration compatibility until an explicit migration or lifecycle
|
|
390
|
+
mutation upgrades each record to schema 9. Schema 9 persists `turns`, including
|
|
382
391
|
timeline-only interrupted outcomes, and derives semantic-only `results` and
|
|
383
392
|
`lastResult` plus `latestTurn` for compact compatibility. Other schemas
|
|
384
393
|
are rejected without rewrite.
|
package/package.json
CHANGED
|
@@ -70,5 +70,7 @@ stop and report that the TaskChef plugin must be reloaded or installed.
|
|
|
70
70
|
7. Return immediately. Preserve a returned provisional client ID only for the
|
|
71
71
|
created-thread directive. Do not call `link_task` from the dispatcher even
|
|
72
72
|
when creation returns a durable ID; the child must self-link.
|
|
73
|
-
8. If creation fails after recording,
|
|
74
|
-
|
|
73
|
+
8. If creation fails after recording, generate a fresh UUID and call
|
|
74
|
+
`report_state` with it as `turnRef`, `failed`, null thread and Codex turn IDs,
|
|
75
|
+
and a bounded summary before returning the failure. Retain that UUID for an
|
|
76
|
+
exact retry.
|
|
@@ -30,10 +30,14 @@ Complete this lifecycle setup before substantive assignment work:
|
|
|
30
30
|
that exact thread ID as the first TaskChef action. An identical retry is
|
|
31
31
|
idempotent. On a follow-up, retry the same link only when the prior link
|
|
32
32
|
cannot be established from the task context.
|
|
33
|
-
3.
|
|
34
|
-
|
|
33
|
+
3. Establish this prompt's lifecycle identity before reporting. If this exact
|
|
34
|
+
Codex thread can be read natively and exposes the current turn ID, use that
|
|
35
|
+
exact value for both `turnRef` and `turnId`. Otherwise generate one fresh
|
|
36
|
+
UUID locally, retain it for this entire prompt, use it as `turnRef`, and use
|
|
37
|
+
`turnId: null`. Do not infer a native ID, reuse an earlier prompt's
|
|
38
|
+
`turnRef`, or let a retry generate a replacement UUID.
|
|
35
39
|
4. Call TaskChef `report_state` with the marked task ID, self-linked thread ID,
|
|
36
|
-
|
|
40
|
+
this prompt's `turnRef` and optional Codex `turnId`, `status: working`, an omitted or null result summary, and a
|
|
37
41
|
concise `requestSummary` describing this turn's assignment or follow-up.
|
|
38
42
|
When the turn targets a known GitHub repository, include its canonical
|
|
39
43
|
`https://github.com/<owner>/<repository>` URL so multi-repository projects
|
|
@@ -45,15 +49,17 @@ as interrupted and starts the current turn. Continue the real assignment from
|
|
|
45
49
|
the current request. Do not manufacture a semantic `failed` result for the old
|
|
46
50
|
turn and do not retry an old terminal report.
|
|
47
51
|
|
|
48
|
-
If `CODEX_THREAD_ID
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
If `CODEX_THREAD_ID` or a required TaskChef tool is unavailable, or if linking
|
|
53
|
+
or the working-state report fails, report the failure visibly and stop before
|
|
54
|
+
substantive work. Native turn reading is optional because the retained fallback
|
|
55
|
+
UUID is the lifecycle identity. Retry a possibly lost working callback with the
|
|
56
|
+
same `turnRef`; never generate a replacement for the same prompt or bypass a
|
|
57
|
+
link-pending state.
|
|
52
58
|
|
|
53
59
|
## Finish every execution turn
|
|
54
60
|
|
|
55
|
-
Before ending,
|
|
56
|
-
|
|
61
|
+
Before ending, call `report_state` with the same `turnRef` and `turnId` values
|
|
62
|
+
used by this prompt's working report, one semantic status, and a concise summary:
|
|
57
63
|
|
|
58
64
|
- `completed` only when the assignment is genuinely complete.
|
|
59
65
|
- `needs_input` only when a semantic decision or missing information must come
|
|
@@ -62,16 +68,16 @@ the current turn with one semantic status and a concise summary:
|
|
|
62
68
|
|
|
63
69
|
A live native approval prompt is Codex state, not semantic `needs_input`; leave
|
|
64
70
|
the approval live instead of storing it as a TaskChef result. Never invent or
|
|
65
|
-
reuse a
|
|
71
|
+
reuse a `turnRef` after a follow-up. If a final-report response is lost, an
|
|
66
72
|
identical terminal retry is safe only while the same turn remains current. On
|
|
67
|
-
a later
|
|
73
|
+
a later prompt, run the start lifecycle with a new `turnRef`; TaskChef
|
|
68
74
|
will preserve the predecessor as interrupted, and only the new turn may receive
|
|
69
75
|
a semantic result. Say reporting failures visibly instead of claiming a tracked
|
|
70
76
|
outcome.
|
|
71
77
|
|
|
72
78
|
Request and result summaries must omit secrets, transcripts, raw command output, hidden reasoning,
|
|
73
79
|
and unnecessary personal data. Identical lifecycle retries are safe; never
|
|
74
|
-
replace a same
|
|
80
|
+
replace a same-`turnRef` report with different content or let an older turn
|
|
75
81
|
overwrite newer state.
|
|
76
82
|
|
|
77
83
|
### Preserve repository and delivery links
|
package/src/cli.js
CHANGED
|
@@ -173,10 +173,12 @@ function taskDetails(task) {
|
|
|
173
173
|
`Title: ${singleLineDetail(task.title)}`,
|
|
174
174
|
`Project: ${singleLineDetail(task.project.name)}`,
|
|
175
175
|
`Current status: ${singleLineDetail(task.status ?? "unknown")}`,
|
|
176
|
-
`Current turn
|
|
176
|
+
`Current turn ref: ${singleLineDetail(task.turnRef ?? "-")}`,
|
|
177
|
+
`Current Codex turn ID: ${singleLineDetail(task.turnId ?? "-")}`,
|
|
177
178
|
`Last result status: ${singleLineDetail(lastResult?.status ?? "-")}`,
|
|
178
179
|
`Last result summary: ${singleLineDetail(lastResult?.summary ?? "-")}`,
|
|
179
|
-
`Last result turn
|
|
180
|
+
`Last result turn ref: ${singleLineDetail(lastResult?.turnRef ?? "-")}`,
|
|
181
|
+
`Last result Codex turn ID: ${singleLineDetail(lastResult?.turnId ?? "-")}`,
|
|
180
182
|
`Last result updated: ${singleLineDetail(lastResult?.updatedAt ?? "-")}`,
|
|
181
183
|
`Project path: ${singleLineDetail(task.project.path)}`,
|
|
182
184
|
`Created: ${singleLineDetail(task.createdAt)}`,
|
|
@@ -187,7 +189,7 @@ function taskDetails(task) {
|
|
|
187
189
|
`Turn count: ${task.turns.length}`,
|
|
188
190
|
"Activity timeline (newest first):",
|
|
189
191
|
...[...task.turns].reverse().map((turn) => (
|
|
190
|
-
`- ${singleLineDetail(turn.startedAt)} | ${singleLineDetail(turn.result?.status ?? "working")} | turn ${singleLineDetail(turn.turnId ?? "-")} | request: ${singleLineDetail(turn.requestSummary ?? "not recorded")} | result: ${singleLineDetail(turn.result?.summary ?? "in progress")}`
|
|
192
|
+
`- ${singleLineDetail(turn.startedAt)} | ${singleLineDetail(turn.result?.status ?? "working")} | ref ${singleLineDetail(turn.turnRef ?? "-")} | Codex turn ${singleLineDetail(turn.turnId ?? "-")} | request: ${singleLineDetail(turn.requestSummary ?? "not recorded")} | result: ${singleLineDetail(turn.result?.summary ?? "in progress")}`
|
|
191
193
|
)),
|
|
192
194
|
"Instruction:",
|
|
193
195
|
task.instruction,
|
|
@@ -200,7 +202,10 @@ async function migrate(args) {
|
|
|
200
202
|
print(result, args, (value) => [
|
|
201
203
|
`Task log: ${value.action}`,
|
|
202
204
|
`Tasks: ${value.taskCount}`,
|
|
205
|
+
`Turns: ${value.turnCount}`,
|
|
203
206
|
`Migrated: ${value.migratedCount}`,
|
|
207
|
+
`Native turn refs: ${value.nativeTurnRefCount}`,
|
|
208
|
+
`Fallback turn refs: ${value.fallbackTurnRefCount}`,
|
|
204
209
|
`Backup: ${value.backupPath ?? "not needed"}`,
|
|
205
210
|
].join("\n"));
|
|
206
211
|
return 0;
|
package/src/dashboard/app.js
CHANGED
|
@@ -259,7 +259,7 @@ function notificationAnnouncement(notification) {
|
|
|
259
259
|
notificationTitle(notification),
|
|
260
260
|
notification.title,
|
|
261
261
|
notification.summary,
|
|
262
|
-
notification.
|
|
262
|
+
notification.turnRef ? `Turn ref ${notification.turnRef}` : null,
|
|
263
263
|
formatRelativeTime(notification.timestamp),
|
|
264
264
|
].filter(Boolean).join(". ");
|
|
265
265
|
}
|
|
@@ -302,7 +302,7 @@ function turnTimeline(task) {
|
|
|
302
302
|
const turnStatus = presentation.status;
|
|
303
303
|
status.className = `status status-${turnStatus}`;
|
|
304
304
|
status.textContent = turnStatus.replaceAll("_", " ");
|
|
305
|
-
const turnKey = turn.turnId ?? `no-turn:${index}`;
|
|
305
|
+
const turnKey = turn.turnRef ?? turn.turnId ?? `no-turn:${index}`;
|
|
306
306
|
const timestamp = timestampControl(presentation.updatedAt, {
|
|
307
307
|
accessibleName: `Turn updated time for ${turnStatus.replaceAll("_", " ")}`,
|
|
308
308
|
key: `detail:${task.id}:turn:${turnKey}`,
|
|
@@ -324,9 +324,7 @@ function turnTimeline(task) {
|
|
|
324
324
|
appendLinkedText(result, presentation.summary, task);
|
|
325
325
|
const turnMetadata = document.createElement("p");
|
|
326
326
|
turnMetadata.className = "result-history-turn";
|
|
327
|
-
turnMetadata.textContent = turn.turnId
|
|
328
|
-
? `Turn ${turn.turnId}`
|
|
329
|
-
: "No turn ID (creation failure)";
|
|
327
|
+
turnMetadata.textContent = `Turn ref ${turn.turnRef ?? "not recorded"}; Codex turn ${turn.turnId ?? "unavailable"}`;
|
|
330
328
|
item.append(header, requestLabel, request, resultLabel, result, turnMetadata);
|
|
331
329
|
return item;
|
|
332
330
|
});
|
|
@@ -357,9 +355,11 @@ function renderDialog(task) {
|
|
|
357
355
|
elements.copyThreadId.disabled = !task.threadId;
|
|
358
356
|
elements.dialogMetadata.replaceChildren(
|
|
359
357
|
...detailRow("Current status", taskStatusLabel(task)),
|
|
360
|
-
...detailRow("Current turn
|
|
358
|
+
...detailRow("Current turn ref", task.turnRef),
|
|
359
|
+
...detailRow("Current Codex turn ID", task.turnId),
|
|
361
360
|
...detailRow("Last result status", task.lastResult?.status?.replaceAll("_", " ")),
|
|
362
|
-
...detailRow("Last result turn
|
|
361
|
+
...detailRow("Last result turn ref", task.lastResult?.turnRef),
|
|
362
|
+
...detailRow("Last result Codex turn ID", task.lastResult?.turnId),
|
|
363
363
|
...detailRow("Last result updated", timestampControl(task.lastResult?.updatedAt, {
|
|
364
364
|
accessibleName: `Last result updated time for ${task.title}`,
|
|
365
365
|
key: `detail:${task.id}:last-result-updated`,
|