docs: define error handling responsibilities and failure guarantees

This commit is contained in:
turtle1999 committed 2026-10-09 11:51:51 +08:00
1 parent cdf2f4c4b6
commit 4c8d377a23
7 files changed
+68 -5

No files matched your search

+1 -1
View File
@@ -24,7 +24,7 @@ This skill is guidance, not a complete checklist. It covers judgment calls that
## Feedback surfaces
Choose the surface by the lifetime of the message relative to the surface that produced it:
For failure meaning, safe recovery actions, and reporting untrusted state or unknown outcomes, apply [dsh-error-handling](../dsh-error-handling/SKILL.md#localized-actionable-visible-reports). Choose the surface by the lifetime of the message relative to the surface that produced it:
- **Transient operation outcomes use the app-wide Toast primitive**, not in-place notices, and the toast's state and rendering live in a host that outlives the reporting surface (a `shell.overlay` entry, as RowActionToast does) — a toast rendered by the panel itself unmounts with that panel. A deletion can close the panel or tab that requested it; only a toast held outside that surface survives the close. Report success and failure; do not report still-pending states.
- **A failed operation keeps the data visible.** A failed deletion keeps its row; the toast announces the failure. Never blank content to show an error for a transient operation.
+1
View File
@@ -31,6 +31,7 @@ description: Use when reviewing a pull request in the deepseek-harness repo —
## Manual checks
- **Intent and interface contracts:** trace both sides of every changed interface. Confirm the implementation matches the PR and any Agent Note, including errors, cancellation, ownership, and disposal.
- **Error handling:** apply [dsh-error-handling](../dsh-error-handling/SKILL.md) to changed failure paths, including their state guarantees and final user-visible reports. Reject catch-and-continue without valid state or a safe recovery action.
- **Lifecycle and concurrency:** for async setup, callbacks, processes, or teardown, apply [defensive-patterns.md](../../../docs/defensive-patterns.md). Check races before publication, cancellation during awaits, independent error reporting, callback containment, ownership before reentry, complete detach cleanup, and quiescent disposal.
- **Capability and consumer fit:** trace every current consumer, then flag consumer-specific behavior leaking into the interface under [the package rules](../../../packages/AGENTS.md). Flag the inverse too: a new public method on a generic service (registry, session, agent) whose only caller is one internal consumer is an unnecessary API expansion — require a private capability closure handed to that consumer at construction instead.
- **Scope, ownership, and necessity:** map each abstraction, state machine, option, defensive copy, and compatibility path to its current contract, production consumer, and owning plugin or service. Challenge unrelated features and speculative generality, then test the PR against [the root rules](../../../AGENTS.md#conventions).
@@ -0,0 +1,61 @@
---
name: dsh-error-handling
description: Design and review DeepSeek Harness error handling. Use when adding or changing thrown errors, failure results, catch blocks, retries, recovery, background-task failure handling, or user-visible error reports. Covers typed errors, localized actionable messages, handling ownership, fault containment, and guarantees after failure.
---
# DeepSeek Harness Error Handling
Define how callers identify a failure, who decides its disposition, what state remains valid, and how affected users learn about it. Apply these rules to the changed operation and its consumers; preserve existing package dependencies and error representations instead of introducing a parallel error framework.
## Typed, structured failures
- Errors raised by owned code must carry a declared type and a stable machine-readable discriminant, with typed details needed for handling. Do not introduce message-only `new Error(...)`, thrown strings, or decisions based on parsing your own error messages. Expected failures returned as values need discriminated result types too.
- Reuse the package's error taxonomy. [HarnessError](../../../packages/llm/llm/src/error.ts) provides a stable `code` and standard `cause`; do not add a dependency cycle merely to inherit it. A compatible package-owned representation is valid, as illustrated by [attachment errors](../../../packages/attachment/attachment/README.md).
- Preserve the original cause when translating external failures into domain errors. Normalize untyped dependency failures at the owning integration; do not invent a specific diagnosis when the cause is unknown. Across process, wire, or persistence interfaces, preserve the declared failure fields through the existing serialization format; do not rely on `instanceof` surviving transport.
- Keep machine identity separate from display text. Codes, typed parameters, and diagnostic causes serve different consumers; a localized sentence is not an error identifier. Do not serialize secrets or expose unrestricted internal diagnostics to users.
## Responsibility and guarantees before implementation
For each failure category, establish the following in the owning API's JSDoc or package README where callers need it. Keep rationale and extended examples out of local comments.
| Subject | Required decision |
|---|---|
| Failure definition | Which component defines the meaning, discriminant, and handling-relevant details? |
| Handling owner | Which layer has the context and authority to recover, terminate, or escalate? |
| Containment boundary | Which operation, task, instance, or process may be affected, and what mechanism limits the effects? |
| Failure guarantee | Which invariants remain valid? Are effects absent, partial, committed, or unknown? What cleanup remains necessary? |
| Reporting owner | Who publishes the terminal status and user-visible report, including if the normal presenter fails? |
A low-level component specifies its own guarantees and cleans up its resources; it need not know a concrete upper-layer catch function. The caller or application composition assigns final disposition. Background tasks need an observer and reporting owner before they start; returning to the caller does not discharge those responsibilities.
## Catch, recover, or stop
- A catch must recover, clean up and propagate, translate and propagate, or terminate and report. Logging alone is not recovery. Catch expected failures narrowly; an unexpected defect must not become an ordinary success or an empty result.
- Continue only when the preconditions for subsequent work still hold. Roll back, compensate, or discard affected state as required. If state validity cannot be established, stop dependent work and invalidate the instance or escalate to its supervisor. Unaffected work may continue only when actual isolation supports it.
- A try/catch limits propagation of an exception, not mutation of shared state. Use resource ownership, transactions, rollback, instance disposal, or process isolation to enforce the declared containment. Apply the [dispatcher and teardown rules](../../../docs/defensive-patterns.md) to their lifecycle responsibilities.
- Retry only when operation semantics and current effects make repetition safe, with cancellation and an explicit retry owner. Finite operations need an attempt or time budget. Long-lived supervision or reconnection may continue under an explicit lifecycle, with backoff, per-attempt limits, and observable degraded status. A timeout may mean the remote operation committed; reconcile the result or use supported idempotency before repeating it. An error code alone does not establish retry safety.
- Expected cancellation has its own outcome. It is neither success nor automatically a fatal defect; partial or unknown effects still require cleanup, disposition, and reporting. Preserve both the original failure and cleanup failures rather than hiding one with the other.
## Localized, actionable, visible reports
Every final human-facing error report must be localized, readable, and actionable. Use the receiving application's locale mechanism; Client UI follows the [locale-owned copy rule](../../notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.md). Map structured identity to a summary, affected operation, and safe next action. Keep raw causes as separately accessible, appropriately redacted diagnostics; an untranslated exception message alone is not the report. Unknown codes still need a localized fallback that admits uncertainty and offers a safe next step.
**Untrusted state or an unknown outcome must never fail silently.** Stop work that depends on that state and explicitly report what is affected, what is known or uncertain, what the system has done, and what the user can safely do next. Never claim rollback, unchanged data, success, or safe retry without the corresponding guarantee.
- **Interactive UI:** show the failure where the affected operation or component is visible. A persistent invalid or unknown state needs a persistent notice or unavailable state; a disappearing toast or background log is insufficient. Follow [UI feedback placement](../dsh-client-ui-ux/SKILL.md#feedback-surfaces).
- **CLI:** emit a readable diagnostic and fail the invocation with a nonzero exit status. A long-running command must expose the affected operation's failed status even when its host remains running.
- **Unattended tasks and services:** emit an error record through a default-enabled channel accessible to the user or operator, and expose failed, unknown, or unavailable task/component status for inspection. Disabled debug logs do not satisfy this requirement.
Assign a reporting fallback outside the failed component, such as host stderr or an independent fatal-error presenter. Reporting failure must not turn the original operation into success or cause an unbounded reporting loop. Machine-oriented logs may retain stable codes and raw diagnostic text; the explanation presented to a human still requires localized context and next steps.
For example, a timed-out remote save with an unknown result must report uncertainty and suspend dependent writes until reconciliation. Direct the user to check the operation's authoritative status; a stale read or a still-running write cannot establish that retry is safe.
## Verification
Trace representative failures from the producer through the actual consumer to the final status and report. Test structured identity across any affected serialization, state validity after partial effects, cleanup, retry budgets or supervisor lifecycle, cancellation, and the visible report in supported locales. Include unexpected failures, unknown outcomes, and a failed normal reporter when those paths apply. Assert that dependent work stops and that no success signal is published; asserting only that an exception was caught or a logger was called is insufficient. Use the existing [testing policy](../../../docs/testing.md) for the relevant unit, integration, and output evidence.
## Conceptual references
- Bertrand Meyer, *Object-Oriented Software Construction*, second edition (1997): exception handling in relation to routine contracts, invariants, retry, and failure propagation.
- Herb Sutter, *Exceptional C++* (1999): exception safety guarantees distinguish valid state and resource cleanup from rollback of observable effects.
- Joe Armstrong, [*Making reliable distributed systems in the presence of software errors*](https://www.erlang.org/download/armstrong_thesis_2003.pdf) (2003), and [OTP supervision principles](https://www.erlang.org/doc/system/sup_princ.html): isolation and an explicit supervisor responsible for recovery.
+1
View File
@@ -143,6 +143,7 @@ Real-API tests/demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`, and
- **No new assertions to `unknown`** (`as unknown` or `<unknown>`). Preserve or reduce the exact legacy baseline; use typed values or validation for replacements ([rule](.agents/notes/implemented/process/2026-09-19-no-unknown-casts.md)).
- **Source plane vs artifact plane, never mixed.** Static gates and tests resolve workspace imports through tsconfig `paths` to `src` and pass on a clean tree; gates consuming built `lib/` declare that dependency ([layout](docs/development.md#typescript-project-layout)).
- **Keep compiler faces explicit.** A package with both Host and Client programs exposes face-specific leaf configs and a solution-only root; repo-wide programs seed a face config, never the root solution ([layout](docs/development.md#typescript-project-layout)).
- **Error handling:** use typed, structured failures and localized, actionable reports. Define handling ownership, containment, and failure guarantees; stop dependent work and visibly report untrusted state or unknown outcomes. Follow [dsh-error-handling](.agents/skills/dsh-error-handling/SKILL.md).
- **An empty `catch` names the error** and why; keep its `try` to one statement.
- **Keep comments local.** Do not restate code, expand unrelated comments, or explain distant behavior without local need ([rationale](.agents/skills/dsh-prose-standard/SKILL.md)).
- **Ban `prove` + `nance`** ([rule](.agents/notes/implemented/process/2026-08-26-ban-ambiguous-origin-label.md)).
+2 -2
View File
@@ -18,8 +18,8 @@
en: 5c906be504b88a65
zh: 8f428f59a87bb1a8
/defensive-patterns/contain-callback-exceptions-in-the-dispatcher:
en: ae1788e76190db2c
zh: a55ab7473bb0c8dd
en: 7dc97fc34b96ebfd
zh: d8763aba452375e7
/defensive-patterns/never-hand-untrusted-output-the-ambient-environment-or-predictable-paths:
en: 09ad9d6433026468
zh: cc0ba1a52859bb08
+1 -1
View File
@@ -22,7 +22,7 @@ A teardown that issues kills/aborts but returns before the work stops leaves orp
## Contain callback exceptions in the dispatcher
A user-supplied listener that throws must not reject the promise it runs inside or starve the listeners after it. Wrap the dispatch loop in try/catch and log; one bad subscriber never breaks core lifecycle.
Catch and report each listener's exception without rejecting the dispatch promise or starving independent listeners. Continue only while shared state remains valid; catching does not undo mutations. If validity is unknown, stop dependent work and escalate to its handling owner with a user-visible report ([error handling](../.agents/skills/dsh-error-handling/SKILL.md)).
## Never hand untrusted output the ambient environment or predictable paths
+1 -1
View File
@@ -24,7 +24,7 @@
## 在分发器中隔离回调异常
用户提供的监听器如果抛出异常,不得导致它所在的 promise 被 reject,也不得饿死排在它后面的监听器。请用 try/catch 包裹分发循环并记录日志;一个行为不当的订阅者绝不能破坏核心生命周期。
逐个捕获并报告监听器的异常,不让分发 promise 被 reject,也不让独立监听器失去执行机会。只有共享状态仍有效时才能继续;捕获异常不会撤销状态修改。若无法确认状态有效,应停止依赖该状态的工作,并交由处置责任方处理,同时向用户明确报告(见[错误处理规范](../.agents/skills/dsh-error-handling/SKILL.md))。
## 绝不将环境变量或可预测路径暴露给不可信输出