From 4aaf7d848cbc812158a711c56fb9c77745a2c517 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sat, 4 Jul 2026 17:05:00 +0800 Subject: [PATCH] =?UTF-8?q?docs(app-boot):=20fix=20review=20findings=20?= =?UTF-8?q?=E2=80=94=20exit-semantics=20precision,=20dep=20row,=20composit?= =?UTF-8?q?ion=20wording?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module/README prose claimed both guard-covered failure classes would otherwise exit 0; only the failed-IMPORT class does (an init throw already exits non-zero via Node's default handler — the guard contributes the labelled line and the guaranteed exit(1), as its own JSDoc says). The dependency-graph row now says loader/include (include is a peer: boot() names it by entry string, so the installed consumer provides it). The bins are self-executing compositions, not main() wrappers. --- packages/README.md | 2 +- packages/ui/app-boot/README.md | 2 +- packages/ui/app-boot/src/index.ts | 20 +++++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/README.md b/packages/README.md index 2f87d57d0d..1d6baadf5a 100644 --- a/packages/README.md +++ b/packages/README.md @@ -63,7 +63,7 @@ dsh-subagent-acp ← dsh-subagent, dsh-agent, dsh-llm, @agentclientprotocol/sdk dsh-tool-subagent ← dsh-subagent, dsh-tools, dsh-agent, dsh-llm (model-facing delegation tool) dsh-tool-todo ← dsh-tools, dsh-agent, dsh-session (model-facing todo_write tool; whole list on the session log) dsh-agent-core ← timer, dsh-llm, dsh-session, dsh-system-prompt, dsh-tools, dsh-agent, dsh-invariants, dsh-tool-bash, dsh-agent-loop (the providerless spine, as one bundle plugin) -dsh-app-boot ← (cordis + loader only) (shared bin boot glue: .env, fail-loud guards, boot sequence) +dsh-app-boot ← (cordis + loader/include only) (shared bin boot glue: .env, fail-loud guards, boot sequence) dsh-stdio-agent ← dsh-agent-core, dsh-session-persistence-jsonl, dsh-agent, dsh-session, dsh-app-boot (stdio chat APP + readline UI + bin) dsh-acp-agent ← dsh-agent-core, dsh-acp, dsh-session-persistence-jsonl, dsh-app-boot (ACP server APP + bin) ``` diff --git a/packages/ui/app-boot/README.md b/packages/ui/app-boot/README.md index 7a03b2b72b..92fbc333be 100644 --- a/packages/ui/app-boot/README.md +++ b/packages/ui/app-boot/README.md @@ -10,6 +10,6 @@ Shared boot glue for the app bins ([`dsh-stdio-agent`](../stdio-agent/README.md) | `assertEntriesLoaded(ctx, binName)` | Throw when a settled tree holds an enabled entry with no fiber (a plugin module that failed to import) | | `boot(binName, absoluteConfigPath)` | Mount the Loader, include the config by absolute `file://` URL, await the whole tree, assert entries loaded, return the root context | -Two failure classes the guards close — both would otherwise exit 0 with a usable config typo reported only as a log line: `loader.await()` swallows init rejections (`Promise.allSettled`), surfaced instead by `installFailLoud`; a failed plugin IMPORT is only logged by the Loader, leaving a fiber-less entry that `assertEntriesLoaded` turns into a `boot()` rejection. +Two failure classes the guards handle: `loader.await()` swallows init rejections (`Promise.allSettled`) — Node still exits non-zero on the resulting unhandled rejection, and `installFailLoud` replaces the noisy dump with one labelled line and a guaranteed `exit(1)`; a failed plugin IMPORT is only logged by the Loader (the process would otherwise exit 0 on a usable config typo), leaving a fiber-less entry that `assertEntriesLoaded` turns into a `boot()` rejection. Bare plugin specifiers in a config (`@deepseek-ai/dsh-*`) resolve through the cordis Loader's internal module loader, active only under `node --expose-internals`; the bins' subprocess smokes exercise that path, while this package's unit suite drives `boot()` in-process against configs with relative specifiers. diff --git a/packages/ui/app-boot/src/index.ts b/packages/ui/app-boot/src/index.ts index 5322ca7eb6..5515501188 100644 --- a/packages/ui/app-boot/src/index.ts +++ b/packages/ui/app-boot/src/index.ts @@ -3,22 +3,24 @@ * the gitignored `.env`, install the fail-loud Loader guards, resolve the * config path (snapshot-aware), and drive the cordis Loader against a leaf * `cordis.yml` until the whole tree has settled. Each bin stays a thin - * self-executing `main()` over these helpers, parameterized by its diagnostic - * prefix; the loader-failure lore lives here, once, under the per-file - * coverage gate. + * self-executing composition over these helpers, parameterized by its + * diagnostic prefix; the loader-failure lore lives here, once, under the + * per-file coverage gate. * - * Two failure classes the guards close, both of which would otherwise exit 0 - * with a usable config typo reported only as a log line: + * Two failure classes the guards handle: * * - `loader.await()` does NOT rethrow a load error (`EntryTree.await()` uses * `Promise.allSettled`, which swallows rejections). A plugin whose * `[Service.init]` throws surfaces as an unhandled rejection AFTER `boot()` - * resolves — {@link installFailLoud} turns that into one labelled stderr - * line and a guaranteed non-zero exit. + * resolves — Node's default handler already exits non-zero, and + * {@link installFailLoud} replaces the noisy dump with one labelled stderr + * line and a guaranteed `exit(1)`. * - A plugin module that fails to IMPORT is caught and only LOGGED by the * cordis Loader (`entry._init`), leaving the entry with no `fiber` and - * producing no rejection — {@link assertEntriesLoaded} makes `boot()` reject - * on any such entry instead of returning a half-empty context. + * producing no rejection — the process would otherwise exit 0 with a usable + * config typo reported only as a log line; {@link assertEntriesLoaded} makes + * `boot()` reject on any such entry instead of returning a half-empty + * context. * * @module @deepseek-ai/dsh-app-boot */