taskchef 7.1.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
@@ -31,6 +31,7 @@
31
31
  "defaultPrompt": [
32
32
  "$taskchef-bootstrap Set up TaskChef in this folder.",
33
33
  "$taskchef-delegate Dispatch this request to the right project.",
34
+ "$taskchef-executor Execute this delegated TaskChef assignment.",
34
35
  "$taskchef-report Report on work in the TaskChef task history."
35
36
  ],
36
37
  "brandColor": "#D97706",
package/README.md CHANGED
@@ -31,7 +31,7 @@ codex plugin add taskchef@favoyang-plugins
31
31
  npm install --global taskchef
32
32
  ```
33
33
 
34
- The plugin provides three skills and a local MCP server. The npm installation
34
+ The plugin provides four skills and a local MCP server. The npm installation
35
35
  puts the `taskchef` CLI on `PATH`. TaskChef installs no hooks, schedules,
36
36
  daemons, or background identity search.
37
37
 
@@ -86,9 +86,21 @@ $taskchef-delegate In payments, add structured logs for failed retries and test
86
86
  ```
87
87
 
88
88
  TaskChef prepares a UUID and marker, persists the task before native creation,
89
- creates the executor, and returns its task link. The executor reads its own
90
- `CODEX_THREAD_ID` and self-links before substantive work. Independent
91
- outcomes may become separate executors; dependent work should stay together.
89
+ creates the executor, and returns its task link. New executor instructions keep
90
+ the assignment visible from the first line, then place the correlation marker
91
+ immediately before an explicit `$taskchef-executor` invocation. That skill
92
+ reads the executor's own `CODEX_THREAD_ID`, self-links, and reports lifecycle
93
+ state. Independent outcomes may become separate executors; dependent work
94
+ should stay together.
95
+
96
+ For example, TaskChef generates this shape:
97
+
98
+ ```text
99
+ Fix duplicate charges after a retry and add a regression test.
100
+
101
+ <!-- taskchef_id=c0f010ff-84f2-4838-a69d-0ff1f5d721d7 -->
102
+ Use $taskchef-executor to execute and report this delegated TaskChef assignment.
103
+ ```
92
104
 
93
105
  ## Work with and report executors
94
106
 
@@ -105,6 +117,11 @@ the last concise semantic result. A follow-up therefore appears as `working`
105
117
  immediately without erasing the previous outcome. TaskChef does not store the
106
118
  transcript or a lifecycle event log.
107
119
 
120
+ Delegated tasks created by earlier TaskChef versions remain compatible: their
121
+ inline executor protocol still parses, self-links, and may use the deprecated
122
+ `report_result` alias. The v7 inline-paragraph named exports remain as deprecated
123
+ historical snapshots, but new delegations use the executor skill and `report_state`.
124
+
108
125
  ## View and report tasks
109
126
 
110
127
  Ask the dispatcher for an on-demand report:
@@ -70,7 +70,7 @@ responsibility until an outcome is landed, transferred, or safely preserved.
70
70
  | Dimension | TaskChef 7 | FirstMate at `038d0f7` |
71
71
  | --- | --- | --- |
72
72
  | Primary goal | Put multi-project work into the right visible Codex task and make it findable later. | Let one person direct a supervised crew across projects through one liaison. |
73
- | Product form | Codex plugin: three skills, local MCP server, data CLI, and loopback dashboard. | Cloneable agent distribution: instructions, internal skills, scripts, policies, and private on-disk state. |
73
+ | Product form | Codex plugin: four skills, local MCP server, data CLI, and loopback dashboard. | Cloneable agent distribution: instructions, internal skills, scripts, policies, and private on-disk state. |
74
74
  | Runtime | Native Codex desktop tasks are the executors and live source of truth. | Harness-driven workers in tmux by default, with documented alternative session backends; Codex can be a harness, but Codex desktop is not a runtime backend. |
75
75
  | Coordinator lifetime | Dispatcher returns immediately and does not supervise. | First mate remains active and uses watcher/guard mechanisms to supervise meaningful events. |
76
76
  | Project isolation | Delegates to the configured Codex project; isolation follows native Codex/project behavior. | Ship and scout workers require separate Treehouse- or backend-managed worktrees. |
@@ -108,7 +108,7 @@ also insert an interpretation layer between user and worker.
108
108
 
109
109
  ### TaskChef
110
110
 
111
- **Fact:** `prepare_dispatch` allocates a TaskChef UUID and exact first-line
111
+ **Fact:** `prepare_dispatch` allocates a TaskChef UUID and exact correlation
112
112
  marker. `record_task` persists the marked instruction before native task
113
113
  creation. The executor then reads its own `CODEX_THREAD_ID` and calls
114
114
  `link_task`; the dispatcher neither searches recent tasks nor repairs identity.
@@ -215,7 +215,7 @@ harness adapter.
215
215
  | --- | --- | --- |
216
216
  | Codex | Built around native Codex projects, tasks, thread reads, and desktop deep links. | Codex is one verified harness; desktop tasks are not its worker runtime. |
217
217
  | GitHub | Configured repository URLs aid routing; delivery remains the executor project's concern. | GitHub CLI, PR state, and configured delivery modes are part of supervised shipping workflows. |
218
- | Skills | Three plugin skills with narrow bootstrap, delegate, and report responsibilities. | Internal firstmate-only skills plus standalone public skills; AGENTS.md routes conditional procedures. |
218
+ | Skills | Four plugin skills with narrow bootstrap, delegate, executor, and report responsibilities. | Internal firstmate-only skills plus standalone public skills; AGENTS.md routes conditional procedures. |
219
219
  | MCP | Four primary local TaskChef tools plus one deprecated compatibility alias own deterministic identity and state writes. | FirstMate describes itself as an agent distribution rather than an MCP product; deterministic behavior lives largely in scripts. |
220
220
  | Runtime extensions | Extend plugin skills/MCP/CLI/dashboard or compose native Codex capabilities. | Add or verify harness adapters, session backends, dispatch profiles, operational scripts, and optional integrations. |
221
221
 
package/docs/spec.md CHANGED
@@ -18,7 +18,7 @@ is dated research, not contract.
18
18
  | **Delegated task** | One independently useful outcome represented by one TaskChef task UUID and snapshot. |
19
19
  | **Executor** | The native Codex task created to own and perform one delegated task. |
20
20
  | **Task record** | One complete JSON object in `tasks.jsonl`; it contains immutable intent/project fields and mutable identity/result fields. |
21
- | **Marker** | The exact first instruction line `<!-- taskchef_id=<lowercase full UUID> -->`, followed by a blank line. |
21
+ | **Marker** | The exact correlation line `<!-- taskchef_id=<lowercase full UUID> -->`; new instructions place it after the complete assignment and immediately before the executor-skill invocation. |
22
22
  | **Record-before-create** | Persisting a link-pending task before asking Codex to create its executor. |
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`. |
@@ -26,7 +26,7 @@ is dated research, not contract.
26
26
  | **Last semantic result** | The most recent `completed`, `needs_input`, or `failed` outcome, preserved separately while a newer turn is working. |
27
27
  | **Current turn ID** | The canonical Codex UUIDv7 returned by an exact native read of the linked executor for the turn being reported. |
28
28
  | **Dashboard** | The loopback, read-only UI derived from validated workspace snapshots and bounded native actions. |
29
- | **Skill** | One packaged agent procedure: `taskchef-bootstrap`, `taskchef-delegate`, or `taskchef-report`. |
29
+ | **Skill** | One packaged agent procedure: `taskchef-bootstrap`, `taskchef-delegate`, `taskchef-executor`, or `taskchef-report`. |
30
30
 
31
31
  ## Components and ownership
32
32
 
@@ -34,6 +34,9 @@ is dated research, not contract.
34
34
  diagnostics. It MUST NOT dispatch or report unless separately requested.
35
35
  - `taskchef-delegate` MUST own routing and record-before-create delegation.
36
36
  It MUST return after creation and MUST NOT poll, supervise, or infer identity.
37
+ - `taskchef-executor` MUST own executor assignment ownership, self-linking,
38
+ exact thread/turn identity, per-turn state reporting, failure behavior,
39
+ privacy, and idempotency. It MUST NOT dispatch the owned assignment again.
37
40
  - `taskchef-report` MUST own on-demand reporting. It MUST NOT poll or persist
38
41
  inferred state.
39
42
  - The MCP server MUST expose four primary lifecycle tools plus the deprecated
@@ -99,8 +102,11 @@ fields MUST NOT change after recording.
99
102
 
100
103
  1. The dispatcher MUST call `prepare_dispatch` once per outcome.
101
104
  2. It MUST choose exactly one configured project and exact native-project path.
102
- 3. It MUST build the instruction from the returned marker, a blank line, the
103
- packaged executor contract paragraphs, and the user's outcome.
105
+ 3. It MUST build the instruction with the user's outcome beginning on line 1
106
+ and remaining uninterrupted, followed by one blank line, the returned
107
+ marker, and exactly one concise explicit `$taskchef-executor` invocation on
108
+ the final line. It MUST NOT inline the executor protocol into a new
109
+ instruction.
104
110
  4. It MUST call `record_task` with `threadId: null` before native creation.
105
111
  5. It MUST create exactly one native Codex executor and return immediately.
106
112
  6. The executor MUST read its own `CODEX_THREAD_ID` and call `link_task`
@@ -117,6 +123,16 @@ If native creation fails after recording, the dispatcher MUST call
117
123
  A link failure MUST remain visible and retryable; the executor MUST report it
118
124
  visibly and MUST NOT continue substantive work.
119
125
 
126
+ Previously recorded instructions with a first-line HTML marker, the older
127
+ first-line `# taskchef_id=<full UUID>` heading, or the former blank line and
128
+ inline executor protocol MUST remain marker-readable and executable. Their
129
+ `report_result` calls MUST remain supported by the deprecated alias. New
130
+ instructions MUST use the trailing marker and explicit executor skill contract
131
+ above. A historical first-line instruction with an executor-skill invocation
132
+ MUST contain exactly one invocation as its final line. A former inline-protocol
133
+ instruction MUST retain non-whitespace task-specific content beyond its known
134
+ lifecycle paragraphs.
135
+
120
136
  `needs_input` MUST mean a semantic user decision or missing fact. A native
121
137
  approval prompt MUST remain live Codex state and MUST NOT be stored as
122
138
  `needs_input`.
@@ -166,7 +182,7 @@ new preparation values, though it writes no state.
166
182
  | `id` | Non-empty string; MUST equal the instruction marker. |
167
183
  | `project` | Non-empty configured project path. |
168
184
  | `title` | Non-empty string. |
169
- | `instruction` | Non-empty string beginning with the exact marker and blank line. |
185
+ | `instruction` | Non-empty string containing exactly one accepted marker and a non-empty assignment. New instructions use the required trailing marker and executor-invocation scaffold; historical first-line forms remain accepted. |
170
186
  | `threadId` | Literal null. |
171
187
 
172
188
  **Structured output:** `{ task: Task }`.
package/docs/workflows.md CHANGED
@@ -11,10 +11,11 @@ research.
11
11
  | Surface | Responsibility |
12
12
  | --- | --- |
13
13
  | `skills/taskchef-delegate/SKILL.md` | Split, route, record-before-create, create, return. |
14
+ | `skills/taskchef-executor/SKILL.md` | Own, self-link, execute, and report every executor turn. |
14
15
  | `skills/taskchef-bootstrap/SKILL.md` | Initialize current workspace and configure projects. |
15
16
  | `skills/taskchef-report/SKILL.md` | Select cached tasks and perform bounded live checks. |
16
17
  | `src/mcp.js` | Four primary lifecycle tools, one deprecated alias, and MCP annotations. |
17
- | `src/delegation.js` | UUID marker, executor contract paragraphs, and creation-failure handling. |
18
+ | `src/delegation.js` | UUID marker, concise executor-skill invocation shape, and creation-failure handling. |
18
19
  | `src/workspace.js` | Current schemas, validation, locking, atomic JSONL writes, linking, and result freshness. |
19
20
  | `src/cli.js` | Administration, inspection, diagnostics, and dashboard startup. |
20
21
  | `src/dashboard.js` | Validated snapshots, SSE fan-out, and bounded open actions. |
@@ -55,7 +56,7 @@ sequenceDiagram
55
56
  D->>C: Create executor with marked instruction
56
57
  C-->>D: Created-task reference
57
58
  D-->>U: Return immediately
58
- C->>E: Start executor
59
+ C->>E: Start executor and load $taskchef-executor
59
60
  E->>E: Read own CODEX_THREAD_ID
60
61
  E->>M: link_task(taskId, threadId)
61
62
  M->>W: linkTask()
@@ -67,6 +68,12 @@ Record-before-create makes native creation failure observable. Executor
67
68
  self-linking removes dispatcher-side polling, task search, title matching, and
68
69
  parent/child identity inference.
69
70
 
71
+ The generated task begins with the complete assignment, then places its marker
72
+ immediately before one explicit `$taskchef-executor` invocation. Older
73
+ recorded tasks with first-line HTML or heading markers and former inline
74
+ protocol remain readable; the deprecated `report_result` alias preserves their
75
+ semantic callbacks.
76
+
70
77
  ## State reporting
71
78
 
72
79
  The executor obtains the turn identity from an exact native read of its own
package/index.js CHANGED
@@ -33,6 +33,7 @@ export {
33
33
  EXECUTOR_LINK_PARAGRAPH,
34
34
  EXECUTOR_RESULT_PARAGRAPH,
35
35
  EXECUTOR_WORKING_PARAGRAPH,
36
+ EXECUTOR_SKILL_INVOCATION,
36
37
  createAndRecordDelegation,
37
38
  isProvisionalThreadId,
38
39
  normalizeCodexThreadId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -33,6 +33,7 @@
33
33
  "src",
34
34
  "skills/taskchef-bootstrap",
35
35
  "skills/taskchef-delegate",
36
+ "skills/taskchef-executor",
36
37
  "skills/taskchef-report"
37
38
  ],
38
39
  "engines": {
@@ -38,7 +38,7 @@ all deterministic workspace operations.
38
38
  `codex add` or hard-code an application bundle path.
39
39
  3. `workspace init` takes no stdin, creates an empty
40
40
  configuration when missing, creates the one-entry-per-task JSONL log, and
41
- refreshes managed instructions. The installed plugin provides all three
41
+ refreshes managed instructions. The installed plugin provides all four
42
42
  TaskChef skills outside the dispatcher workspace.
43
43
  4. Run `doctor --json` after setup or when the user asks to diagnose the
44
44
  workspace. Doctor is read-only. Rerun `workspace init --json` to repair the
@@ -10,17 +10,22 @@ and return immediately.
10
10
 
11
11
  ## Invocation boundary
12
12
 
13
- A task whose initial structured `codexDelegation.input` starts with an exact
14
- `<!-- taskchef_id=<full UUID> -->` marker already owns that delegated
15
- assignment. Execute it in the current task. Do not re-dispatch it merely
16
- because it concerns TaskChef or a configured project. Explicit requests to
17
- delegate separate work remain valid.
18
-
19
- Use the bundled `prepare_dispatch`, `record_task`, `link_task`, and
20
- `report_state` MCP tools directly. `report_result` is a deprecated compatibility
21
- alias and must not be emitted into new executor instructions. Never fall back to shell writes. If a
22
- required tool is unavailable, stop and report that the TaskChef plugin must be
23
- reloaded or installed.
13
+ A task whose initial structured `codexDelegation.input` contains either the
14
+ exact new trailing TaskChef marker plus final `$taskchef-executor` invocation,
15
+ an exact first-line HTML marker, or the historical first-line
16
+ `# taskchef_id=<full UUID>` heading already owns that delegated assignment.
17
+ This includes former inline-protocol tasks that lack the skill invocation.
18
+ An owned instruction must have exactly one accepted marker and a non-whitespace
19
+ task-specific assignment. If it contains an executor-skill invocation, require
20
+ exactly one as the final line. Marker-only, duplicate-marker, scaffold-only, or
21
+ misplaced-invocation inputs are not valid delegated tasks. Execute a valid one
22
+ in the current task. Do not re-dispatch it merely because it concerns TaskChef
23
+ or a configured project. Explicit requests to delegate separate work remain
24
+ valid.
25
+
26
+ Use the bundled `prepare_dispatch`, `record_task`, and `report_state` MCP tools
27
+ directly. Never fall back to shell writes. If a required tool is unavailable,
28
+ stop and report that the TaskChef plugin must be reloaded or installed.
24
29
 
25
30
  ## Boundaries
26
31
 
@@ -43,16 +48,17 @@ reloaded or installed.
43
48
  3. Route against configured project `name`, `description`, and canonical
44
49
  `githubRepos`; use `path` only as checkout identity. Require exactly one
45
50
  match and an exact native-project path. Ask instead of guessing.
46
- 4. Prefix each executor instruction with the preparation's exact marker as the
47
- first line, a blank line, and these required paragraphs before the body:
48
-
49
- > This task owns the delegated assignment. Execute it in this task; do not re-dispatch it merely because it concerns TaskChef or a configured project. Explicit requests to delegate separate work remain valid.
50
-
51
- > Before any other work, read this executor's own durable Codex thread ID from the current task's CODEX_THREAD_ID environment value and call the TaskChef link_task MCP tool with that thread ID and the marked TaskChef task ID. Never use CODEX_SESSION_ID or the parent or delegator thread ID. If linking fails, CODEX_THREAD_ID is unavailable, or the tool is unavailable, report the failure visibly and retry on a later turn; do not guess an identity or continue substantive work while the task is link-pending.
52
-
53
- > After a successful initial link, and at the start of every follow-up turn before substantive work, read this exact Codex thread natively to obtain the current turn ID and call TaskChef report_state with the marked task ID, the self-linked thread ID, that current turn ID, status working, and summary omitted or null. link_task remains the first TaskChef action on the initial turn; do not report working before identity is linked. Never reuse a prior turn ID after a follow-up.
54
-
55
- > Before ending, read this exact Codex thread again and call TaskChef report_state for the same current working turn with status completed, needs_input, or failed and a concise summary. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.
51
+ 4. Build each executor instruction in this exact shape:
52
+
53
+ - Begin with the actual assignment on the first line and keep its complete
54
+ body uninterrupted.
55
+ - After the assignment, add one blank line and the preparation's exact
56
+ marker on its own line.
57
+ - Immediately after the marker, end the instruction with exactly:
58
+ `Use $taskchef-executor to execute and report this delegated TaskChef assignment.`
59
+ - Include exactly one marker and exactly one executor-skill invocation.
60
+ - Do not inline executor ownership, identity, linking, or result-reporting
61
+ protocol. The explicitly invoked executor skill owns those mechanics.
56
62
 
57
63
  5. Before creating each executor, call `record_task` exactly once with `id`,
58
64
  `project`, `title`, the exact marked `instruction`, and `threadId: null`.
@@ -63,23 +69,3 @@ reloaded or installed.
63
69
  when creation returns a durable ID; the child must self-link.
64
70
  8. If creation fails after recording, call `report_state` with `failed`, null
65
71
  thread/turn IDs, and a bounded summary before returning the failure.
66
-
67
- ## Executor contract
68
-
69
- The executor must make `link_task(taskId, threadId)` its first TaskChef action.
70
- It obtains its own durable ID from the current task's `CODEX_THREAD_ID`, never
71
- from the delegation's `sourceThreadId`, `CODEX_SESSION_ID`, inherited session
72
- metadata, title matching, or a parent task.
73
- Identical retries are safe. A rejected link, unavailable tool, or interrupted
74
- initial turn leaves the record visibly link-pending and retryable; the executor
75
- must not guess or do substantive work first.
76
-
77
- After linking on the initial turn, and before substantive work on every later
78
- turn, the executor reads the exact thread and calls `report_state` with
79
- `working`, the current turn ID, and no summary. Before ending that same turn it
80
- reports a semantic state with the same turn ID and a summary. A follow-up must
81
- use the new turn ID. `needs_input` is only for a real user decision, not live
82
- approval UI.
83
-
84
- The filesystem watcher surfaces `link_task` and `report_state` writes to the
85
- dashboard. The linked child ID drives the exact Codex deep link.
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: taskchef-executor
3
+ description: "Execute an assignment carrying either the new exact TaskChef marker-plus-invocation scaffold or an accepted historical first-line TaskChef marker or inline protocol. Includes executor ownership, self-linking, per-turn lifecycle reporting, identity safety, and final semantic state. Use when explicitly invoked by a new delegated instruction or when resuming the same new or historical executor task. Do not use to dispatch work or report on other TaskChef tasks."
4
+ ---
5
+
6
+ # TaskChef Executor
7
+
8
+ Own and execute the delegated assignment in the current Codex task. Do not
9
+ re-dispatch it merely because it concerns TaskChef or a configured project.
10
+ Explicit requests to delegate separate work remain valid.
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.
18
+
19
+ ## Start every execution turn
20
+
21
+ Complete this lifecycle setup before substantive assignment work:
22
+
23
+ 1. Read this task's own durable Codex thread ID from `CODEX_THREAD_ID`. Never
24
+ use `CODEX_SESSION_ID`, `sourceThreadId`, a parent or delegator ID,
25
+ inherited metadata, title matching, recent-task search, transcripts, or a
26
+ provisional client ID.
27
+ 2. On the initial turn, call TaskChef `link_task` with the marked task ID and
28
+ that exact thread ID as the first TaskChef action. An identical retry is
29
+ idempotent. On a follow-up, retry the same link only when the prior link
30
+ cannot be established from the task context.
31
+ 3. Read this exact Codex thread natively and obtain the current turn ID. Do not
32
+ infer it or reuse an earlier turn ID.
33
+ 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
+
36
+ If `CODEX_THREAD_ID`, exact native thread reading, or a required TaskChef tool
37
+ is unavailable, or if linking or the working-state report fails, report the
38
+ failure visibly and stop before substantive work. Retry on a later turn. Never
39
+ guess identity or bypass a link-pending state.
40
+
41
+ ## Finish every execution turn
42
+
43
+ Before ending, read this exact Codex thread again and call `report_state` for
44
+ the current turn with one semantic status and a concise summary:
45
+
46
+ - `completed` only when the assignment is genuinely complete.
47
+ - `needs_input` only when a semantic decision or missing information must come
48
+ from the user.
49
+ - `failed` when the requested outcome cannot be completed or safely resumed.
50
+
51
+ A live native approval prompt is Codex state, not semantic `needs_input`; leave
52
+ the approval live instead of storing it as a TaskChef result. Never invent or
53
+ reuse a turn ID after a follow-up. If a final-report response is lost, an
54
+ identical retry is safe only while the same turn remains current. On a later
55
+ turn, run the start lifecycle with its new current turn ID and report that
56
+ turn's actual outcome. Say reporting failures visibly instead of claiming a
57
+ tracked outcome.
58
+
59
+ Summaries must omit secrets, transcripts, raw command output, hidden reasoning,
60
+ and unnecessary personal data. Identical lifecycle retries are safe; never
61
+ replace a same-turn report with different content or let an older turn
62
+ overwrite newer state.
63
+
64
+ ## Compatibility
65
+
66
+ Existing delegated tasks may include the former inline ownership, linking, and
67
+ `report_result` paragraphs. Continue executing those tasks here without
68
+ re-dispatching. Prefer `report_state` when available. If an older installed
69
+ TaskChef exposes only `report_result`, follow its inline protocol; after an
70
+ 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
77
+ result-reporting paragraphs as lifecycle scaffolding; execute the remaining
78
+ task-specific body. Require non-whitespace task-specific content and never
79
+ treat an invocation by itself as an assignment.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "TaskChef Executor"
3
+ short_description: "Run and report a delegated TaskChef assignment."
4
+ default_prompt: "Use $taskchef-executor to execute and report this delegated TaskChef assignment."
package/src/delegation.js CHANGED
@@ -1,14 +1,42 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
 
3
+ /** @deprecated Historical v7 inline-prompt snapshot. New delegations use taskchef-executor. */
3
4
  export const EXECUTOR_OWNERSHIP_PARAGRAPH = "This task owns the delegated assignment. Execute it in this task; do not re-dispatch it merely because it concerns TaskChef or a configured project. Explicit requests to delegate separate work remain valid.";
5
+ /** @deprecated Historical v7 inline-prompt snapshot. New delegations use taskchef-executor. */
4
6
  export const EXECUTOR_LINK_PARAGRAPH = "Before any other work, read this executor's own durable Codex thread ID from the current task's CODEX_THREAD_ID environment value and call the TaskChef link_task MCP tool with that thread ID and the marked TaskChef task ID. Never use CODEX_SESSION_ID or the parent or delegator thread ID. If linking fails, CODEX_THREAD_ID is unavailable, or the tool is unavailable, report the failure visibly and retry on a later turn; do not guess an identity or continue substantive work while the task is link-pending.";
7
+ /** @deprecated Historical v7 inline-prompt snapshot. New delegations use taskchef-executor. */
5
8
  export const EXECUTOR_WORKING_PARAGRAPH = "After a successful initial link, and at the start of every follow-up turn before substantive work, read this exact Codex thread natively to obtain the current turn ID and call TaskChef report_state with the marked task ID, the self-linked thread ID, that current turn ID, status working, and summary omitted or null. link_task remains the first TaskChef action on the initial turn; do not report working before identity is linked. Never reuse a prior turn ID after a follow-up.";
9
+ /** @deprecated Historical v7 inline-prompt snapshot. New delegations use taskchef-executor. */
6
10
  export const EXECUTOR_RESULT_PARAGRAPH = "Before ending, read this exact Codex thread again and call TaskChef report_state for the same current working turn with status completed, needs_input, or failed and a concise summary. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.";
11
+ export const EXECUTOR_SKILL_INVOCATION = "Use $taskchef-executor to execute and report this delegated TaskChef assignment.";
12
+ const HISTORICAL_RESULT_WITH_TURN_PARAGRAPH = "Before ending, call the TaskChef report_result MCP tool with the marked task ID, this executor's self-linked thread ID, the current turn ID from an exact native read of that same thread, completed, needs_input, or failed, and a concise summary. Never reuse a prior turn ID after a follow-up. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.";
13
+ const HISTORICAL_RESULT_PARAGRAPH = "Before ending, call the TaskChef report_result MCP tool with completed, needs_input, or failed and a concise summary. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.";
14
+ const HISTORICAL_EXECUTOR_SCAFFOLD_LINES = new Set([
15
+ EXECUTOR_OWNERSHIP_PARAGRAPH,
16
+ EXECUTOR_LINK_PARAGRAPH,
17
+ EXECUTOR_WORKING_PARAGRAPH,
18
+ EXECUTOR_RESULT_PARAGRAPH,
19
+ HISTORICAL_RESULT_WITH_TURN_PARAGRAPH,
20
+ HISTORICAL_RESULT_PARAGRAPH,
21
+ ]);
22
+ const HISTORICAL_INLINE_PROTOCOLS = [
23
+ [EXECUTOR_OWNERSHIP_PARAGRAPH, EXECUTOR_LINK_PARAGRAPH, EXECUTOR_WORKING_PARAGRAPH, EXECUTOR_RESULT_PARAGRAPH],
24
+ [EXECUTOR_OWNERSHIP_PARAGRAPH, EXECUTOR_LINK_PARAGRAPH, HISTORICAL_RESULT_WITH_TURN_PARAGRAPH],
25
+ [EXECUTOR_OWNERSHIP_PARAGRAPH, HISTORICAL_RESULT_PARAGRAPH],
26
+ [EXECUTOR_OWNERSHIP_PARAGRAPH],
27
+ ];
28
+
29
+ function hasTaskSpecificContent(lines) {
30
+ return lines.some((line) => (
31
+ line.trim().length > 0 && !HISTORICAL_EXECUTOR_SCAFFOLD_LINES.has(line)
32
+ ));
33
+ }
7
34
 
8
35
  const UUID_SOURCE = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
9
36
  const UUID_PATTERN = new RegExp(`^${UUID_SOURCE}$`);
10
37
  const CODEX_UUID_V7_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
11
38
  const TASKCHEF_MARKER_PATTERN = new RegExp(`^<!-- taskchef_id=(${UUID_SOURCE}) -->$`);
39
+ const LEGACY_TASKCHEF_MARKER_PATTERN = new RegExp(`^# taskchef_id=(${UUID_SOURCE})$`);
12
40
 
13
41
  function requireObject(value, name) {
14
42
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${name} must be an object`);
@@ -84,20 +112,93 @@ export function taskChefMarker(taskId) {
84
112
 
85
113
  export function parseTaskChefMarker(instruction) {
86
114
  if (typeof instruction !== "string") return null;
87
- const firstLine = instruction.split(/\r?\n/, 1)[0];
88
- const currentMatch = firstLine.match(TASKCHEF_MARKER_PATTERN);
89
- if (currentMatch === null) return null;
90
- const prefix = instruction.match(/^([^\r\n]*)(\r?\n)\2/);
91
- return prefix === null ? null : currentMatch[1];
115
+ const lines = instruction.split(/\r\n|\r|\n/);
116
+ const hasHistoricalAssignment = () => {
117
+ const rest = lines.slice(1);
118
+ const executorSkillInvocationIndices = rest.flatMap((line, index) => (
119
+ line === EXECUTOR_SKILL_INVOCATION ? [index] : []
120
+ ));
121
+ if (executorSkillInvocationIndices.length > 0) {
122
+ const bodyLines = rest.slice(0, -2);
123
+ return executorSkillInvocationIndices.length === 1
124
+ && executorSkillInvocationIndices[0] === rest.length - 1
125
+ && lines.at(-1) === EXECUTOR_SKILL_INVOCATION
126
+ && lines.at(-2) === ""
127
+ && bodyLines.length > 0
128
+ && bodyLines[0].trim().length > 0
129
+ && bodyLines.at(-1).trim().length > 0
130
+ && hasTaskSpecificContent(bodyLines)
131
+ && !bodyLines.some((line) => HISTORICAL_EXECUTOR_SCAFFOLD_LINES.has(line));
132
+ }
133
+
134
+ const containsInlineScaffold = rest.some((line) => HISTORICAL_EXECUTOR_SCAFFOLD_LINES.has(line));
135
+ if (!containsInlineScaffold) return rest.join("\n").trim().length > 0;
136
+ return HISTORICAL_INLINE_PROTOCOLS.some((protocol) => {
137
+ const prefix = ["", ...protocol.flatMap((line) => [line, ""])];
138
+ if (!prefix.every((line, index) => rest[index] === line)) return false;
139
+ const bodyLines = rest.slice(prefix.length);
140
+ return hasTaskSpecificContent(bodyLines)
141
+ && !bodyLines.some((line) => HISTORICAL_EXECUTOR_SCAFFOLD_LINES.has(line));
142
+ });
143
+ };
144
+ const currentMatches = lines.flatMap((line, index) => {
145
+ const match = line.match(TASKCHEF_MARKER_PATTERN);
146
+ return match === null ? [] : [{ id: match[1], index }];
147
+ });
148
+ const legacyMatches = lines.flatMap((line, index) => {
149
+ const match = line.match(LEGACY_TASKCHEF_MARKER_PATTERN);
150
+ return match === null ? [] : [{ id: match[1], index }];
151
+ });
152
+ if (currentMatches.length + legacyMatches.length !== 1) return null;
153
+
154
+ if (legacyMatches.length === 1) {
155
+ const [{ id, index }] = legacyMatches;
156
+ if (index !== 0) return null;
157
+ return hasHistoricalAssignment() ? id : null;
158
+ }
159
+
160
+ const [{ id, index }] = currentMatches;
161
+ if (index === 0) {
162
+ return hasHistoricalAssignment() ? id : null;
163
+ }
164
+ const executorSkillReferences = instruction.match(/\$taskchef-executor\b/gi) ?? [];
165
+ const isTrailingScaffold = index === lines.length - 2
166
+ && index >= 2
167
+ && lines[0].trim().length > 0
168
+ && lines.at(-3) === ""
169
+ && lines.at(-4).trim().length > 0
170
+ && lines.at(-1) === EXECUTOR_SKILL_INVOCATION
171
+ && hasTaskSpecificContent(lines.slice(0, index - 1))
172
+ && !lines.slice(0, index - 1).some((line) => HISTORICAL_EXECUTOR_SCAFFOLD_LINES.has(line))
173
+ && executorSkillReferences.length === 1;
174
+ return isTrailingScaffold ? id : null;
92
175
  }
93
176
 
94
177
  export function prepareDelegation(instruction, { taskId = randomUUID() } = {}) {
95
- requireString(instruction, "instruction");
96
- if (parseTaskChefMarker(instruction) !== null) throw new Error("instruction already contains a TaskChef marker");
178
+ const rawBody = requireString(instruction, "instruction");
179
+ if (/^[^\S\r\n]*(?:\r\n|\r|\n)/.test(rawBody)) {
180
+ throw new Error("instruction must begin with useful task content on its first line");
181
+ }
182
+ const body = rawBody.replace(/(?:(?:\r\n|\r|\n)[^\S\r\n]*)+$/, "");
183
+ if (body.split(/\r\n|\r|\n/).some((line) => (
184
+ TASKCHEF_MARKER_PATTERN.test(line) || LEGACY_TASKCHEF_MARKER_PATTERN.test(line)
185
+ ))) {
186
+ throw new Error("instruction already contains a TaskChef marker");
187
+ }
188
+ if (/\$taskchef-executor\b/i.test(body)) {
189
+ throw new Error("instruction contains a reserved TaskChef executor skill reference");
190
+ }
191
+ const bodyLines = body.split(/\r\n|\r|\n/);
192
+ if (!hasTaskSpecificContent(bodyLines)) {
193
+ throw new Error("instruction must contain task-specific content, not only TaskChef lifecycle scaffolding");
194
+ }
195
+ if (bodyLines.some((line) => HISTORICAL_EXECUTOR_SCAFFOLD_LINES.has(line))) {
196
+ throw new Error("instruction contains reserved historical TaskChef lifecycle scaffolding");
197
+ }
97
198
  const id = requireUuid(taskId);
98
199
  return {
99
200
  id,
100
- instruction: `${taskChefMarker(id)}\n\n${EXECUTOR_OWNERSHIP_PARAGRAPH}\n\n${EXECUTOR_LINK_PARAGRAPH}\n\n${EXECUTOR_WORKING_PARAGRAPH}\n\n${EXECUTOR_RESULT_PARAGRAPH}\n\n${instruction}`,
201
+ instruction: `${body}\n\n${taskChefMarker(id)}\n${EXECUTOR_SKILL_INVOCATION}`,
101
202
  };
102
203
  }
103
204
 
package/src/mcp.js CHANGED
@@ -68,7 +68,7 @@ export function createTaskChefMcpServer({
68
68
  { name: "taskchef", version: "1.0.0" },
69
69
  {
70
70
  instructions:
71
- "Prepare with prepare_dispatch, call record_task before creating the Codex task, then create it natively and return immediately. The executor must call link_task first, report_state working at the start of each execution turn, and report_state with a semantic outcome before ending.",
71
+ "Prepare with prepare_dispatch, call record_task before creating the Codex task, then create it natively and return immediately. Follow the active TaskChef skill for role-specific sequencing of the identity and state tools.",
72
72
  },
73
73
  );
74
74
 
@@ -114,7 +114,7 @@ export function createTaskChefMcpServer({
114
114
  },
115
115
  async (input) => {
116
116
  if (parseTaskChefMarker(input.instruction) !== input.id) {
117
- throw new Error("record_task instruction must start with its exact TaskChef marker");
117
+ throw new Error("record_task instruction must contain its exact TaskChef marker in an accepted scaffold");
118
118
  }
119
119
  const task = await record(workspace, input);
120
120
  return toolResult("task", task, `Recorded TaskChef task ${task.id}.`);
package/src/workspace.js CHANGED
@@ -737,7 +737,7 @@ async function validateDispatchShape(dispatch, name = "task") {
737
737
  id,
738
738
  project,
739
739
  title: requireString(dispatch.title, `${name}.title`).trim(),
740
- instruction: requireString(dispatch.instruction, `${name}.instruction`).trim(),
740
+ instruction: requireString(dispatch.instruction, `${name}.instruction`),
741
741
  threadId: dispatch.threadId === null
742
742
  ? null
743
743
  : normalizeDurableThreadId(dispatch.threadId, `${name}.threadId`),