docs(tasks): clarify admission lifecycle
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md
|
||||
2026-06-20-generic-long-running-tool-runtime.md: 12e9b794e7e31c0dcbb45fac2f1f3d6852573b5a
|
||||
2026-06-20-generic-long-running-tool-runtime.zh.md: 2d85b9ba824f9f1620a31c181e5d9a573c52875f
|
||||
2026-06-20-generic-long-running-tool-runtime.md: 1edc3422c253e06178a5c8ebf68dfd4ef1289e31
|
||||
2026-06-20-generic-long-running-tool-runtime.zh.md: 8cfc2b2ec7ecce1a358b0be63ca52e9319bf362a
|
||||
|
||||
@@ -25,7 +25,7 @@ Long-running tools are producers. `dsh-tool-bash` adapts a `BashProcess` into in
|
||||
|
||||
The literal types live on the [tasks subsystem page](../../../../docs/subsystems/tasks.md). A producer calls `ctx.tasks.start()` with a kind, label, optional owning `Agent`, optional positive `outputLimitBytes`, and a `run()` function. The runtime completes all failable preflight work before calling `run()` and invokes it once. After `run()` returns hooks, registration commits without another failable step; a producer cannot start work that lacks a collectable task id.
|
||||
|
||||
The process-local provider also owns bounded admission. Its positive-safe-integer `maxConcurrentTasksPerOwner` config defaults to `10`; `start()` derives each exact `Agent` object's active count from `running` and `stopping` records, while every unowned task shares one service bucket. Capacity rejection occurs before `run()` and id allocation, and producer `done` settlement is the only event that releases a stopping task's place. The provider does not queue, preempt, or retain a second mutable count.
|
||||
The process-local provider also owns bounded admission, whose rationale is recorded in the [bounded background task admission decision](../bug-fix/2026-08-11-bounded-background-task-admission.md). Its positive-safe-integer `maxConcurrentTasksPerOwner` config defaults to `10`; `start()` derives each exact `Agent` object's active count from `running` and `stopping` records, while every unowned task shares one service bucket. Capacity rejection occurs before `run()` and id allocation, and producer `done` settlement is the only event that releases a stopping task's place. The provider does not queue, preempt, or retain a second mutable count.
|
||||
|
||||
`outputLimitBytes` is producer-owned presentation policy, not a registry buffer. The registry validates and projects it unchanged into `TaskSnapshot`; generic control APIs apply the cap to complete model-facing output after adding their own status or notice metadata. Omitting it preserves the existing controller behavior, so the runtime does not impose a hidden default on unrelated producer families.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Status: implemented
|
||||
|
||||
字面类型见[任务子系统页面](../../../../docs/subsystems/tasks.md)。生产方调用 `ctx.tasks.start()`,传入 kind、label、可选的所属 `Agent`、可选的正数 `outputLimitBytes` 与一个 `run()` 函数。运行时会在调用 `run()` 前完成所有可能失败的预检工作,并且只调用一次。`run()` 返回钩子后,注册过程不会再执行可能失败的步骤而直接提交;生产方无法启动没有可收集 task id 的工作。
|
||||
|
||||
进程内 Service provider 还拥有有界准入。它的 `maxConcurrentTasksPerOwner` 配置必须是正的安全整数,默认值为 `10`;`start()` 从 `running` 与 `stopping` 记录派生每个确切 `Agent` 对象的活动数量,而全部无 owner 任务共享一个服务级桶。容量拒绝发生在 `run()` 与 id 分配之前,处于 stopping 的任务只有在生产方 `done` 结算时才释放名额。Service provider 不排队或抢占任务,也不保留第二份可变计数。
|
||||
进程内 Service provider 还拥有有界准入,其理由记录在[有界后台任务准入决策](../bug-fix/2026-08-11-bounded-background-task-admission.md)中。它的 `maxConcurrentTasksPerOwner` 配置必须是正的安全整数,默认值为 `10`;`start()` 从 `running` 与 `stopping` 记录派生每个确切 `Agent` 对象的活动数量,而全部无 owner 任务共享一个服务级桶。容量拒绝发生在 `run()` 与 id 分配之前,处于 stopping 的任务只有在生产方 `done` 结算时才释放名额。Service provider 不排队或抢占任务,也不保留第二份可变计数。
|
||||
|
||||
`outputLimitBytes` 是生产方拥有的呈现策略,而非注册表缓冲区。注册表校验该值,并将其原样投影到 `TaskSnapshot`;通用任务控制器添加自身的状态或通知元数据后,再将该上限应用于完整的面向模型输出。省略该值时保持现有控制器行为,因此运行时不会向无关的生产方类别施加隐式默认值。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-11-bounded-background-task-admission.md
|
||||
2026-08-11-bounded-background-task-admission.md: a7df21e5e981d80635220af4d50fa596967659bd
|
||||
2026-08-11-bounded-background-task-admission.zh.md: 9d0041af05899a164b79b83c77d35fc8f25edff4
|
||||
2026-08-11-bounded-background-task-admission.md: 24512a87f554cd2d775fe76c5a6e5a700a51f2e4
|
||||
2026-08-11-bounded-background-task-admission.zh.md: dc3abaf4a64a4dc5fe5cacaabd3c29e278874646
|
||||
|
||||
@@ -14,6 +14,8 @@ The process-local task registry already owns the exact task owner and the author
|
||||
|
||||
`LocalTaskService` owns a `maxConcurrentTasksPerOwner` configuration field. It accepts positive safe integers, defaults to `10`, and is available through the provider's Cordis schema, the typed `agent-spine-demo` bundle, and the ACP app configuration. The bundle transports the value; the process-local provider owns its meaning.
|
||||
|
||||
The [generic task runtime decision](../architecture/2026-06-20-generic-long-running-tool-runtime.md) owns the shared Task lifecycle and control API; this note owns the process-local admission policy.
|
||||
|
||||
`start()` performs admission after the existing task-controller, task-field, and live-owner checks and before `TaskStart.run()`. It derives the active count from the registry's current records instead of storing another counter:
|
||||
|
||||
| Record | Occupies capacity | Release fact |
|
||||
@@ -48,6 +50,6 @@ The task-provider suite covers the default and explicit limits, producer-before
|
||||
|
||||
## Consequences
|
||||
|
||||
One exact owner cannot keep creating Task-backed live resources indefinitely, and unrelated owners retain independent allowances. A slow stop can temporarily keep a bucket full, which is deliberate: the configured number bounds work that may still own resources, not cancellation requests.
|
||||
One exact owner cannot keep creating Task-backed live resources indefinitely, and unrelated owners retain independent allowances. A slow stop keeps a bucket full until `done` settles, which is deliberate: the configured number bounds work that may still own resources, not cancellation requests. A producer whose `cancel` returns but whose `done` never settles holds one slot for the rest of the service lifetime and can stall teardown because the registry cannot safely infer resource release.
|
||||
|
||||
Admission scans the process-local registry on each start. The cost grows with retained Task history, accepted in exchange for one state authority and a default limit small enough to bound the common live set. Terminal history remains available to existing reads and listings without consuming capacity.
|
||||
|
||||
@@ -14,6 +14,8 @@ Status: implemented
|
||||
|
||||
`LocalTaskService` 拥有 `maxConcurrentTasksPerOwner` 配置字段。它只接受正的安全整数,默认值为 `10`,并通过 Service provider 的 Cordis schema、typed `agent-spine-demo` 组合包与 ACP 应用配置提供。组合包只传输该值;其含义归进程内 Service provider 所有。
|
||||
|
||||
[通用任务运行时决策](../architecture/2026-06-20-generic-long-running-tool-runtime.md)拥有共享 Task 生命周期与控制 API;本记录只拥有进程内准入策略。
|
||||
|
||||
`start()` 在现有任务控制器、任务字段与存活 owner 检查之后、`TaskStart.run()` 之前执行准入。它从注册表当前记录派生活动数量,而不保存另一份计数:
|
||||
|
||||
| 记录 | 占用容量 | 释放事实 |
|
||||
@@ -48,6 +50,6 @@ owner 与服务释放保留现有顺序:请求取消,在生产方释放资
|
||||
|
||||
## 后果
|
||||
|
||||
单个确切 owner 无法再无限创建由 Task 承载的实时资源,无关 owner 则保留独立额度。缓慢停止可能暂时让桶保持满载,这是有意行为:配置值限制的是仍可能拥有资源的工作,而不是取消请求。
|
||||
单个确切 owner 无法再无限创建由 Task 承载的实时资源,无关 owner 则保留独立额度。缓慢停止会让桶保持满载直到 `done` 结算,这是有意行为:配置值限制的是仍可能拥有资源的工作,而不是取消请求。如果生产方的 `cancel` 返回后始终不结算 `done`,它会在服务剩余生命周期内持续占用一个名额并阻塞销毁,因为注册表无法安全推断资源已经释放。
|
||||
|
||||
每次启动都会扫描进程内注册表。成本随保留的 Task 历史增长;为了保持单一状态权威,并利用足以约束常见实时集合的较小默认值,接受这一代价。终止历史仍可供现有读取与列表使用,但不消耗容量。
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
||||
config-catalog.md: f88bb888367e73188e20a1e2fbefdf3c4a926798
|
||||
config-catalog.zh.md: 5f3cbd274ec8704581642b74e7583249a0db6172
|
||||
config-catalog.md: 02abc64cfaf677e904ec585c28cbe6053444535c
|
||||
config-catalog.zh.md: 8542403f68098b9e3939fce6ca033c39b1ecaf27
|
||||
|
||||
@@ -2039,7 +2039,10 @@ Source: [`packages/core/system-prompt/src/index.ts:186`](../packages/core/system
|
||||
```ts config-catalog
|
||||
/** Configuration for the process-local task registry. */
|
||||
export interface Config {
|
||||
/** Maximum `running` plus `stopping` tasks per exact owner; omission defaults to 10. */
|
||||
/**
|
||||
* Maximum `running` plus `stopping` tasks per exact owner or in the shared unowned bucket;
|
||||
* omission defaults to 10.
|
||||
*/
|
||||
maxConcurrentTasksPerOwner?: number
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2041,7 +2041,10 @@ export interface Config {
|
||||
```ts config-catalog
|
||||
/** Configuration for the process-local task registry. */
|
||||
export interface Config {
|
||||
/** Maximum `running` plus `stopping` tasks per exact owner; omission defaults to 10. */
|
||||
/**
|
||||
* Maximum `running` plus `stopping` tasks per exact owner or in the shared unowned bucket;
|
||||
* omission defaults to 10.
|
||||
*/
|
||||
maxConcurrentTasksPerOwner?: number
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/tasks/tasks-local/README.md
|
||||
README.md: b486438eb2f73728361a6140fdec33603a2ace45
|
||||
README.zh.md: ecc9104e4145add9448d43e1eb2565a3fc7e09c3
|
||||
README.md: f558676b36bb5462453bde553eac27b458e1268e
|
||||
README.zh.md: ed8f2b1220692a00c09b6605b18231f5263f00e5
|
||||
|
||||
@@ -31,4 +31,4 @@ No direct invalidation; the named consumer owns any request-prefix changes.
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Tasks are process-local** — records die with the harness process; durable or cross-restart execution needs a separate backend implementing the seam.
|
||||
- **A silently ineffective cancel can stall teardown** — only an explicit throw can be force-failed safely.
|
||||
- **A silently ineffective cancel can stall teardown and hold capacity** — if `cancel` returns without settling `done`, the registry cannot distinguish it from a slow stop; the task keeps one bucket slot for the rest of the service lifetime, and only an explicit throw can be force-failed safely.
|
||||
|
||||
@@ -31,4 +31,4 @@
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **任务只存在于进程本地**:记录会随 harness 进程终止而消失;持久或跨重启执行需要一个单独实现该 seam 的后端。
|
||||
- **静默无效的取消可能使销毁过程停滞**:只有显式抛出异常才能安全地强制标为失败。
|
||||
- **静默无效的取消可能使销毁过程停滞并持续占用容量**:如果 `cancel` 返回后始终未结算 `done`,注册表就无法将其与缓慢停止区分开;该任务会在服务剩余生命周期内持续占用一个桶名额,只有显式抛出异常才能安全地强制标为失败。
|
||||
|
||||
@@ -29,7 +29,10 @@ const DEFAULT_MAX_CONCURRENT_TASKS_PER_OWNER = 10
|
||||
|
||||
/** Configuration for the process-local task registry. */
|
||||
export interface Config {
|
||||
/** Maximum `running` plus `stopping` tasks per exact owner; omission defaults to 10. */
|
||||
/**
|
||||
* Maximum `running` plus `stopping` tasks per exact owner or in the shared unowned bucket;
|
||||
* omission defaults to 10.
|
||||
*/
|
||||
maxConcurrentTasksPerOwner?: number
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,10 @@ export const name = 'tasks-local-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* No runtime invariant: the Service Definition companion in `@deepseek-ai/dsh-tasks` already
|
||||
* validates every registry snapshot this implementation publishes.
|
||||
* No runtime invariant: the Service Definition companion in `@deepseek-ai/dsh-tasks` validates
|
||||
* every published snapshot. `LocalTaskService.start()` enforces this provider's configured
|
||||
* admission limit synchronously before producer execution; repeating that aggregate check here
|
||||
* would expose provider-private configuration solely to this companion.
|
||||
*/
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user