docs(app-boot): fix review findings — exit-semantics precision, dep row, composition wording

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.
This commit is contained in:
Tianyi Cui
2026-07-04 17:05:00 +08:00
parent 4a0941fb4b
commit 4aaf7d848c
3 changed files with 13 additions and 11 deletions

View File

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

View File

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

View File

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