taskchef 7.8.0 → 7.9.1
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 +17 -17
- package/docs/images/result-history-dashboard.jpg +0 -0
- package/docs/spec.md +36 -35
- package/docs/workflows.md +23 -22
- package/package.json +1 -1
- package/skills/taskchef-delegate/SKILL.md +3 -2
- package/skills/taskchef-executor/SKILL.md +17 -14
- package/skills/taskchef-report/SKILL.md +8 -9
- package/src/cli.js +4 -4
- package/src/dashboard/app.js +44 -20
- package/src/dashboard/index.html +1 -1
- package/src/dashboard/state.js +31 -0
- package/src/dashboard/styles.css +3 -1
- package/src/dashboard.js +12 -1
- package/src/delegation.js +11 -7
- package/src/mcp.js +23 -2
- package/src/workspace.js +227 -50
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
TaskChef is a local dispatch desk for Codex. Give one dispatcher a request and
|
|
4
4
|
it records each independently useful outcome, creates a normal Codex task in
|
|
5
5
|
the right project, and returns immediately. The executor task is where live
|
|
6
|
-
work, approvals, and follow-ups happen; TaskChef
|
|
7
|
-
while projecting the latest
|
|
6
|
+
work, approvals, and follow-ups happen; TaskChef pairs each turn's request with
|
|
7
|
+
its semantic result while projecting the latest pair for compact navigation.
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
|
-
request -> recorded TaskChef task -> Codex executor ->
|
|
10
|
+
request -> recorded TaskChef task -> Codex executor -> request/result turn timeline
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Which document should I read?
|
|
@@ -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 7; schema 4/5/6 migration supported)
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
List or change routing targets conversationally:
|
|
@@ -104,7 +104,6 @@ For example, TaskChef generates this shape:
|
|
|
104
104
|
|
|
105
105
|
```text
|
|
106
106
|
Fix duplicate charges after a retry and add a regression test.
|
|
107
|
-
|
|
108
107
|
<!-- taskchef_id=c0f010ff-84f2-4838-a69d-0ff1f5d721d7 -->
|
|
109
108
|
Use $taskchef-executor to execute and report this delegated TaskChef assignment.
|
|
110
109
|
```
|
|
@@ -119,12 +118,12 @@ when a turn starts and one semantic outcome before that same turn ends:
|
|
|
119
118
|
- `failed`: the executor or creation attempt ended unsuccessfully.
|
|
120
119
|
|
|
121
120
|
A native approval prompt is live Codex state, not `needs_input`.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
When work starts, the executor reports a concise request summary. TaskChef
|
|
122
|
+
appends a `turns` entry that pairs that request with a null result while working,
|
|
123
|
+
then fills the same entry with the semantic outcome. A follow-up therefore shows
|
|
124
|
+
its own request with “In progress,” never the preceding turn's result. Returned
|
|
125
|
+
tasks still derive `results` and `lastResult` as compatibility projections.
|
|
126
|
+
TaskChef does not store transcripts, hidden reasoning, or non-semantic events.
|
|
128
127
|
|
|
129
128
|
Delegated tasks created by earlier TaskChef versions remain compatible: their
|
|
130
129
|
inline executor protocol still parses, self-links, and may use the deprecated
|
|
@@ -174,8 +173,9 @@ taskchef dashboard --port 3211
|
|
|
174
173
|
```
|
|
175
174
|
|
|
176
175
|
The loopback dashboard watches `tasks.jsonl`, groups current states, and opens
|
|
177
|
-
linked Codex tasks. List snapshots and SSE events carry only the latest
|
|
178
|
-
|
|
176
|
+
linked Codex tasks. List snapshots and SSE events carry only the latest
|
|
177
|
+
request/result pair; opening task details fetches the full newest-first activity
|
|
178
|
+
timeline.
|
|
179
179
|
The header shows the running TaskChef package version reported by the same
|
|
180
180
|
bounded health identity used for compatible-listener checks.
|
|
181
181
|
Task and result times are relative through 29 days (with minute detail for the
|
|
@@ -203,7 +203,7 @@ no task data, credentials, environment variables, process control, or secrets.
|
|
|
203
203
|
|
|
204
204
|

|
|
205
205
|
|
|
206
|
-

|
|
207
207
|
|
|
208
208
|
## Common recovery
|
|
209
209
|
|
|
@@ -218,8 +218,8 @@ taskchef doctor
|
|
|
218
218
|
|
|
219
219
|
`doctor` is read-only. `workspace init` creates missing files and refreshes
|
|
220
220
|
managed instructions. `workspace migrate` explicitly upgrades supported schema
|
|
221
|
-
4/5 task lines to schema
|
|
222
|
-
source and converted log before writing, creates an exclusive `tasks.jsonl.pre-
|
|
221
|
+
4/5/6 task lines to schema 7 under the workspace lock. It validates the complete
|
|
222
|
+
source and converted log before writing, creates an exclusive `tasks.jsonl.pre-v7-*.bak`
|
|
223
223
|
backup, atomically replaces the log, validates the result, and becomes an
|
|
224
224
|
idempotent no-op after migration. If replacement fails, the original remains
|
|
225
225
|
or the reported backup can be restored; unsupported or invalid input is rejected
|
|
@@ -230,7 +230,7 @@ executor so its first action can retry `link_task`. Do not guess an identity
|
|
|
230
230
|
or edit `tasks.jsonl`. If native task creation failed, the record is retained
|
|
231
231
|
as `failed` with null thread and turn IDs.
|
|
232
232
|
|
|
233
|
-
Schemas other than 4, 5, and
|
|
233
|
+
Schemas other than 4, 5, 6, and 7 remain unsupported. Retain such a workspace
|
|
234
234
|
unchanged and create a current workspace; the migration command deliberately
|
|
235
235
|
does not guess how to convert unknown formats.
|
|
236
236
|
|
|
Binary file
|
package/docs/spec.md
CHANGED
|
@@ -23,7 +23,7 @@ is dated research, not contract.
|
|
|
23
23
|
| **Self-linking** | The executor's one-way registration of its own canonical Codex UUIDv7 from `CODEX_THREAD_ID`. |
|
|
24
24
|
| **Link-pending** | A working task whose `threadId` is null and `updatedBy` is `dispatcher`. |
|
|
25
25
|
| **Current execution state** | The latest reported executor turn and its `working`, `needs_input`, `completed`, or `failed` status. |
|
|
26
|
-
| **
|
|
26
|
+
| **Turn timeline** | The ordered collection pairing each turn's concise request summary with its eventual semantic result. |
|
|
27
27
|
| **Last semantic result** | The final result-history entry, exposed through the derived `lastResult` compatibility alias. |
|
|
28
28
|
| **Current turn ID** | The canonical Codex UUIDv7 returned by an exact native read of the linked executor for the turn being reported. |
|
|
29
29
|
| **Dashboard** | The loopback, read-only UI derived from validated workspace snapshots and bounded native actions. |
|
|
@@ -69,8 +69,8 @@ Repository URLs MUST canonicalize to `https://github.com/<owner>/<repository>`
|
|
|
69
69
|
and be case-insensitively deduplicated.
|
|
70
70
|
|
|
71
71
|
`tasks.jsonl` MUST contain zero or more newline-terminated schema-4, schema-5,
|
|
72
|
-
or schema-
|
|
73
|
-
formats; every new record and state mutation MUST write schema
|
|
72
|
+
schema-6, or schema-7 records, one per line. Schemas 4 through 6 are supported
|
|
73
|
+
migration/read formats; every new record and state mutation MUST write schema 7. Other schemas
|
|
74
74
|
or unsupported fields MUST be rejected without conversion.
|
|
75
75
|
Reads and writes MUST reject symlinked managed files. Mutations
|
|
76
76
|
MUST hold the shared workspace lock and replace state atomically; read-only
|
|
@@ -82,7 +82,7 @@ Every record MUST contain exactly these fields:
|
|
|
82
82
|
|
|
83
83
|
| Field | Contract |
|
|
84
84
|
| --- | --- |
|
|
85
|
-
| `schemaVersion` | Integer `
|
|
85
|
+
| `schemaVersion` | Integer `7`; schema-4/5/6 records remain readable until explicit migration or their next mutation. |
|
|
86
86
|
| `id` | Unique safe TaskChef ID; delegation uses a lowercase full UUID. |
|
|
87
87
|
| `project` | Immutable configured-project snapshot. |
|
|
88
88
|
| `title` | Non-empty display title. |
|
|
@@ -94,13 +94,12 @@ Every record MUST contain exactly these fields:
|
|
|
94
94
|
| `turnId` | Null before turn reporting; otherwise the current reported turn. Linked MCP journeys use a canonical Codex UUIDv7. |
|
|
95
95
|
| `updatedAt` | ISO 8601 timestamp not earlier than `createdAt` or the prior `updatedAt`; clock rollback cannot backdate a transition. |
|
|
96
96
|
| `updatedBy` | `dispatcher` or `mcp`. |
|
|
97
|
-
| `
|
|
97
|
+
| `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 or is `{status, summary, updatedAt}`. Canonical self-linking turn IDs are unique. A migrated low-level opaque record may retain one final reused ID to represent its legacy completed-result-plus-working-state ambiguity. |
|
|
98
98
|
|
|
99
|
-
Returned Task objects MUST additionally expose `
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
next major version after callers migrate to `results.at(-1)`.
|
|
99
|
+
Returned Task objects MUST additionally expose `latestTurn` as null for an empty
|
|
100
|
+
timeline or the final `turns` entry. They MUST derive `results` from completed
|
|
101
|
+
turns and `lastResult` from the final derived result. These projections MUST NOT
|
|
102
|
+
be persisted in schema 7 and remain compatibility aliases for existing callers.
|
|
104
103
|
|
|
105
104
|
Task IDs and non-null thread identities MUST be unique. The immutable intent
|
|
106
105
|
fields MUST NOT change after recording.
|
|
@@ -117,10 +116,10 @@ the final link, preserving the delegate skill's immediate-return contract.
|
|
|
117
116
|
1. The dispatcher MUST call `prepare_dispatch` once per outcome.
|
|
118
117
|
2. It MUST choose exactly one configured project and exact native-project path.
|
|
119
118
|
3. It MUST build the instruction with the user's outcome beginning on line 1
|
|
120
|
-
and remaining uninterrupted, followed by one
|
|
121
|
-
marker, and exactly one concise explicit `$taskchef-executor`
|
|
122
|
-
the final line. It MUST NOT
|
|
123
|
-
instruction.
|
|
119
|
+
and remaining uninterrupted, followed by exactly one newline, the returned
|
|
120
|
+
marker, one newline, and exactly one concise explicit `$taskchef-executor`
|
|
121
|
+
invocation on the final line. It MUST NOT place a blank line before or after
|
|
122
|
+
the marker or inline the executor protocol into a new instruction.
|
|
124
123
|
4. It MUST call `record_task` with `threadId: null` before native creation.
|
|
125
124
|
5. It MUST create exactly one native Codex executor and return immediately.
|
|
126
125
|
6. The executor MUST read its own `CODEX_THREAD_ID` and call `link_task`
|
|
@@ -137,9 +136,10 @@ If native creation fails after recording, the dispatcher MUST call
|
|
|
137
136
|
A link failure MUST remain visible and retryable; the executor MUST report it
|
|
138
137
|
visibly and MUST NOT continue substantive work.
|
|
139
138
|
|
|
140
|
-
Previously recorded instructions with
|
|
141
|
-
first-line
|
|
142
|
-
|
|
139
|
+
Previously recorded instructions with the former blank line before a trailing
|
|
140
|
+
marker, a first-line HTML marker, the older first-line
|
|
141
|
+
`# taskchef_id=<full UUID>` heading, or the former blank line and inline
|
|
142
|
+
executor protocol MUST remain marker-readable and executable. Their
|
|
143
143
|
`report_result` calls MUST remain supported by the deprecated alias. New
|
|
144
144
|
instructions MUST use the trailing marker and explicit executor skill contract
|
|
145
145
|
above. A historical first-line instruction with an executor-skill invocation
|
|
@@ -238,8 +238,8 @@ new preparation values, though it writes no state.
|
|
|
238
238
|
|
|
239
239
|
**Structured output:** `{ task: Task }`.
|
|
240
240
|
|
|
241
|
-
The returned task has schema
|
|
242
|
-
|
|
241
|
+
The returned task has schema 7, `working`, null summary/turn/thread/latestTurn/lastResult,
|
|
242
|
+
empty `turns` and derived `results` arrays,
|
|
243
243
|
`updatedBy: dispatcher`, and equal creation/update timestamps. Duplicate IDs,
|
|
244
244
|
unknown projects, malformed markers, and invalid input fail. Repeating a
|
|
245
245
|
successful call is not idempotent; it fails as a duplicate.
|
|
@@ -268,7 +268,7 @@ marker, or ineligible state fails.
|
|
|
268
268
|
### `report_state`
|
|
269
269
|
|
|
270
270
|
**Caller:** executor, or dispatcher only for native creation failure.
|
|
271
|
-
**Mutation:** replaces the current state atomically and preserves `
|
|
271
|
+
**Mutation:** replaces the current state atomically and preserves `turns`.
|
|
272
272
|
|
|
273
273
|
**Input:**
|
|
274
274
|
|
|
@@ -279,6 +279,7 @@ marker, or ineligible state fails.
|
|
|
279
279
|
| `turnId` | Current canonical Codex UUIDv7 for a linked MCP journey; null only for creation failure. Maximum 256 characters at the MCP boundary. |
|
|
280
280
|
| `status` | `working`, `needs_input`, `completed`, or `failed`. |
|
|
281
281
|
| `summary` | Omitted or null for `working`; required non-empty string of at most 2,000 characters otherwise. |
|
|
282
|
+
| `requestSummary` | Concise current request of at most 1,000 characters for `working`; optional for backward compatibility and omitted for semantic states. |
|
|
282
283
|
|
|
283
284
|
**Structured output:** `{ task: Task }`.
|
|
284
285
|
|
|
@@ -286,10 +287,10 @@ For a linked self-linking journey, `working` MUST identify a turn newer than
|
|
|
286
287
|
the current turn and last semantic result. A semantic state MUST match the
|
|
287
288
|
current working turn. Repeating an identical state is idempotent; conflicting
|
|
288
289
|
or older state fails. A null-identity record accepts only a fresh executor
|
|
289
|
-
creation `failed` state with both IDs null.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
creation `failed` state with both IDs null. Starting work appends one turn with
|
|
291
|
+
its request and a null result; the semantic report fills that same turn's result.
|
|
292
|
+
An identical retry for any settled turn returns success without an append. A
|
|
293
|
+
different request or result for the same turn, a stale turn, or a semantic
|
|
293
294
|
result that does not match the active working turn MUST fail.
|
|
294
295
|
|
|
295
296
|
**Annotations:** `readOnlyHint: false`, `destructiveHint: true`,
|
|
@@ -299,10 +300,10 @@ result that does not match the active working turn MUST fail.
|
|
|
299
300
|
|
|
300
301
|
`report_result` retains the prior semantic-only input shape and statuses as a
|
|
301
302
|
temporary compatibility alias. It implicitly accepts a fresh supplied turn and
|
|
302
|
-
stores its semantic result, including for supported schema-4/5 records and
|
|
303
|
+
stores its semantic result in a request-unknown turn, including for supported schema-4/5/6 records and
|
|
303
304
|
low-level opaque direct records. It does not accept `working`. New executor
|
|
304
|
-
instructions MUST use `report_state`. Successful mutation upgrades schema 4/5
|
|
305
|
-
to schema
|
|
305
|
+
instructions MUST use `report_state`. Successful mutation upgrades schema 4/5/6
|
|
306
|
+
to schema 7; unsupported schemas remain rejected.
|
|
306
307
|
|
|
307
308
|
## Reporting and dashboard
|
|
308
309
|
|
|
@@ -324,9 +325,9 @@ startup safely. Direct thread navigation
|
|
|
324
325
|
MUST require a canonical Codex UUIDv7. Otherwise it MAY open the revalidated
|
|
325
326
|
configured project. Project paths from task history MUST be matched against
|
|
326
327
|
current configuration before use.
|
|
327
|
-
Snapshot and SSE list payloads MUST omit full `
|
|
328
|
-
|
|
329
|
-
return the full validated task so the dialog can render
|
|
328
|
+
Snapshot and SSE list payloads MUST omit full `turns` and derived `results`
|
|
329
|
+
history and include `latestTurn`. The bounded per-task detail endpoint MAY
|
|
330
|
+
return the full validated task so the dialog can render the paired timeline newest first.
|
|
330
331
|
Dashboard notifications MUST capture an immutable event-time projection of the
|
|
331
332
|
task title, lifecycle state and event, turn ID when present, event timestamp,
|
|
332
333
|
and relevant concise summary. Rendering MUST NOT resolve historical notice text
|
|
@@ -358,12 +359,12 @@ its displayed summary when present, event time, and missing-task state.
|
|
|
358
359
|
|
|
359
360
|
## Task-log migration
|
|
360
361
|
|
|
361
|
-
`workspace migrate` MUST explicitly convert every supported schema-4/5 record
|
|
362
|
-
under the shared lock.
|
|
363
|
-
|
|
364
|
-
complete source and complete schema-
|
|
362
|
+
`workspace migrate` MUST explicitly convert every supported schema-4/5/6 record
|
|
363
|
+
under the shared lock. Each legacy semantic result becomes a request-unknown
|
|
364
|
+
completed turn; a newer working state becomes a final unfinished turn. Migration
|
|
365
|
+
MUST validate the complete source and complete schema-7 candidate before changing the task log,
|
|
365
366
|
create and read back an exclusive recovery backup, atomically replace the log,
|
|
366
|
-
and validate the installed result. A fully schema-
|
|
367
|
+
and validate the installed result. A fully schema-7 log MUST be an idempotent
|
|
367
368
|
no-op without another backup. Invalid/unsupported input MUST remain untouched;
|
|
368
369
|
failures after backup creation MUST report the backup path and MUST never
|
|
369
370
|
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-7 snapshot
|
|
93
93
|
W-->>M: working link-pending task
|
|
94
94
|
M-->>D: task
|
|
95
95
|
D->>C: Create executor with marked instruction
|
|
@@ -116,8 +116,8 @@ semantic callbacks.
|
|
|
116
116
|
## State reporting
|
|
117
117
|
|
|
118
118
|
The executor obtains the turn identity from an exact native read of its own
|
|
119
|
-
linked task. `report_state` records live turn state
|
|
120
|
-
|
|
119
|
+
linked task. `report_state` records live turn state as a paired request/result
|
|
120
|
+
timeline.
|
|
121
121
|
|
|
122
122
|
```mermaid
|
|
123
123
|
sequenceDiagram
|
|
@@ -128,15 +128,15 @@ sequenceDiagram
|
|
|
128
128
|
participant W as workspace.js
|
|
129
129
|
E->>C: Exact read of linked executor
|
|
130
130
|
C-->>E: Current turn ID
|
|
131
|
-
E->>M: report_state(..., working,
|
|
131
|
+
E->>M: report_state(..., working, requestSummary)
|
|
132
132
|
M->>W: reportTaskState()
|
|
133
|
-
W->>W:
|
|
133
|
+
W->>W: Append turn with request and null result
|
|
134
134
|
E->>E: Work, finish, or reach semantic decision
|
|
135
135
|
E->>M: report_state(..., semantic status, summary)
|
|
136
136
|
M->>W: reportTaskState()
|
|
137
137
|
W->>W: Lock and validate identity and freshness
|
|
138
138
|
alt Same current working turn
|
|
139
|
-
W->>W:
|
|
139
|
+
W->>W: Fill that turn's result and derive compatibility results
|
|
140
140
|
W-->>M: Updated task
|
|
141
141
|
M-->>E: Recorded result
|
|
142
142
|
else Same turn and same result
|
|
@@ -171,11 +171,11 @@ sequenceDiagram
|
|
|
171
171
|
U->>E: Provide decision
|
|
172
172
|
E->>C: Read exact executor after follow-up
|
|
173
173
|
C-->>E: turnB
|
|
174
|
-
E->>M: report_state(..., turnB, working,
|
|
174
|
+
E->>M: report_state(..., turnB, working, requestB)
|
|
175
175
|
M->>W: reportTaskState()
|
|
176
|
-
W->>W: Require turnB greater and
|
|
177
|
-
W-->>M: working snapshot plus
|
|
178
|
-
M-->>E: working snapshot plus
|
|
176
|
+
W->>W: Require turnB greater and append request B
|
|
177
|
+
W-->>M: working snapshot plus paired timeline
|
|
178
|
+
M-->>E: working snapshot plus paired timeline
|
|
179
179
|
E->>M: report_state(..., turnB, completed, summaryB)
|
|
180
180
|
M->>W: reportTaskState()
|
|
181
181
|
W-->>M: completed snapshot plus result B
|
|
@@ -263,7 +263,7 @@ sequenceDiagram
|
|
|
263
263
|
F-->>D: Filesystem change
|
|
264
264
|
D->>F: Bounded read from one descriptor
|
|
265
265
|
D->>D: Validate current schema and sort
|
|
266
|
-
D-->>B: Compact SSE snapshot
|
|
266
|
+
D-->>B: Compact SSE snapshot with latestTurn only
|
|
267
267
|
B->>D: GET task detail on demand
|
|
268
268
|
D-->>B: Full validated history
|
|
269
269
|
B->>D: Open task action
|
|
@@ -279,8 +279,8 @@ The dashboard binds to `127.0.0.1`, has no shared session state, limits task
|
|
|
279
279
|
count, file size, result count, and display fields, and checks origin/authority
|
|
280
280
|
for stateful local actions. Its bounded identity endpoint contains no task data
|
|
281
281
|
or secrets. The monitor already validates the complete log, but
|
|
282
|
-
snapshot/SSE list projections omit `results` so repeated
|
|
283
|
-
unnecessary history. A read-only per-task endpoint returns full
|
|
282
|
+
snapshot/SSE list projections omit `turns` and derived `results` so repeated
|
|
283
|
+
updates do not resend unnecessary history. A read-only per-task endpoint returns the full timeline only
|
|
284
284
|
when the dialog opens. Historical project paths are untrusted until matched
|
|
285
285
|
against current configuration.
|
|
286
286
|
|
|
@@ -306,14 +306,14 @@ all, and ordinary rerendering do not re-announce retained history. Toast action
|
|
|
306
306
|
labels remain concise while `aria-describedby` connects the visible summary,
|
|
307
307
|
event time, and missing-task explanation for assistive technology.
|
|
308
308
|
|
|
309
|
-
## Schema 4/5 migration
|
|
309
|
+
## Schema 4/5/6 migration
|
|
310
310
|
|
|
311
311
|
`taskchef workspace migrate` acquires the same workspace lock as lifecycle
|
|
312
|
-
writers, validates the complete legacy log, converts
|
|
313
|
-
|
|
312
|
+
writers, validates the complete legacy log, converts schema-4/5/6 results into
|
|
313
|
+
request-unknown completed turns and preserves a newer working turn, then validates the
|
|
314
314
|
complete candidate. Before replacement it writes and reads back an exclusive
|
|
315
|
-
`tasks.jsonl.pre-
|
|
316
|
-
validated again. A second run sees only schema
|
|
315
|
+
`tasks.jsonl.pre-v7-*.bak` file. The task log is replaced atomically and
|
|
316
|
+
validated again. A second run sees only schema 7 and returns unchanged without
|
|
317
317
|
another backup. Unsupported or malformed input fails before backup/rewrite;
|
|
318
318
|
after a later filesystem failure, the reported backup is the recovery source.
|
|
319
319
|
|
|
@@ -330,7 +330,8 @@ summary is cryptographically authenticated; this is a local single-user trust
|
|
|
330
330
|
model. Managed files, instructions, project snapshots, MCP inputs, and dashboard
|
|
331
331
|
requests are validated at every action boundary.
|
|
332
332
|
|
|
333
|
-
Configuration schema 2 and task schemas 4, 5, and
|
|
334
|
-
are read/migration compatibility until an explicit migration or lifecycle
|
|
335
|
-
mutation upgrades each record to schema
|
|
336
|
-
|
|
333
|
+
Configuration schema 2 and task schemas 4, 5, 6, and 7 are accepted. Schemas
|
|
334
|
+
4/5/6 are read/migration compatibility until an explicit migration or lifecycle
|
|
335
|
+
mutation upgrades each record to schema 7. Schema 7 persists `turns` and derives
|
|
336
|
+
`results`, `lastResult`, and `latestTurn` for compact compatibility. Other schemas
|
|
337
|
+
are rejected without rewrite.
|
package/package.json
CHANGED
|
@@ -52,8 +52,9 @@ stop and report that the TaskChef plugin must be reloaded or installed.
|
|
|
52
52
|
|
|
53
53
|
- Begin with the actual assignment on the first line and keep its complete
|
|
54
54
|
body uninterrupted.
|
|
55
|
-
- After the assignment, add one
|
|
56
|
-
marker on its own line.
|
|
55
|
+
- After the assignment's final character, add exactly one newline and the
|
|
56
|
+
preparation's exact marker on its own line. Do not add a blank line before
|
|
57
|
+
or after the marker.
|
|
57
58
|
- Immediately after the marker, end the instruction with exactly:
|
|
58
59
|
`Use $taskchef-executor to execute and report this delegated TaskChef assignment.`
|
|
59
60
|
- Include exactly one marker and exactly one executor-skill invocation.
|
|
@@ -9,12 +9,13 @@ Own and execute the delegated assignment in the current Codex task. Do not
|
|
|
9
9
|
re-dispatch it merely because it concerns TaskChef or a configured project.
|
|
10
10
|
Explicit requests to delegate separate work remain valid.
|
|
11
11
|
|
|
12
|
-
New instructions present the complete assignment first, followed by one
|
|
13
|
-
|
|
14
|
-
explicit skill invocation.
|
|
15
|
-
|
|
16
|
-
marker and invocation are lifecycle scaffolding, not
|
|
17
|
-
Require exactly one marker and do not infer an ID from
|
|
12
|
+
New instructions present the complete assignment first, followed by exactly one
|
|
13
|
+
newline, the exact `<!-- taskchef_id=<full UUID> -->` marker, one newline, and
|
|
14
|
+
the final explicit skill invocation. There is no blank line before or after the
|
|
15
|
+
marker. Treat that UUID as the TaskChef task ID. The assignment is everything
|
|
16
|
+
before the marker; the marker and invocation are lifecycle scaffolding, not
|
|
17
|
+
part of the deliverable. Require exactly one marker and do not infer an ID from
|
|
18
|
+
similar prose.
|
|
18
19
|
|
|
19
20
|
## Start every execution turn
|
|
20
21
|
|
|
@@ -31,7 +32,8 @@ Complete this lifecycle setup before substantive assignment work:
|
|
|
31
32
|
3. Read this exact Codex thread natively and obtain the current turn ID. Do not
|
|
32
33
|
infer it or reuse an earlier turn ID.
|
|
33
34
|
4. Call TaskChef `report_state` with the marked task ID, self-linked thread ID,
|
|
34
|
-
current turn ID, `status: working`,
|
|
35
|
+
current turn ID, `status: working`, an omitted or null result summary, and a
|
|
36
|
+
concise `requestSummary` describing this turn's assignment or follow-up.
|
|
35
37
|
|
|
36
38
|
If `CODEX_THREAD_ID`, exact native thread reading, or a required TaskChef tool
|
|
37
39
|
is unavailable, or if linking or the working-state report fails, report the
|
|
@@ -56,7 +58,7 @@ turn, run the start lifecycle with its new current turn ID and report that
|
|
|
56
58
|
turn's actual outcome. Say reporting failures visibly instead of claiming a
|
|
57
59
|
tracked outcome.
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
Request and result summaries must omit secrets, transcripts, raw command output, hidden reasoning,
|
|
60
62
|
and unnecessary personal data. Identical lifecycle retries are safe; never
|
|
61
63
|
replace a same-turn report with different content or let an older turn
|
|
62
64
|
overwrite newer state.
|
|
@@ -68,12 +70,13 @@ Existing delegated tasks may include the former inline ownership, linking, and
|
|
|
68
70
|
re-dispatching. Prefer `report_state` when available. If an older installed
|
|
69
71
|
TaskChef exposes only `report_result`, follow its inline protocol; after an
|
|
70
72
|
upgrade, the deprecated `report_result` alias remains available for exact
|
|
71
|
-
legacy retries. Also accept historical instructions
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
the
|
|
76
|
-
|
|
73
|
+
legacy retries. Also accept historical instructions with the former blank line
|
|
74
|
+
before a trailing marker, an exact HTML marker on the first line with or
|
|
75
|
+
without the former blank line, or the older exact
|
|
76
|
+
first-line `# taskchef_id=<full UUID>` heading. These compatibility forms do not
|
|
77
|
+
change the identity or lifecycle rules above. For either first-line form, the
|
|
78
|
+
assignment follows the marker. Ignore the final executor invocation and any
|
|
79
|
+
recognizable former inline ownership, linking, working-state, or
|
|
77
80
|
result-reporting paragraphs as lifecycle scaffolding; execute the remaining
|
|
78
81
|
task-specific body. Require non-whitespace task-specific content and never
|
|
79
82
|
treat an invocation by itself as an assignment.
|
|
@@ -44,15 +44,14 @@ all deterministic task-log operations.
|
|
|
44
44
|
detailed read. Native approval is live Codex state, not a `needs_input`
|
|
45
45
|
callback. An inactive status never proves semantic completion; it only
|
|
46
46
|
permits a trustworthy cached MCP result to stand.
|
|
47
|
-
4. In schema
|
|
48
|
-
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
semantic result into `results` and `lastResult` without rewriting their log line.
|
|
47
|
+
4. In schema 7, treat `turns` as the ordered request/result timeline. Each turn
|
|
48
|
+
pairs a bounded `requestSummary` with either one semantic `result` or null
|
|
49
|
+
while work is in progress. `latestTurn` is the compact current pair.
|
|
50
|
+
`results` and `lastResult` remain derived compatibility projections; do not
|
|
51
|
+
pair the latest request with an earlier result. Treat a failed result with
|
|
52
|
+
null thread and turn IDs as a fresh executor-creation failure. Schema 4/5/6
|
|
53
|
+
snapshots normalize their semantic results into legacy turns with a null
|
|
54
|
+
request summary without rewriting their log line.
|
|
56
55
|
No live read is possible or needed for that creation failure. When identity is certain and
|
|
57
56
|
metadata says the thread is inactive, trust the latest semantic result by
|
|
58
57
|
default in a broad overview unless a newer working state makes it historical.
|
package/src/cli.js
CHANGED
|
@@ -183,10 +183,10 @@ function taskDetails(task) {
|
|
|
183
183
|
`Updated by: ${singleLineDetail(task.updatedBy ?? "-")}`,
|
|
184
184
|
`Task ID: ${singleLineDetail(task.id)}`,
|
|
185
185
|
`Thread ID: ${singleLineDetail(task.threadId ?? "-")}`,
|
|
186
|
-
`
|
|
187
|
-
"
|
|
188
|
-
...[...task.
|
|
189
|
-
`- ${singleLineDetail(
|
|
186
|
+
`Turn count: ${task.turns.length}`,
|
|
187
|
+
"Activity timeline (newest first):",
|
|
188
|
+
...[...task.turns].reverse().map((turn) => (
|
|
189
|
+
`- ${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")}`
|
|
190
190
|
)),
|
|
191
191
|
"Instruction:",
|
|
192
192
|
task.instruction,
|
package/src/dashboard/app.js
CHANGED
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
dismissNotification,
|
|
4
4
|
findCurrentTask,
|
|
5
5
|
KNOWN_TASK_STATUSES,
|
|
6
|
+
latestTurnPresentation,
|
|
7
|
+
mergeProjectedTurns,
|
|
6
8
|
nextDateFilterRefreshDelay,
|
|
7
9
|
notificationDismissLabel,
|
|
8
10
|
notificationOpenLabel,
|
|
@@ -222,34 +224,44 @@ function detailRow(term, value) {
|
|
|
222
224
|
return [dt, dd];
|
|
223
225
|
}
|
|
224
226
|
|
|
225
|
-
function
|
|
226
|
-
if (
|
|
227
|
+
function turnTimeline(task) {
|
|
228
|
+
if (task.turns.length === 0) {
|
|
227
229
|
const empty = document.createElement("p");
|
|
228
230
|
empty.className = "result-history-empty";
|
|
229
|
-
empty.textContent = "No
|
|
231
|
+
empty.textContent = "No executor turn has been reported yet.";
|
|
230
232
|
return [empty];
|
|
231
233
|
}
|
|
232
|
-
return [...
|
|
234
|
+
return [...task.turns].reverse().map((turn, index) => {
|
|
233
235
|
const item = document.createElement("article");
|
|
234
236
|
item.className = `result-history-item${index === 0 ? " result-history-latest" : ""}`;
|
|
235
237
|
const header = document.createElement("div");
|
|
236
238
|
header.className = "result-history-header";
|
|
237
239
|
const status = document.createElement("span");
|
|
238
|
-
|
|
239
|
-
status.
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
const turnStatus = turn.result?.status ?? "working";
|
|
241
|
+
status.className = `status status-${turnStatus}`;
|
|
242
|
+
status.textContent = turnStatus.replaceAll("_", " ");
|
|
243
|
+
const turnKey = turn.turnId ?? `no-turn:${index}`;
|
|
244
|
+
const timestamp = timestampControl(turn.result?.updatedAt ?? turn.startedAt, {
|
|
245
|
+
accessibleName: `Turn updated time for ${turnStatus.replaceAll("_", " ")}`,
|
|
246
|
+
key: `detail:${task.id}:turn:${turnKey}`,
|
|
244
247
|
});
|
|
245
248
|
header.append(status, timestamp);
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
const requestLabel = document.createElement("h4");
|
|
250
|
+
requestLabel.textContent = "Request";
|
|
251
|
+
const request = document.createElement("p");
|
|
252
|
+
request.className = "preserve-lines";
|
|
253
|
+
request.textContent = turn.requestSummary ?? "Request not recorded by this TaskChef version.";
|
|
254
|
+
const resultLabel = document.createElement("h4");
|
|
255
|
+
resultLabel.textContent = "Result";
|
|
256
|
+
const result = document.createElement("p");
|
|
257
|
+
result.className = "preserve-lines";
|
|
258
|
+
result.textContent = turn.result?.summary ?? "In progress";
|
|
259
|
+
const turnMetadata = document.createElement("p");
|
|
260
|
+
turnMetadata.className = "result-history-turn";
|
|
261
|
+
turnMetadata.textContent = turn.turnId
|
|
262
|
+
? `Turn ${turn.turnId}`
|
|
263
|
+
: "No turn ID (creation failure)";
|
|
264
|
+
item.append(header, requestLabel, request, resultLabel, result, turnMetadata);
|
|
253
265
|
return item;
|
|
254
266
|
});
|
|
255
267
|
}
|
|
@@ -260,12 +272,13 @@ function renderDialog(task) {
|
|
|
260
272
|
: null;
|
|
261
273
|
const detailedTask = {
|
|
262
274
|
...task,
|
|
275
|
+
turns: mergeProjectedTurns(task, state.selectedTask?.turns ?? []),
|
|
263
276
|
results: task.results ?? preservedResults ?? [],
|
|
264
277
|
};
|
|
265
278
|
state.selectedTask = detailedTask;
|
|
266
279
|
elements.dialogProject.textContent = task.project.name;
|
|
267
280
|
elements.dialogTitle.textContent = task.title;
|
|
268
|
-
elements.dialogResults.replaceChildren(...
|
|
281
|
+
elements.dialogResults.replaceChildren(...turnTimeline(detailedTask));
|
|
269
282
|
elements.dialogInstruction.textContent = task.instruction;
|
|
270
283
|
elements.copyThreadId.disabled = !task.threadId;
|
|
271
284
|
elements.dialogMetadata.replaceChildren(
|
|
@@ -313,7 +326,7 @@ async function openDialog(task) {
|
|
|
313
326
|
}
|
|
314
327
|
} catch {
|
|
315
328
|
if (state.selectedTask?.id === task.id) {
|
|
316
|
-
showMessage("Task
|
|
329
|
+
showMessage("Task activity timeline is temporarily unavailable.");
|
|
317
330
|
}
|
|
318
331
|
}
|
|
319
332
|
}
|
|
@@ -337,7 +350,18 @@ function taskCard(task) {
|
|
|
337
350
|
project.textContent = task.project.name;
|
|
338
351
|
const summary = document.createElement("p");
|
|
339
352
|
summary.className = "task-summary";
|
|
340
|
-
|
|
353
|
+
const latest = latestTurnPresentation(task);
|
|
354
|
+
const requestLabel = document.createElement("strong");
|
|
355
|
+
requestLabel.textContent = "Request";
|
|
356
|
+
const request = document.createElement("span");
|
|
357
|
+
request.className = "preserve-lines";
|
|
358
|
+
request.textContent = latest.requestSummary;
|
|
359
|
+
const resultLabel = document.createElement("strong");
|
|
360
|
+
resultLabel.textContent = "Result";
|
|
361
|
+
const result = document.createElement("span");
|
|
362
|
+
result.className = "preserve-lines";
|
|
363
|
+
result.textContent = latest.resultSummary;
|
|
364
|
+
summary.replaceChildren(requestLabel, request, resultLabel, result);
|
|
341
365
|
const time = timestampControl(
|
|
342
366
|
task.meaningfulUpdatedAt ?? task.updatedAt ?? task.createdAt,
|
|
343
367
|
{
|
package/src/dashboard/index.html
CHANGED
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
<button id="copy-thread-id" class="secondary-button" type="button">Copy thread ID</button>
|
|
95
95
|
</div>
|
|
96
96
|
<section>
|
|
97
|
-
<h3>
|
|
97
|
+
<h3>Activity timeline</h3>
|
|
98
98
|
<div id="dialog-results" class="result-history"></div>
|
|
99
99
|
</section>
|
|
100
100
|
<section>
|