taskchef 7.22.4 → 7.23.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.22.4",
3
+ "version": "7.23.0",
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
@@ -16,6 +16,7 @@ request -> recorded TaskChef task -> Codex executor -> request/result turn timel
16
16
  | --- | --- |
17
17
  | Install, configure, dispatch, inspect, and recover | This README |
18
18
  | Follow the normative agent contract and MCP interfaces | [Specification](docs/spec.md) |
19
+ | Understand dashboard start, upgrade, and shutdown | [Dashboard lifecycle](docs/dashboard-lifecycle.md) |
19
20
  | Understand implementation flows and trust boundaries | [Workflows](docs/workflows.md) |
20
21
  | Compare TaskChef with FirstMate | [FirstMate comparison research](docs/firstmate-taskchef-comparison.md) |
21
22
  | Review deferred ideas | [Backlog](BACKLOG.md) |
@@ -61,6 +62,8 @@ AGENTS.md managed dispatcher instructions plus user additions
61
62
  taskchef.json schema-2 Codex project index, dashboard preference, and delegation metadata
62
63
  tasks.jsonl one task snapshot per line (schema 10; schema 4-9 migration supported)
63
64
  .taskchef-usage.json optional mode-0600 ccusage snapshot and turn-boundary cache
65
+ .taskchef-dashboard-owner.json optional mode-0600 current dashboard identity and control credential; retained and atomically replaced
66
+ .taskchef-dashboard-handoff.json optional mode-0600 secret-free signed final lease snapshot; retained and atomically overwritten
64
67
  ```
65
68
 
66
69
  Index or inspect Codex projects conversationally:
@@ -238,28 +241,36 @@ $taskchef-dashboard Ensure and open the TaskChef dashboard.
238
241
  ```
239
242
 
240
243
  MCP activation and `$taskchef-dashboard` call the input-free `ensure_dashboard`
241
- MCP tool. It starts at
242
- most one dashboard inside the existing TaskChef MCP process on
243
- `127.0.0.1:3210`, or reuses a listener only when its bounded `/api/health`
244
- identity proves the exact TaskChef/dashboard-server version and the same
245
- canonical workspace. The response says `started` or `reused` and includes the
246
- stable URL, canonical workspace, and versions.
247
-
248
- The in-process dashboard closes on MCP transport close, stdin EOF, SIGINT,
249
- SIGTERM, or detected loss of the MCP process's original parent. Activating a
250
- newer TaskChef MCP can also retire an older compatible MCP-owned dashboard for
251
- the same canonical workspace through an authenticated loopback handoff. These
252
- guards reduce unnecessary orphans but do not claim that Codex supplies a
253
- restart or child-process cleanup guarantee. TaskChef adds no daemon or other
254
- OS-persistent component, and dashboard availability remains best-effort while
255
- Codex is closed.
256
-
257
- Authenticated handoff requires the older listener to use the same dashboard
258
- protocol and to have written TaskChef's private mode-0600 ownership record in
259
- that canonical workspace. A nonce-bound HMAC challenge proves the listener has
260
- the matching secret before TaskChef sends a separately authenticated graceful
261
- shutdown request. The secret is never returned by `/api/health`, sent in a
262
- request, or logged. Standalone, different-workspace, newer, malformed,
244
+ MCP tool. It starts at most one loopback dashboard scoped to the running Codex
245
+ session on `127.0.0.1:3210`, or reuses a listener only when its bounded
246
+ `/api/health` identity proves the exact TaskChef/dashboard-server version,
247
+ canonical workspace, and `session` launcher. The response says `started` or
248
+ `reused` and includes the stable URL, canonical workspace, and versions.
249
+
250
+ The dashboard is launched by MCP but is not hosted by one MCP transport. It
251
+ therefore survives an individual MCP close, stdin EOF, signal, or plugin reload.
252
+ Each MCP securely registers its original Codex parent PID; after every
253
+ registered Codex session PID disappears for a grace period, the dashboard
254
+ closes and exits. Activating an installed newer TaskChef MCP authenticates and
255
+ retires a compatible older `mcp`- or `session`-owned dashboard for the same
256
+ canonical workspace, then starts the installed version. These are best-effort
257
+ local guards, not a claim that Codex supplies a restart guarantee. TaskChef adds
258
+ no daemon or OS-persistent component. See the complete
259
+ [dashboard lifecycle](docs/dashboard-lifecycle.md).
260
+
261
+ Authenticated handoff requires the older listener to have written TaskChef's
262
+ private mode-0600 ownership record in that canonical workspace. A nonce-bound
263
+ HMAC challenge proves the listener has the matching secret before TaskChef
264
+ sends an authenticated two-phase handoff. A session listener prepares by
265
+ fencing ordinary joins, adding the activating Codex PID, and returning a signed
266
+ bounded lease snapshot while remaining live. A separate authenticated commit
267
+ allows a short concurrent-activator grace, returns the signed final immutable
268
+ lease set, and then schedules graceful shutdown after a bounded response-retry
269
+ window. A mode-`0600`, secret-free, signed final-snapshot record permits exact
270
+ lease recovery if every commit response is lost; a prior MCP-owned listener
271
+ receives a separately authenticated graceful shutdown request. The secret is never
272
+ returned by `/api/health`, sent in a request, or logged. Standalone,
273
+ different-workspace, newer, malformed,
263
274
  unverified, and spoofed listeners are left untouched. Versions predating this
264
275
  protocol cannot be taken over safely and may require one final manual cleanup.
265
276
  The ownership record is durable metadata: shutdown retains it, and the next
@@ -316,10 +327,10 @@ app-callable archive interface or guarantees CLI compatibility. The dormant
316
327
  server path also rejects requests before discovering or invoking the CLI.
317
328
 
318
329
  Task details also show whole-task and per-turn token usage when `ccusage` can
319
- map the linked Codex thread. A completed turn briefly shows “Tokens:
320
- calculating…” while TaskChef performs bounded deferred reconciliation, because
330
+ map the linked Codex thread. A completed turn briefly shows “Calculating token
331
+ usage…” while TaskChef performs bounded deferred reconciliation, because
321
332
  the terminal lifecycle callback precedes Codex's final output write. Historical
322
- tasks may show a trustworthy task total while older turns remain “Tokens
333
+ tasks may show a trustworthy task total while older turns remain “Token usage
323
334
  unavailable” when no cumulative turn boundaries were recorded. Input, cached
324
335
  input, output, reasoning, and total counts retain ccusage's categories. Dollar
325
336
  figures are labeled API-equivalent estimates; provenance identifies the online
@@ -330,9 +341,10 @@ pricing modes. ccusage 20.0.20 may omit GPT-5.6 cache-write charges, so TaskChef
330
341
  labels that analyzer limitation instead of presenting the estimate as complete.
331
342
  The header shows the running TaskChef package version reported by the same
332
343
  bounded health identity used for compatible-listener checks.
333
- The canonical port is owned by a dashboard initialized in the TaskChef MCP host
334
- before its tool transport connects. Health identity records an `mcp` launcher,
335
- and MCP recovery reuses only another exact-compatible MCP-launched dashboard;
344
+ The canonical port is owned by a dashboard session process initialized by the
345
+ TaskChef MCP before its tool transport connects. Health identity records a
346
+ `session` launcher, and MCP recovery reuses only another exact-compatible,
347
+ authenticated session dashboard;
336
348
  a foreground `taskchef dashboard` process is intentionally standalone so its
337
349
  child commands cannot silently inherit an agent-shell sandbox.
338
350
  Task and result times are relative through 29 days (with minute detail for the
@@ -419,14 +431,14 @@ does not necessarily reload Codex. Then run `$taskchef-dashboard` (or call
419
431
 
420
432
  - the expected released TaskChef version;
421
433
  - the expected dashboard protocol `serverVersion`;
422
- - the `mcp` dashboard launcher;
434
+ - the `session` dashboard launcher;
423
435
  - the canonical TaskChef workspace path;
424
436
  - the canonical `http://127.0.0.1:3210/` URL.
425
437
 
426
438
  The release-install sequence is therefore: install plugin, activate or reload
427
439
  the new MCP process, ensure the dashboard, then verify TaskChef version,
428
- protocol `serverVersion`, `mcp` launcher, canonical workspace, and URL.
429
- Exact-compatible MCP servers may be reused; standalone and unknown listeners
440
+ protocol `serverVersion`, `session` launcher, canonical workspace, and URL.
441
+ Exact-compatible session servers may be reused; standalone and unknown listeners
430
442
  remain untouched.
431
443
 
432
444
  If autostart reports a **verified older TaskChef** listener, the port occupant
@@ -0,0 +1,177 @@
1
+ # Dashboard lifecycle
2
+
3
+ This document defines how the canonical TaskChef dashboard starts, survives MCP
4
+ reloads, changes version, and stops. The [specification](spec.md) is normative;
5
+ the [README](../README.md) covers user operation, and
6
+ [workflows](workflows.md) maps these guarantees to implementation.
7
+
8
+ ## Ownership model
9
+
10
+ The canonical dashboard is a loopback-only process scoped to a running Codex
11
+ application session. It is launched and maintained by TaskChef MCP activation,
12
+ but it is not hosted by one MCP transport. This distinction lets the dashboard
13
+ survive an individual MCP EOF, signal, transport close, or plugin reload while
14
+ other TaskChef MCP transports in the same Codex session continue using it.
15
+
16
+ TaskChef does not install a daemon, login item, lifecycle hook, or privileged
17
+ service. A foreground `taskchef dashboard` process remains a separate
18
+ `standalone` launcher and is never adopted as the canonical session dashboard.
19
+
20
+ The dashboard was originally moved into the MCP runtime so the experimental
21
+ chat-archive action could inherit the MCP host environment. That action is now
22
+ disabled because Codex does not expose a reliable supported app-callable
23
+ archive interface. The dashboard still benefits from MCP activation,
24
+ workspace resolution, and startup isolation, but none of those requires the
25
+ HTTP listener to share one MCP transport's lifetime. The session process keeps
26
+ those benefits without retaining the obsolete per-transport ownership.
27
+
28
+ ## Token usage presentation
29
+
30
+ The detail view distinguishes an active turn whose final usage is pending from
31
+ a terminal turn whose usage is being calculated. Available usage keeps both
32
+ the token total and API-equivalent USD estimate; an available cumulative value
33
+ shown during a newer active turn is labeled as known so far.
34
+
35
+ ![Pending token usage in the task detail view](images/dashboard-token-pending.jpg)
36
+
37
+ ## Start and reuse
38
+
39
+ Unless `dashboard.autostart` is explicitly `false`, MCP activation runs the
40
+ same serialized ensure operation exposed by `ensure_dashboard` before the MCP
41
+ transport connects. An explicit ensure remains available when autostart is
42
+ disabled. Failure is isolated from MCP tool startup and emits only a bounded,
43
+ non-sensitive diagnostic.
44
+
45
+ The canonical session manager requires an explicit nonzero port and normally
46
+ binds `127.0.0.1:3210`; ephemeral port zero remains available only to direct
47
+ foreground/server callers that own the returned listener. Reuse requires an
48
+ exact bounded health identity:
49
+
50
+ - TaskChef dashboard service and health schema;
51
+ - installed TaskChef version;
52
+ - dashboard protocol version;
53
+ - canonical workspace;
54
+ - `session` launcher.
55
+
56
+ The MCP then authenticates the listener with the private ownership credential
57
+ and registers its original Codex parent PID as a session lease. Registration
58
+ success and transferred lease sets carry response HMACs bound to their fresh
59
+ request nonce and complete result, so a different process appearing between
60
+ the challenge and control request cannot be adopted. Concurrent starts
61
+ serialize locally and converge across processes by authenticating the single
62
+ listener that wins the port race.
63
+
64
+ A detached child reports readiness over a private inherited IPC channel only
65
+ after the listener is bound and the new owner record is durably published.
66
+ Startup errors use the same channel. A bounded timeout sends a cooperative
67
+ cancellation message, so slow or failed initialization cannot be mistaken for
68
+ success or silently strand a late or partially constructed listener. Owner
69
+ publication checks cancellation before atomic replacement; while publication
70
+ is in flight, every close path retains the port until publication settles, so
71
+ an older credential writer cannot race and overwrite a succeeding session's
72
+ owner record.
73
+
74
+ ## MCP reload and plugin upgrade
75
+
76
+ Closing one MCP transport does not close the session dashboard. EOF, SIGINT,
77
+ SIGTERM, protocol close, failed transport startup, and detected MCP-parent loss
78
+ still close that MCP process promptly, but the independently hosted dashboard
79
+ remains while a registered Codex session PID is alive.
80
+
81
+ After a plugin upgrade, installing files alone cannot execute new code. When
82
+ the newly installed MCP activates, it compares the live identity with its own
83
+ version. An exact current listener is reused. A verified older TaskChef
84
+ `mcp`- or `session`-launched listener for the same canonical workspace receives
85
+ an authenticated graceful handoff request. A session listener prepares an
86
+ idempotent handoff by fencing new registrations, adding the activating Codex
87
+ PID, and snapshotting its bounded live leases while it remains running. Only
88
+ after the MCP receives and verifies that signed result does it send a separate
89
+ authenticated commit that schedules graceful shutdown. A lost prepare response
90
+ is safely retried; an abandoned preparation expires and reopens registration.
91
+ A commit begins a short bounded finalization grace during which concurrent
92
+ authenticated activators can join. The commit response then returns the final
93
+ immutable lease snapshot with a credential-bound proof. The listener remains
94
+ available for a bounded commit-response retry window before graceful shutdown,
95
+ so a surviving activator can replace an elected peer that exits before launch
96
+ without discarding the older live sessions. The replacement starts with the
97
+ verified final leases. A full lease set refuses a distinct activating PID
98
+ without stopping the old listener. Finalization also reserves one of the 64
99
+ bounded lease slots for a distinct recovery activator; a 64-lease snapshot
100
+ cannot commit retirement and leaves the old listener running. The new MCP then
101
+ starts the current `session` version. A newer listener is never downgraded,
102
+ including during the brief old-to-new listener gap. If different newer versions
103
+ race, a higher contender authenticates and replaces a lower winner until the
104
+ highest active version remains.
105
+
106
+ Before acknowledging commit, the retiring session atomically records that
107
+ signed final snapshot in the mode-`0600` workspace file
108
+ `.taskchef-dashboard-handoff.json`. The record
109
+ contains no control secret. It exists only so an activator that loses every
110
+ commit response can authenticate and recover the exact finalized leases after
111
+ the old listener closes. The single record is retained and atomically
112
+ overwritten by the next finalized handoff; it cannot authenticate against a
113
+ new owner identity and secret. Avoiding post-publication deletion prevents one
114
+ replacement from removing a newer handoff generation. Invalid, permissively
115
+ readable, foreign-workspace, or incorrectly signed records are ignored and
116
+ never authorize takeover.
117
+
118
+ Both private metadata writers sync the completed file, atomically rename it,
119
+ and sync the canonical workspace directory before reporting publication. Final
120
+ lease joins are fenced before the immutable snapshot and durable write begin.
121
+
122
+ Versions predating authenticated ownership cannot be retired safely. Such a
123
+ legacy listener may require one final manual cleanup. This limitation is
124
+ intentional; TaskChef does not weaken listener safety to automate migration.
125
+
126
+ ## Codex session end
127
+
128
+ Each MCP registers the exact PID of the Codex process that launched it. The
129
+ dashboard checks only those registered PIDs with a non-signalling existence
130
+ probe. When none remains alive for the configured grace period, the dashboard
131
+ gracefully closes its HTTP listener and exits. Registering another live Codex
132
+ session during the grace period cancels expiry.
133
+
134
+ This is a best-effort local lifecycle guard, not a claim that Codex documents
135
+ or guarantees restart cleanup. PID existence cannot prove application identity
136
+ after an unlikely PID reuse, and abrupt operating-system termination can always
137
+ interrupt graceful cleanup. A later MCP activation still performs authenticated
138
+ version handoff, which is the recovery boundary for a recognized stale
139
+ TaskChef listener.
140
+
141
+ ## Security boundary
142
+
143
+ TaskChef writes ownership to `.taskchef-dashboard-owner.json` and finalized
144
+ handoff metadata to `.taskchef-dashboard-handoff.json` as private regular
145
+ mode-0600 files in the canonical workspace. The credential never appears in health responses,
146
+ diagnostics, URLs, or logs. A fresh nonce-bound HMAC challenge proves that the
147
+ listener and owner record share the credential. Shutdown and session
148
+ registration use separate action-bound proofs; successful registration and
149
+ handoff responses are also nonce- and result-bound. Nonces are single-use within
150
+ a bounded five-minute replay window. The replay cache has a fixed capacity and
151
+ refuses overflow while entries remain valid.
152
+
153
+ TaskChef sends control requests only after all public identity and private
154
+ ownership fields match. It never discovers or kills a port owner, sends OS
155
+ signals to a listener, uses `kill -9`, searches broadly for processes, or
156
+ requires elevated permission. These listeners remain untouched:
157
+
158
+ - standalone dashboards;
159
+ - unknown or spoofed services;
160
+ - different canonical workspaces;
161
+ - newer TaskChef versions;
162
+ - malformed or mismatched ownership records;
163
+ - listeners that fail the authenticated challenge;
164
+ - uncooperative legacy listeners without the handoff protocol.
165
+
166
+ ## Lifecycle matrix
167
+
168
+ | Event | MCP transport | Canonical dashboard |
169
+ | --- | --- | --- |
170
+ | MCP activation, no listener | Starts | Starts current session version |
171
+ | MCP activation, exact listener | Starts | Authenticated reuse and lease registration |
172
+ | One MCP EOF, signal, or reload | Closes | Remains while a Codex session PID is alive |
173
+ | Plugin upgrade activates newer MCP | Starts current version | Verified older version retires; current version starts |
174
+ | All registered Codex session PIDs disappear | May already be closed | Graceful close after the grace period |
175
+ | `dashboard.autostart: false` | Starts normally | No activation-time ensure; explicit ensure still works |
176
+ | Foreground `taskchef dashboard` | Unrelated | Standalone process follows its foreground CLI lifetime |
177
+ | Unknown or unverified port occupant | Starts normally | Occupant is untouched; ensure reports a bounded conflict |
package/docs/spec.md CHANGED
@@ -57,9 +57,15 @@ is dated research, not contract.
57
57
 
58
58
  ## Workspace contract
59
59
 
60
- TaskChef MUST manage only `AGENTS.md`, `taskchef.json`, `tasks.jsonl`, and the
61
- optional `.taskchef-usage.json` cache
62
- inside the dispatcher workspace. It MUST preserve unrelated paths.
60
+ TaskChef MUST manage only `AGENTS.md`, `taskchef.json`, `tasks.jsonl`, the
61
+ optional `.taskchef-usage.json` cache, and the optional dashboard lifecycle
62
+ records `.taskchef-dashboard-owner.json` and
63
+ `.taskchef-dashboard-handoff.json` inside the dispatcher workspace. It MUST
64
+ preserve unrelated paths. The owner record MUST be a retained mode-`0600`
65
+ current-listener identity and control credential that is atomically replaced by
66
+ a new owner. The handoff record MUST be a retained mode-`0600`, secret-free,
67
+ credential-signed final lease snapshot that is atomically overwritten by the
68
+ next finalized handoff.
63
69
 
64
70
  `taskchef.json` MUST have schema version 2, the following required fields, and
65
71
  an optional exact `dashboard` object:
@@ -191,8 +197,9 @@ port conflicts, and dashboard errors MUST NOT prevent tool registration or MCP
191
197
  availability. They MUST emit only a bounded non-sensitive diagnostic through
192
198
  the MCP process logging channel. MCP initialization MUST NOT open a browser.
193
199
  If transport connection fails after dashboard startup, the MCP server MUST
194
- best-effort close both its owned dashboard and partially attached transport,
195
- then propagate the original connection failure even when cleanup also fails.
200
+ best-effort close its manager and partially attached transport, then propagate
201
+ the original connection failure even when cleanup also fails. Closing that
202
+ individual MCP MUST NOT directly close a valid Codex-session dashboard.
196
203
 
197
204
  At the start of every dispatcher turn, the dispatcher SHOULD call
198
205
  `ensure_dashboard` best-effort. Failure MUST NOT block direct TaskChef answers,
@@ -282,12 +289,11 @@ object. Validation, marker, identity, uniqueness, freshness, or filesystem
282
289
  failures are surfaced as tool errors and MUST NOT partially mutate the log.
283
290
 
284
291
  ### `ensure_dashboard`
285
-
286
- **Caller:** dispatcher. **Mutation:** starts at most one in-process loopback
287
- HTTP server. The first owned start writes a private dashboard ownership record
288
- in the canonical workspace. The record remains after shutdown and the next
289
- owner atomically replaces it before reporting startup success.
290
- Exact-compatible reuse does not write it.
292
+ **Caller:** dispatcher. **Mutation:** starts or reuses the authenticated
293
+ Codex-session-scoped loopback dashboard. The first start writes a private
294
+ dashboard ownership record in the canonical workspace. The record remains
295
+ after shutdown and the next owner atomically replaces it before reporting
296
+ startup success.
291
297
 
292
298
  **Input:** empty object.
293
299
 
@@ -296,7 +302,7 @@ Exact-compatible reuse does not write it.
296
302
  ```text
297
303
  { dashboard: {
298
304
  action: "started" | "reused",
299
- launcher: "mcp",
305
+ launcher: "session",
300
306
  url: "http://127.0.0.1:3210/",
301
307
  workspace: string,
302
308
  taskchefVersion: string,
@@ -304,51 +310,98 @@ Exact-compatible reuse does not write it.
304
310
  } }
305
311
  ```
306
312
 
307
- MCP initialization MUST complete its best-effort dashboard start before exposing
308
- the MCP transport. Calls MUST serialize within one MCP process. The first call
309
- starts an owned dashboard or reuses an exact compatible MCP-launched listener;
310
- later and concurrent calls are idempotent and report reuse after the single start. The stable default MUST
311
- bind only to `127.0.0.1:3210` and MUST NOT accept a model-supplied workspace,
312
- host, or port.
313
-
314
- Before reuse, TaskChef MUST query a bounded loopback identity endpoint and
315
- require the exact fixed service/schema, TaskChef version, dashboard-server
316
- version, canonical workspace, and `mcp` launcher identity. Exact-compatible
317
- reuse MUST remain non-destructive and MUST NOT require ownership metadata. A
318
- manager that reuses another same-version MCP listener MUST monitor that listener
319
- for the remainder of its own lifetime and perform ordinary singleton startup if
320
- the reused owner exits, so overlapping plugin reloads converge on the surviving
321
- MCP process without signaling or terminating either process. A temporary
322
- different or unknown occupant remains untouched and does not disable that
323
- non-destructive availability monitor.
324
-
325
- An older TaskChef version MAY be retired only when all of these facts hold: its
326
- health identity is exact and reports the same canonical workspace, `mcp`
327
- launcher, and dashboard-server version; its semantic version is older than the
328
- requesting TaskChef version; a private regular mode-0600 owner record in that
329
- same workspace exactly matches the listener; and a fresh nonce-bound HMAC
330
- challenge proves control of that record's secret. Only after that proof MAY the
331
- new process send a separately nonce-bound authenticated loopback shutdown
332
- request. Shutdown nonces MUST be single-use. The secret MUST NOT appear in
333
- health responses, requests, diagnostics, or logs. Shutdown MUST retain the
334
- durable owner record so retirement cannot expose a credentialless live-listener
335
- window or delete a replacement's record. An owner accepting handoff MUST retire its in-memory dashboard manager
336
- so later tool calls cannot restart the older listener. Concurrent handoffs MUST
337
- converge on one listener and exact-compatible reuse.
338
-
339
- A standalone, unknown, malformed, different-workspace, newer, incompatible,
340
- unproven, spoofed, or legacy listener without valid ownership metadata MUST
341
- produce a concise actionable conflict and MUST NOT receive a shutdown request,
342
- signal, or process-level termination attempt. TaskChef MUST NOT discover or
343
- kill port owners. A startup or owner-record-write failure MUST leave no owned
344
- listener. The MCP server MUST close its owned dashboard on explicit MCP close,
345
- transport close, stdin EOF, SIGINT, SIGTERM, or detected replacement of its
346
- original parent process. Parent-loss detection is a local best-effort guard,
347
- not a Codex restart guarantee. Signal and parent-loss shutdown MUST bound the
348
- wait for an in-flight transport startup before closing the MCP server, so a
349
- never-settling startup cannot retain the dashboard indefinitely. This keeps dashboard child operations in the MCP
350
- host environment and prevents an agent-shell or foreground CLI dashboard from
351
- being mistaken for the canonical MCP-owned runtime.
313
+ MCP initialization MUST complete its best-effort ensure before exposing the
314
+ MCP transport. Calls MUST serialize within one MCP process. The first call
315
+ starts a dashboard session process or reuses the exact current listener; later
316
+ and concurrent calls are idempotent. The stable default MUST bind only to
317
+ `127.0.0.1:3210` and MUST NOT accept a model-supplied workspace, host, port,
318
+ session PID, or credential.
319
+
320
+ Exact reuse MUST require the fixed service/schema, TaskChef version,
321
+ dashboard-server version, canonical workspace, and `session` launcher identity.
322
+ It MUST additionally prove the private owner credential and use a fresh,
323
+ single-use, action-bound HMAC to register the MCP's original Codex parent PID.
324
+ The registration acknowledgement and every transferred lease set MUST carry a
325
+ separate response HMAC bound to the request nonce and complete accepted result.
326
+ The independent dashboard MUST survive closure of an individual MCP transport.
327
+ The session manager MUST require an explicit nonzero port because a detached
328
+ child cannot safely return an ephemeral bound port without an additional IPC
329
+ ownership channel. Direct foreground server callers MAY continue to bind port
330
+ zero when they retain the returned listener.
331
+
332
+ The dashboard MUST track only explicitly registered PIDs using non-signalling
333
+ existence probes. Once every registered PID is absent for the grace period, it
334
+ MUST gracefully close its HTTP listener and exit. A new authenticated live
335
+ registration during the grace period MUST cancel expiry. This is a local
336
+ best-effort session guard, not a Codex restart guarantee.
337
+
338
+ An older TaskChef version MAY be retired only when its exact health identity
339
+ reports the same canonical workspace and an `mcp` or `session` launcher; a
340
+ private regular mode-0600 owner record exactly matches that listener; and a
341
+ fresh nonce-bound HMAC challenge proves control of the record's secret. Only
342
+ then MAY the new MCP send an action- and nonce-bound authenticated loopback
343
+ handoff. Before retiring a prior `session` listener, an idempotent prepare
344
+ action MUST fence new registrations, register the activating Codex PID,
345
+ validate and return the bounded live PID lease set while the old listener
346
+ remains running. Only a separate authenticated commit after the caller verifies
347
+ that result MAY schedule shutdown. A lost prepare response MUST be retryable,
348
+ and an abandoned preparation MUST expire without shutting down the listener. A
349
+ full, failed, or malformed transfer MUST leave the
350
+ listener running and MUST NOT acknowledge a concurrent registration that will
351
+ be absent from the returned set. A prior `mcp` listener MAY instead receive a
352
+ separately authenticated graceful shutdown request.
353
+ A commit MUST begin a short bounded finalization grace that allows concurrent
354
+ authenticated activators to join. Its response MUST contain a credential-bound
355
+ proof of the final immutable complete lease snapshot. The listener MUST remain
356
+ available for a bounded commit-response retry window before shutdown, so a
357
+ surviving activator can recover if the elected one exits before launch. An
358
+ activator observing the authenticated old-owner listener gap MUST wait for the
359
+ elected replacement and MUST NOT bind an older version into that gap.
360
+ The finalized transferable snapshot MUST reserve one bounded lease slot for a
361
+ distinct recovery activator. If that complete union cannot fit, commit MUST fail
362
+ without stopping the old listener. When different compatible newer versions
363
+ race, a higher version that loses the bind MUST authenticate and replace the
364
+ lower winner rather than accepting it as final.
365
+ Before acknowledging commit, the retiring session MUST atomically persist the
366
+ credential-bound final snapshot in a private mode-`0600` same-workspace record
367
+ that contains no secret. A replacement MAY use that record only after verifying
368
+ its exact workspace, listener identity, handoff identifier when known, bounded
369
+ PID set, and HMAC with the private owner credential. It MUST ignore the record
370
+ once replacement ownership is published, but MUST NOT unlink it after that
371
+ transition because a newer handoff may already have atomically replaced it. The
372
+ single retained record MUST be overwritten only by a newer finalized handoff
373
+ and MUST remain unusable against a different owner identity or credential.
374
+ Owner and handoff publication MUST sync the completed private file, atomically
375
+ rename it, and sync the containing canonical workspace directory before
376
+ reporting success. Finalization MUST fence further handoff joins before taking
377
+ and persisting the immutable snapshot.
378
+ The new MCP MAY then start its current `session` version and MUST NOT downgrade
379
+ a newer listener. Concurrent startup and handoff MUST converge on one
380
+ authenticated listener. Single-use control nonces MUST be retained in a fixed,
381
+ time-bounded replay cache that refuses overflow while entries remain valid.
382
+
383
+ The credential MUST NOT appear in health responses, requests, diagnostics, or
384
+ logs. A standalone, unknown, malformed, different-workspace, newer, unproven,
385
+ spoofed, or legacy listener without valid ownership metadata MUST produce a
386
+ concise actionable conflict and MUST NOT receive a shutdown request, signal,
387
+ or process-level termination attempt. TaskChef MUST NOT discover or kill port
388
+ owners. An invalid workspace, initial task log, or ownership write MUST leave
389
+ no newly owned listener.
390
+
391
+ Detached session startup MUST use a private child readiness acknowledgement
392
+ sent only after listener bind and atomic ownership publication. Startup errors
393
+ MUST propagate through that channel. A bounded readiness timeout MUST
394
+ cooperatively cancel the exact child so late initialization cannot strand an
395
+ unreported listener. Ownership publication MUST check cancellation before its
396
+ atomic replacement, and the listener MUST retain the port until that write
397
+ commits or aborts on every shutdown path so an older writer cannot overwrite a
398
+ successor's credential.
399
+
400
+ MCP EOF, explicit close, protocol close, SIGINT, SIGTERM, transport-start
401
+ failure, and MCP-parent loss MUST still bound MCP shutdown. They MUST close the
402
+ MCP manager and transport without directly closing the independently hosted
403
+ session dashboard. Foreground `taskchef dashboard` remains `standalone`, is
404
+ never adopted by MCP, and follows its foreground process lifetime.
352
405
 
353
406
  The packaged `$taskchef-dashboard` skill MUST call this tool, report `started`
354
407
  or `reused`, and return the canonical clickable URL. It MAY use an available
@@ -358,6 +411,9 @@ back to the link without failing. It MUST NOT inspect task outcomes or dispatch.
358
411
  **Annotations:** `readOnlyHint: false`, `destructiveHint: false`,
359
412
  `openWorldHint: false`.
360
413
 
414
+ The complete operational rationale and limits are documented in
415
+ [Dashboard lifecycle](dashboard-lifecycle.md).
416
+
361
417
  ### `prepare_dispatch`
362
418
 
363
419
  **Caller:** dispatcher. **Mutation:** none.
@@ -643,8 +699,8 @@ identity or infer semantic results.
643
699
 
644
700
  TaskChef MUST NOT install or require daemons, launchd agents, login items,
645
701
  system services, cron jobs, hooks, privileged components, or elevated/system
646
- permissions for dashboard availability. Availability is best-effort while the
647
- owning Codex/plugin MCP process is alive and is not guaranteed while Codex is
702
+ permissions for dashboard availability. Availability is best-effort while a
703
+ registered Codex session process is alive and is not guaranteed while Codex is
648
704
  closed.
649
705
 
650
706
  Releases predating authenticated dashboard handoff are legacy listeners. A
@@ -656,6 +712,6 @@ protocol, compatible prior-version handoff is automatic.
656
712
  Installing or replacing plugin files MUST NOT be described as activating the
657
713
  new MCP code. Release verification MUST install the plugin, activate or reload
658
714
  the new MCP process, ensure the dashboard, and verify the expected TaskChef
659
- version, dashboard protocol `serverVersion`, `mcp` launcher, canonical workspace,
660
- and canonical URL. Exact-compatible MCP-listener reuse remains valid; installation MUST NOT be
715
+ version, dashboard protocol `serverVersion`, `session` launcher, canonical workspace,
716
+ and canonical URL. Exact-compatible session-listener reuse remains valid; installation MUST NOT be
661
717
  claimed to reload Codex automatically.