taskchef 5.11.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: taskchef-delegate
3
- description: "Dispatch actionable requests through the per-user TaskChef workspace into independently openable Codex project tasks. Use automatically for actionable work received in the canonical TaskChef dispatcher workspace. From any other project, use only when the user explicitly asks to delegate or split separate work into Codex tasks; TaskChef-related subject matter alone is not delegation intent. Record before creation, resolve provisional identity by exact marker within a strict bound, and never wait for executor completion."
3
+ description: "Dispatch actionable requests through the per-user TaskChef workspace into independently openable Codex project tasks. Use automatically for actionable work received in the canonical TaskChef dispatcher workspace. From any other project, use only when the user explicitly asks to delegate or split separate work into Codex tasks; TaskChef-related subject matter alone is not delegation intent. Record before creation, require executor self-linking, and never wait for executor completion."
4
4
  ---
5
5
 
6
6
  # TaskChef Delegate
@@ -12,154 +12,76 @@ and return immediately.
12
12
 
13
13
  A task whose initial structured `codexDelegation.input` starts with an exact
14
14
  `<!-- taskchef_id=<full UUID> -->` marker already owns that delegated
15
- assignment. Execute the assignment in the current task. Do not re-dispatch it
16
- merely because it concerns TaskChef or a configured project.
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.
17
18
 
18
- This does not prevent the task from using TaskChef later. Use this skill
19
- normally when the initial assignment explicitly asks to delegate separate
20
- work, or when the user later explicitly requests a new delegation.
21
-
22
- Use the bundled TaskChef `prepare_dispatch`, `record_task`, `resolve_task`, and `report_result`
23
- tools for deterministic workspace and task-record operations. Call them
24
- directly; never probe for them or fall back to shell CLI writes. If a required
25
- tool is unavailable, stop and report that the TaskChef plugin must be reloaded
26
- or reinstalled. Resolve the plugin root from this skill only when writing an
27
- explicitly requested benchmark artifact.
19
+ Use the bundled `prepare_dispatch`, `record_task`, `link_task`, and
20
+ `report_result` MCP tools directly. Never fall back to shell writes. If a
21
+ required tool is unavailable, stop and report that the TaskChef plugin must be
22
+ reloaded or installed.
28
23
 
29
24
  ## Boundaries
30
25
 
31
- - Keep implementation, tests, and reports in the TaskChef source repository.
32
- - Keep only `AGENTS.md`, `taskchef.json`, and `tasks.jsonl` in a dispatcher
26
+ - Keep only `AGENTS.md`, `taskchef.json`, and `tasks.jsonl` in the dispatcher
33
27
  workspace.
34
28
  - Use real Codex tasks, never collaboration or subagent tools.
35
- - Use only TaskChef's `UserPromptSubmit` hook: after identity is separately
36
- verified, it records the initial turn and provides read-only current-turn
37
- context on follow-up prompts. Never infer
38
- lifecycle state from hooks.
39
- - Never use schedules, daemons, background monitors, or indefinite polling.
40
- The only permitted thread discovery is the bounded exact-marker resolution
41
- after a provisional creation result.
42
- - Never wait for delegated work after executor creation.
43
- - Never collect transcripts or hidden reasoning.
29
+ - Never use hooks, schedules, daemons, background monitors, recent-task
30
+ searches, transcripts, hidden reasoning, or polling for identity.
31
+ - Never wait for delegated work after native creation.
32
+ - Treat executor-supplied identity as a cooperative assertion in TaskChef's
33
+ local single-user trust boundary, not transport-authenticated proof.
44
34
 
45
35
  ## Dispatch
46
36
 
47
37
  1. Split the request into the smallest independently useful outcomes. Include
48
38
  constraints, expected testing, and reporting in every instruction.
49
- 2. In parallel, list the native Codex projects once and call `prepare_dispatch`
50
- exactly once for each outcome. Every preparation produces a distinct task
51
- UUID and marker; never reuse either across executors. The preparation tool
52
- resolves the canonical workspace, loads and validates the configured routing targets,
53
- generates the lowercase full UUID task ID, and returns `preparedAt` plus the
54
- exact first-line marker. Use
55
- `$taskchef-bootstrap` if the workspace is missing or unhealthy.
56
- The tool resolves an absolute (or `~/`-prefixed) `TASKCHEF_WORKSPACE`, then
57
- `~/.agents/taskchef`; do not substitute the current project. Reject the
58
- dispatcher workspace itself as a target. Never take a pre-creation thread
59
- snapshot.
60
- 3. Classify against configured `name`, every URL in the `githubRepos` list, and
61
- `description`. Use `path` only as checkout identity. Managed `*-workspace`
62
- projects advertise their child or sub-repositories in this list.
63
- When the prompt contains a GitHub issue or pull-request URL, canonicalize
64
- its case-insensitive owner/repository identity, ignoring `http` versus
65
- `https`, an optional `www`, a trailing slash or `.git`, and the issue or PR
66
- suffix. Check that identity against every repository URL of every configured
67
- project. Route on this evidence only when exactly one configured project
68
- matches. Ask instead of guessing when no project or several projects match.
69
- 4. Resolve the selected configured path against the already-loaded native
70
- projects and require an exact match. Do not list native projects again.
71
- 5. For each outcome, use only its corresponding task ID, preparation time, and
72
- marker returned by its own preparation call.
73
- Prefix the complete executor instruction with
74
- exactly `<!-- taskchef_id=<full UUID> -->` as the first line, followed by a
75
- blank line, this executor-role paragraph, another blank line, the result
76
- paragraph below, another blank line, and the instruction body:
39
+ 2. In parallel, list native Codex projects once and call `prepare_dispatch`
40
+ exactly once per outcome. Never reuse a task ID or marker. Do not take a
41
+ pre-creation thread snapshot.
42
+ 3. Route against configured project `name`, `description`, and canonical
43
+ `githubRepos`; use `path` only as checkout identity. Require exactly one
44
+ match and an exact native-project path. Ask instead of guessing.
45
+ 4. Prefix each executor instruction with the preparation's exact marker as the
46
+ first line, a blank line, and these required paragraphs before the body:
77
47
 
78
48
  > 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.
79
49
 
80
- > 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.
81
-
82
- Preserve this
83
- marked instruction for recording, and note the creation time. The exact
84
- random marker is the sole correlation proof.
85
- 6. Before creating each executor, call `record_task` exactly once with `id`,
86
- `project`, `title`, the marked `instruction`, and `threadId: null`. This
87
- closes the hook race: the entry exists before the executor can submit its
88
- initial prompt.
89
- 7. Create one real Codex task using the exact configured project, a local
90
- environment on its executor host, the marked instruction, and a short title.
91
- 8. If creation returns a durable `threadId`, call `resolve_task` immediately.
92
- If creation returns only `clientThreadId`, `pendingWorktreeId`, or a `local:`
93
- ID, retain it only for the created-thread directive and run this one bounded
94
- identity-resolution sequence:
95
- - Check immediately, then at 10 and 30 seconds after creation. At each
96
- checkpoint, call the native recent-task listing once.
97
- - Restrict candidates to Codex tasks created no earlier than five seconds
98
- before `preparedAt`, on the returned host and selected project when those
99
- fields are available. Prefer the exact title only as an ordering hint.
100
- - Read each remaining bounded candidate at most once per checkpoint and
101
- accept only the single task whose structured initial
102
- `codexDelegation.input` begins with this outcome's exact marker.
103
- - Call `resolve_task` only for that one verified durable child ID. Never use
104
- `session_id`, a source/parent ID, title similarity, or a provisional ID as
105
- identity proof.
106
- - Stop at 30 seconds. Zero matches remain unresolved; multiple exact matches
107
- are an ambiguity and also remain unresolved. Never guess or continue
108
- polling.
109
-
110
- The initial hook may wait for this canonical record transition so it can
111
- inject the verified child ID and current turn ID. Codex documents that
112
- subagent hooks receive the parent `session_id`, so the hook must never write
113
- that field into `threadId`.
114
-
115
- 9. If executor creation fails, call `report_result` for the already-recorded
116
- task with `failed`, null thread/turn IDs, and a concise creation error.
117
- 10. Emit the appropriate created-thread directive and return after the bounded
118
- identity step.
119
- Treat a nullable record as preserved but not yet linked. Do not read an
120
- executor for progress and never wait for executor work completion.
121
-
122
- The bundled structured tools are the only dispatch-time path to the canonical
123
- workspace. They reuse the same exact-field validation, locking, atomic writes,
124
- and one-way nullable resolution as the CLI without shell parsing, stdin, or
125
- per-command filesystem escalation. The package exports repository routing,
126
- marker, and record-before-create orchestration helpers for deterministic tests.
127
- Keep the CLI for bootstrap, manual inspection, and recovery outside delegation.
128
-
129
- ## Later resolution
130
-
131
- The trusted initial hook never resolves identity from its `session_id`. It uses
132
- only the child ID already stored by immediate or bounded exact-marker
133
- resolution. For manual recovery, require one exact structured marker match,
134
- then call `resolve_task` once. Never edit `tasks.jsonl` directly. Resolution is
135
- an idempotent one-way transition from `threadId: null` to one unique durable
136
- child thread ID.
137
-
138
- ## Legacy benchmark compatibility
139
-
140
- The schema-v1 benchmark fixtures below describe the removed snapshot resolver
141
- and remain only for historical-result validation. Do not use that resolver for
142
- new delegation benchmarks. A future benchmark schema should measure record,
143
- creation, bounded exact-marker resolution, and result callback without
144
- indefinite polling. The
145
- legacy fixture starts from
146
- `<plugin-root>/assets/e2e-benchmark-example.json`. Capture one ISO start/end
147
- interval for the parallel preparation/project-list operation, followed by
148
- sequential non-overlapping creation, recording, and optional provisional
149
- resolution stages. Follow the complete schema and workflow transitions in
150
- `<plugin-root>/SPEC.md`; the example is the durable-success starting shape.
151
- Omit later stages when an earlier operation stops the workflow.
152
- Fallback snapshot observations additionally require `recentTaskCount`,
153
- `candidateCount`, `exactMatchCount`, and `resolveWriteMs`. For each snapshot,
154
- also record `resolveWriteOutcome` as `not-attempted`, `succeeded`, or `failed`;
155
- a unique match whose atomic write fails remains unresolved. After the bounded
156
- workflow ends, verify the canonical task record once and, when resolved, read
157
- the task once to verify the requested output. A failed verification remains a
158
- false validation flag even when the corresponding operation succeeded. Feed
159
- one exact JSON value to
160
- `node <plugin-root>/scripts/e2e-benchmark.js write <output-directory>` on closed,
161
- non-interactive stdin. Keep timestamped results in the TaskChef source
162
- repository's ignored `reports/e2e-benchmarks/` directory. Never include hidden
163
- reasoning or transcripts. Use the script's `clean` command before establishing
164
- a replacement baseline. Mark candidate filtering effective only when the
165
- fallback snapshot narrows reads below the complete recent-task window.
50
+ > 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.
51
+
52
+ > 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.
53
+
54
+ 5. Before creating each executor, call `record_task` exactly once with `id`,
55
+ `project`, `title`, the exact marked `instruction`, and `threadId: null`.
56
+ 6. Create one real Codex task using the exact configured project, an appropriate
57
+ native environment, the marked instruction, and a short title.
58
+ 7. Return immediately. Preserve a returned provisional client ID only for the
59
+ created-thread directive. Do not call `link_task` from the dispatcher even
60
+ when creation returns a durable ID; the child must self-link.
61
+ 8. If creation fails after recording, call `report_result` with `failed`, null
62
+ thread/turn IDs, and a bounded summary before returning the failure.
63
+
64
+ ## Executor contract
65
+
66
+ The executor must make `link_task(taskId, threadId)` its first TaskChef action.
67
+ It obtains its own durable ID from the current task's `CODEX_THREAD_ID`, never
68
+ from the delegation's `sourceThreadId`, `CODEX_SESSION_ID`, inherited session
69
+ metadata, title matching, or a parent task.
70
+ Identical retries are safe. A rejected link, unavailable tool, or interrupted
71
+ initial turn leaves the record visibly link-pending and retryable; the executor
72
+ must not guess or do substantive work first.
73
+
74
+ For every semantic result, the executor supplies its linked thread ID and the
75
+ current turn ID obtained by reading that exact thread. A follow-up must use the
76
+ new turn ID. Do not reuse the initial turn ID. `needs_input` is only for a real
77
+ user decision, not live approval UI.
78
+
79
+ The filesystem watcher surfaces `link_task` and `report_result` writes to the
80
+ dashboard. The linked child ID drives the exact Codex deep link.
81
+
82
+ ## Legacy recovery
83
+
84
+ `taskchef task resolve` exists only for unresolved records created before the
85
+ self-linking schema. Require an exact marker match and a unique durable child
86
+ ID. The command rejects new self-linking records. Never edit `tasks.jsonl`
87
+ directly.
@@ -47,11 +47,12 @@ all deterministic task-log operations.
47
47
  4. Treat `updatedBy: mcp`, `status: failed`, and null thread/turn IDs as a fresh
48
48
  executor-creation failure. No live read is possible or needed; report the
49
49
  stored failure summary, not unresolved. Otherwise, only a snapshot with
50
- `updatedBy: mcp` is a cached semantic result. A
51
- dispatcher- or hook-written `working` snapshot has no semantic callback and
52
- requires one live task query when selected; if the task is inactive and no
53
- callback exists, report the outcome as unknown rather than treating
54
- `working` as fresh. When identity is certain and metadata says the thread is
50
+ `updatedBy: mcp`, a result status, a non-null summary, and a non-null turn ID
51
+ is a cached semantic result. Any `working` snapshot has no semantic
52
+ callback, including a self-linked `updatedBy: mcp` snapshot, and requires
53
+ one live task query when selected; if the task is inactive and no callback
54
+ exists, report the outcome as unknown rather than treating `working` as
55
+ fresh. When identity is certain and metadata says the thread is
55
56
  inactive, trust the latest MCP result by default in a broad overview. Do not
56
57
  read every idle terminal task in an overview merely because native
57
58
  `updatedAt` is later: callbacks normally run before Codex finalizes the same
@@ -74,9 +75,12 @@ all deterministic task-log operations.
74
75
  completed, failed, unresolved, or unknown. Show the cached summary when it
75
76
  remains fresh. If a newer turn exists without a callback, describe the live
76
77
  state and label the cached result stale rather than overwriting it.
77
- 6. Never edit `tasks.jsonl` directly during reporting. The initial hook normally
78
- resolves null identity; manual recovery may call `task resolve` only after
79
- one exact structured marker match. Never persist inferred status,
78
+ 6. Never edit `tasks.jsonl` directly during reporting. The task API preserves
79
+ each record's persisted `schemaVersion`: a schema 4 null identity is executor
80
+ link-pending and must be retried by that executor, while a schema 1-3 null
81
+ identity is a legacy recovery candidate. Manual recovery may call
82
+ `task resolve` only for the latter after one exact structured marker match.
83
+ Never persist inferred status,
80
84
  transcripts, prose classifications, or hidden reasoning. Do not poll or wait.
81
85
 
82
86
  If the task history is empty, say that TaskChef has not recorded any tasks. If
package/src/cli.js CHANGED
@@ -464,12 +464,13 @@ Usage:
464
464
  taskchef project remove <name> [--json] [--workspace <path>]
465
465
  taskchef dispatch prepare [--json] [--workspace <path>]
466
466
  taskchef task record [--json] [--workspace <path>]
467
- taskchef task resolve <task-id> --thread-id <thread-id> [--json] [--workspace <path>]
467
+ taskchef task resolve <legacy-task-id> --thread-id <thread-id> [--json] [--workspace <path>]
468
468
  taskchef task show <task-id-or-8-character-prefix> [--json] [--workspace <path>]
469
469
  taskchef task list [--project <name-or-path>] [--ascending] [--full-id] [--json] [--workspace <path>]
470
470
  taskchef task summary [--json] [--workspace <path>]
471
471
 
472
472
  Task record reads one JSON value from closed, non-interactive standard input.
473
+ Task resolve is a legacy migration command and rejects self-linking task records.
473
474
  Task show accepts a full task ID or the exact 8-character ID printed by task list.
474
475
  Task show prints human-readable details by default; --json prints the complete task object.
475
476
  Project import reads a JSON
@@ -0,0 +1,17 @@
1
+ export async function openTaskFromControl(event, taskId, {
2
+ fetchAction = globalThis.fetch,
3
+ showMessage,
4
+ } = {}) {
5
+ event.stopPropagation();
6
+ const control = event.currentTarget;
7
+ control.disabled = true;
8
+ try {
9
+ const response = await fetchAction(`/api/tasks/${encodeURIComponent(taskId)}/open-codex`, {
10
+ method: "POST",
11
+ });
12
+ const result = await response.json();
13
+ showMessage(result.message);
14
+ } finally {
15
+ control.disabled = false;
16
+ }
17
+ }
@@ -7,6 +7,7 @@ import {
7
7
  taskStatusLabel,
8
8
  taskWithinDateFilter,
9
9
  } from "./state.js";
10
+ import { openTaskFromControl } from "./actions.js";
10
11
 
11
12
  const state = {
12
13
  tasks: [],
@@ -161,7 +162,18 @@ function taskCard(task) {
161
162
  const time = document.createElement("time");
162
163
  time.dateTime = task.meaningfulUpdatedAt ?? task.updatedAt ?? task.createdAt;
163
164
  time.textContent = `Updated ${formatTime(time.dateTime)}`;
164
- article.append(heading, project, summary, time);
165
+ const footer = document.createElement("div");
166
+ footer.className = "task-footer";
167
+ const openTask = document.createElement("button");
168
+ openTask.type = "button";
169
+ openTask.className = "secondary-button task-open";
170
+ openTask.textContent = "Open task";
171
+ openTask.setAttribute("aria-label", `Open ${task.title} in Codex`);
172
+ openTask.addEventListener("click", (event) => openTaskFromControl(event, task.id, {
173
+ showMessage,
174
+ }));
175
+ footer.append(time, openTask);
176
+ article.append(heading, project, summary, footer);
165
177
  return article;
166
178
  }
167
179
 
@@ -251,16 +263,7 @@ elements.copyThreadId.addEventListener("click", async () => {
251
263
  showMessage("Clipboard access is unavailable. Copy the thread ID from the metadata below.");
252
264
  }
253
265
  });
254
- elements.openProject.addEventListener("click", async () => {
266
+ elements.openProject.addEventListener("click", async (event) => {
255
267
  if (!state.selectedTask) return;
256
- elements.openProject.disabled = true;
257
- try {
258
- const response = await fetch(`/api/tasks/${encodeURIComponent(state.selectedTask.id)}/open-codex`, {
259
- method: "POST",
260
- });
261
- const result = await response.json();
262
- showMessage(result.message);
263
- } finally {
264
- elements.openProject.disabled = false;
265
- }
268
+ await openTaskFromControl(event, state.selectedTask.id, { showMessage });
266
269
  });
@@ -12,7 +12,7 @@
12
12
  <header class="site-header">
13
13
  <div>
14
14
  <p class="eyebrow">TaskChef</p>
15
- <h1>Task dashboard</h1>
15
+ <h1>TaskChef dashboard</h1>
16
16
  <p class="subtitle">Latest delegated work, updated as TaskChef reports change.</p>
17
17
  </div>
18
18
  <div class="connection" role="status" aria-live="polite">
@@ -74,6 +74,8 @@ select { min-width: 180px; padding: 9px 34px 9px 11px; border: 1px solid var(--b
74
74
  .task-project { margin: 3px 0 12px; font-size: 0.86rem; }
75
75
  .task-summary { max-width: 78ch; margin-bottom: 14px; }
76
76
  time { font-size: 0.78rem; }
77
+ .task-footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
78
+ .task-open { flex: none; }
77
79
 
78
80
  .status { flex: none; padding: 4px 9px; border-radius: 999px; background: var(--surface-muted); color: var(--muted); font-size: 0.72rem; font-weight: 750; text-transform: capitalize; }
79
81
  .status-completed { background: var(--accent-soft); color: var(--accent); }
@@ -122,6 +124,8 @@ pre { max-height: 280px; margin: 0; padding: 14px; overflow: auto; border-radius
122
124
  .toolbar label, .toolbar select { width: 100%; }
123
125
  .task-count { margin: 0; }
124
126
  .task-heading { align-items: flex-start; }
127
+ .task-footer { align-items: flex-start; flex-direction: column; }
128
+ .task-open { width: 100%; }
125
129
  .dialog-actions { align-items: stretch; flex-direction: column; }
126
130
  .metadata { grid-template-columns: 1fr; }
127
131
  .metadata dt { padding-bottom: 0; border-bottom: 0; }
package/src/dashboard.js CHANGED
@@ -37,6 +37,7 @@ const CONTENT_SECURITY_POLICY = [
37
37
 
38
38
  const STATIC_FILES = new Map([
39
39
  ["/", ["index.html", "text/html; charset=utf-8"]],
40
+ ["/actions.js", ["actions.js", "text/javascript; charset=utf-8"]],
40
41
  ["/app.js", ["app.js", "text/javascript; charset=utf-8"]],
41
42
  ["/state.js", ["state.js", "text/javascript; charset=utf-8"]],
42
43
  ["/styles.css", ["styles.css", "text/css; charset=utf-8"]],