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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.8.0",
3
+ "version": "7.9.1",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
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 keeps every semantic result
7
- while projecting the latest one for compact navigation and reporting.
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 -> current state + result history
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 6; schema 4/5 migration supported)
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
- TaskChef stores the current reported execution state and appends every concise
123
- semantic result to `results`. A follow-up therefore appears as `working`
124
- immediately without erasing any prior outcome. The final entry is exposed as a
125
- derived `lastResult` compatibility alias; it is not persisted independently and
126
- is planned for removal in the next major version after callers move to
127
- `results.at(-1)`. TaskChef does not store transcripts or non-semantic events.
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-result
178
- projection; opening task details fetches the full newest-first result history.
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
  ![Immutable event-time dashboard notifications](docs/images/notification-event-snapshots.jpg)
205
205
 
206
- ![Task detail result history](docs/images/result-history-dashboard.jpg)
206
+ ![Task detail activity timeline](docs/images/result-history-dashboard.jpg)
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 6 under the workspace lock. It validates the complete
222
- source and converted log before writing, creates an exclusive `tasks.jsonl.pre-v6-*.bak`
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 6 remain unsupported. Retain such a workspace
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
 
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
- | **Result history** | The ordered collection of every accepted `completed`, `needs_input`, or `failed` per-turn outcome. |
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-6 records, one per line. Schemas 4 and 5 are supported migration/read
73
- formats; every new record and state mutation MUST write schema 6. Other schemas
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 `6`; schema-4/5 records remain readable until explicit migration or their next mutation. |
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
- | `results` | Ordered oldest-first array of `{status, summary, turnId, updatedAt}` semantic results, with unique turn IDs and nondecreasing timestamps. |
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 `lastResult` as null for an empty
100
- history or the final `results` entry. `lastResult` MUST be derived and MUST NOT
101
- be persisted in schema 6. It is a compatibility alias for schema-4/5, CLI,
102
- MCP, reporting-skill, and dashboard-list callers, with removal planned for the
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 blank line, the returned
121
- marker, and exactly one concise explicit `$taskchef-executor` invocation on
122
- the final line. It MUST NOT inline the executor protocol into a new
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 a first-line HTML marker, the older
141
- first-line `# taskchef_id=<full UUID>` heading, or the former blank line and
142
- inline executor protocol MUST remain marker-readable and executable. Their
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 6, `working`, null summary/turn/thread/lastResult,
242
- an empty `results` array,
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 `results`.
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. Success sets the current state and
290
- appends a semantic result to `results`; starting newer work preserves the entire
291
- collection. An identical retry for any settled turn returns success without an
292
- append. A different result for a settled turn, a stale turn, or a semantic
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 6; unsupported schemas remain rejected.
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 `results` history and include the
328
- derived latest-result projection. The bounded per-task detail endpoint MAY
329
- return the full validated task so the dialog can render history newest first.
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. A legacy `lastResult`, or a schema-4 current semantic
363
- state, becomes zero or one initial `results` entry. Migration MUST validate the
364
- complete source and complete schema-6 candidate before changing the task log,
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-6 log MUST be an idempotent
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-6 snapshot
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 while preserving the ordered
120
- semantic result history.
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, null)
131
+ E->>M: report_state(..., working, requestSummary)
132
132
  M->>W: reportTaskState()
133
- W->>W: Store current turn and preserve results
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: Append semantic result and derive lastResult
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, null)
174
+ E->>M: report_state(..., turnB, working, requestB)
175
175
  M->>W: reportTaskState()
176
- W->>W: Require turnB greater and preserve results
177
- W-->>M: working snapshot plus history A
178
- M-->>E: working snapshot plus history A
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 without results
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 updates do not resend
283
- unnecessary history. A read-only per-task endpoint returns full history only
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 each schema-4/5 latest
313
- result into zero or one initial schema-6 `results` entry, then validates the
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-v6-*.bak` file. The task log is replaced atomically and
316
- validated again. A second run sees only schema 6 and returns unchanged without
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 6 are accepted. Schemas 4/5
334
- are read/migration compatibility until an explicit migration or lifecycle
335
- mutation upgrades each record to schema 6. Schema 6 persists `results` only and
336
- derives `lastResult` for compatibility. Other schemas are rejected without rewrite.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.8.0",
3
+ "version": "7.9.1",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -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 blank line and the preparation's exact
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 blank
13
- line, the exact `<!-- taskchef_id=<full UUID> -->` marker, and the final
14
- explicit skill invocation. Treat that UUID as the TaskChef task ID. The
15
- assignment is everything before the blank line that precedes the marker; the
16
- marker and invocation are lifecycle scaffolding, not part of the deliverable.
17
- Require exactly one marker and do not infer an ID from similar prose.
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`, and an omitted or null summary.
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
- Summaries must omit secrets, transcripts, raw command output, hidden reasoning,
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 whose exact HTML marker is
72
- the first line, with or without the former blank line, and the older exact
73
- first-line `# taskchef_id=<full UUID>` heading. These compatibility forms do
74
- not change the identity or lifecycle rules above. For either first-line form,
75
- the assignment follows the marker. Ignore the final executor invocation and
76
- any recognizable former inline ownership, linking, working-state, or
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 6, treat `status`, `turnId`, and `updatedAt` as the latest reported
48
- execution state and treat `results` as the ordered semantic history.
49
- `lastResult` is the derived compatibility alias for `results.at(-1)`; prefer
50
- the collection when full history matters and the alias for compact latest-state
51
- reporting. A `working` state with a non-null `lastResult` means a newer executor
52
- turn started after that result; show the prior result as history, not as the
53
- current outcome. Treat a failed `lastResult` with null thread and turn IDs as
54
- a fresh executor-creation failure. Schema 4/5 snapshots normalize zero or one
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
- `Result count: ${task.results.length}`,
187
- "Result history (newest first):",
188
- ...[...task.results].reverse().map((result) => (
189
- `- ${singleLineDetail(result.updatedAt)} | ${singleLineDetail(result.status)} | turn ${singleLineDetail(result.turnId ?? "-")} | ${singleLineDetail(result.summary)}`
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,
@@ -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 resultHistory(taskId, results) {
226
- if (results.length === 0) {
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 semantic result has been reported yet.";
231
+ empty.textContent = "No executor turn has been reported yet.";
230
232
  return [empty];
231
233
  }
232
- return [...results].reverse().map((result, index) => {
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
- status.className = `status status-${result.status}`;
239
- status.textContent = result.status.replaceAll("_", " ");
240
- const resultKey = result.turnId ?? `${result.status}:${index}`;
241
- const timestamp = timestampControl(result.updatedAt, {
242
- accessibleName: `Result updated time for ${result.status.replaceAll("_", " ")}`,
243
- key: `detail:${taskId}:result:${resultKey}`,
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 summary = document.createElement("p");
247
- summary.className = "preserve-lines";
248
- summary.textContent = result.summary;
249
- const turn = document.createElement("p");
250
- turn.className = "result-history-turn";
251
- turn.textContent = result.turnId ? `Turn ${result.turnId}` : "No turn ID (creation failure)";
252
- item.append(header, summary, turn);
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(...resultHistory(task.id, detailedTask.results));
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 result history is temporarily unavailable.");
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
- summary.textContent = task.lastResult?.summary ?? "No semantic result reported yet.";
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
  {
@@ -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>Result history</h3>
97
+ <h3>Activity timeline</h3>
98
98
  <div id="dialog-results" class="result-history"></div>
99
99
  </section>
100
100
  <section>