瀏覽代碼

docs(tasks): clarify admission lifecycle

pku-xht 2 月之前
父節點
當前提交
38922cbbb9

+ 2 - 2
.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.i18n.yaml

@@ -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

+ 1 - 1
.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md

@@ -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.
 

+ 1 - 1
.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.zh.md

@@ -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 - 2
.agents/notes/implemented/bug-fix/2026-08-11-bounded-background-task-admission.i18n.yaml

@@ -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

+ 3 - 1
.agents/notes/implemented/bug-fix/2026-08-11-bounded-background-task-admission.md

@@ -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.

+ 3 - 1
.agents/notes/implemented/bug-fix/2026-08-11-bounded-background-task-admission.zh.md

@@ -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 - 2
docs/config-catalog.i18n.yaml

@@ -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

+ 4 - 1
docs/config-catalog.md

@@ -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
 }
 ```

+ 4 - 1
docs/config-catalog.zh.md

@@ -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 - 2
packages/tasks/tasks-local/README.i18n.yaml

@@ -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

+ 1 - 1
packages/tasks/tasks-local/README.md

@@ -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.

+ 1 - 1
packages/tasks/tasks-local/README.zh.md

@@ -31,4 +31,4 @@
 ## 已知限制与暂缓事项
 
 - **任务只存在于进程本地**:记录会随 harness 进程终止而消失;持久或跨重启执行需要一个单独实现该 seam 的后端。
-- **静默无效的取消可能使销毁过程停滞**:只有显式抛出异常才能安全地强制标为失败。
+- **静默无效的取消可能使销毁过程停滞并持续占用容量**:如果 `cancel` 返回后始终未结算 `done`,注册表就无法将其与缓慢停止区分开;该任务会在服务剩余生命周期内持续占用一个桶名额,只有显式抛出异常才能安全地强制标为失败。

+ 4 - 1
packages/tasks/tasks-local/src/index.ts

@@ -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
 }
 

+ 4 - 2
packages/tasks/tasks-local/src/invariant.ts

@@ -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 = () => {}