cf550f3feb4bfeb6809a2cd324cf28f4cb8ec00d
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4d7726ecd3 |
fix review findings: don't treat disabled entries as load failures; scope the verify-package-paths lib skip to a real package root
assertEntriesLoaded() flagged ANY fiber-less entry as a failed import, but a `disabled: true` entry settles without a fiber by design (Entry.refresh() skips init() when disabled) — a valid "plugin off" config, not a broken import. Both app bins now filter `fiber === undefined && !entry.disabled`. The stdio built-bin smoke gains a disabled-(unresolvable)-entry config that must still boot. The verify-package-paths lib-skip was unconditional and ran before the moved-package check, so a stale group-less `packages/acp-agent/lib/bin.js` (the exact drift this gate catches) was silently ignored just for containing `lib`. Scope the skip: only exempt `lib` when it is the segment after an EXISTING `packages/<group>/<pkg>` root, so a real-but-unbuilt `lib/bin.js` is still exempt while a stale package path flags. |
||
|
|
3567808171 |
fix review findings: harden the app bins + built-bin smokes, arch-exception doc, snapshot fixture-guard
BLOCKER — the published lib/bin.js (stdio + acp) was exercised only via tsx
(demo:* / the src/bin.ts smokes); the built artifact under plain `node` was
unguarded. Root-cause on the BUILT bin:
1. Settle race: boot() returned once loader.create() registered the include
ENTRY, but the include loads its child plugins asynchronously — so boot()
(and main()) resolved while the app plugins (stdin reader, agent loop, ACP
bridge) were still mounting. A CLI with no attached handles yet exits 0
silently, and a load error surfaces as an unhandled rejection AFTER boot.
Fix: `await ctx.loader.await()` after create() — settle the whole tree.
2. Config-path robustness: hand the include the config's ABSOLUTE file:// URL
so resolution never depends on ctx.baseUrl / can never fall back to cwd.
Both bins fixed identically. NOTE: the cordis Loader resolves a config's bare
plugin specifiers via its internal module loader, active only under
`node --expose-internals`; the bin cannot add a node flag itself, so this is
documented in the bin JSDoc + both package READMEs (the demos already comply).
The repo `examples/*/cordis.yml` are tsx-only artifacts (workspace plugins
resolve through the tsconfig paths map, not node_modules), so they are not a
valid plain-node bin target — the smokes use a real-install-shaped temp dir.
Fail loud on a load failure: boot() previously exited 0 SILENTLY when a config
path's directory does not exist — the include plugin fails to IMPORT, the cordis
Loader catches+LOGS it and leaves the entry with no fiber (no rejection), and
`loader.await()` does not rethrow (EntryTree.await uses Promise.allSettled). Fix:
boot() now calls assertEntriesLoaded(ctx) after the tree settles and throws on
any entry with no fiber, so a typo'd config dir exits non-zero with a clear
message. main() also installs an unhandledRejection guard (installFailLoud) that
replaces Node's stack dump with a single labelled stderr line for the
companion case (a missing config FILE in a real dir, whose include-init throw
surfaces as a rejection Node already exits non-zero on). Regression tests added
to both built-bin smokes (missing dir + missing file → non-zero exit + stderr);
verified the missing-dir test fails on the pre-fix bin.
Built-bin smokes (the reviewer's ask): packages/ui/{stdio,acp}-agent/tests/
built-bin.e2e.ts run the REAL lib/bin.js under `node` (NOT tsx) in a temp
consumer dir, asserting the stdio echo round-trip / the acp initialize response
+ stdout purity, plus the fail-loud cases above. They build-gate (skip if lib/
absent) and run in a new ci.yml step after the build.
Issue 2 — packages/README.md + docs/architecture.md said "plugins depend on
interfaces, never on the concrete loop", but dsh-agent-core imports the concrete
dsh-agent-loop. Scope the rule to EXTENSION plugins and carve out the sanctioned
COMPOSITION/bundle exception (dsh-agent-core composes the concrete spine); note
it in the implemented RFC too.
Issue 3 — examples/acp-agent/tests/acp.snapshot.ts fixture-guard claimed
no-model scenarios need no session.jsonl, but runScenario() always boots
llm-replay with the session.jsonl path and loadReplayScript() throws when it is
absent. Require session.jsonl for ALL scenarios (no-model ones ship a
header-only fixture) and rewrite the comment to match reality.
|
||
|
|
66e56bd395 |
fix review findings: stale coding-agent README, export-shape guards, overclaim wording
Codex review of PR #88 found three issues in the example-app extraction: A1 — examples/coding-agent/README.md's plugin table still listed the OLD direct-wired leaf entries (agent-loop, session-persistence, src/stdio-chat.ts — the whole src/ dir is gone). Rewrite it to the four real leaf entries the current cordis.yml loads (hmr, llm-deepseek, bash, stdio-agent), noting that tool-bash/persistence/agent/loop now live inside the agent-core + stdio-agent bundles. A2 — the three new app/spine packages (agent-core, stdio-agent, acp-agent) export NO `inject`, so a stray `export default apply` would let unwrapExports collapse the module and silently DROP name/Config WITHOUT crashing — the real-load-path smokes would stay green. agent-core is never Loader-unwrapped at all. Add an explicit export-shape guard per package: assert no `default` export and that the real Loader.unwrapExports leaves name/Config/apply intact. Verified each fails when `export default apply` is added. B — soften "structurally unreachable / cannot wire a stdout logger" overclaims in the acp-agent/agent-core READMEs and the implemented RFC: a leaf CAN still add a sibling logger entry; the accurate claim is the app omits one so the default leaf has nothing to get wrong. Keep the safety directive (never add a stdout logger to an ACP leaf). |
||
|
|
e2bde2902c |
refactor(examples): extract the app spine into dsh-agent-core + app packages
Implements docs/rfc/.../2026-06-20-extract-example-app-packages.md. Each
example was thick — a hand-rolled start.ts, an infra preamble, nested
base.yml/base-core.yml/acp-tail.yml includes, and a coupled front-door
cluster enforced only by prose. This moves the composition into packages so
each example is a thin leaf cordis.yml: pick the swappable backends, load one
app package.
New packages:
- @deepseek-ai/dsh-agent-core (packages/core/agent-core): one bundle plugin
that loads the providerless/executor-less/UI-less spine (timer + llm +
sessions + system-prompt + tools + agents + invariants + tool-bash +
agent-loop) via ctx.plugin(...) inside apply(), and forwards agent-loop's
`agents` list as its own Config (export const Config = AgentLoop.Config,
default []).
- @deepseek-ai/dsh-stdio-agent (packages/ui/stdio-agent): terminal chat APP —
agent-core + console logger + readline UI + a pre-created `main` agent, with
a bin. The demo:echo/coding front door.
- @deepseek-ai/dsh-acp-agent (packages/ui/acp-agent): ACP server APP —
agent-core + JSONL persistence + the acp bridge, NO stdout logger, with a
bin. The stdout-purity footgun is structurally unreachable from the leaf.
Amendment to the RFC: hmr stays a LEAF cordis.yml entry, not baked into
dsh-stdio-agent. hmr is a Loader-only dev plugin (throws without
--expose-internals; the in-process test tier can't even import its decorator
form), so a package statically importing it could never carry the per-file
coverage gate. Unlike the console logger, a stray hmr is not a stdout-purity
footgun, so leaving it at the leaf costs no safety. With hmr out, all three new
packages carry in-process unit specs at 100%.
Boot glue (Loader tail, .env load, snapshot-mode selection, stdin-dispose
lifecycle) moves into each app's bin; start.ts and base.yml/base-core.yml/
acp-tail.yml are deleted. Each app package gets a keyless real-load-path test
that boots through its bin + the cordis Loader (guarding the unwrapExports
export-shape bug class, postmortem 0001). ACP snapshot replay stays green
against the existing committed goldens (pure boot restructuring). RFC moved
proposed->implemented with the amendment recorded; package/example/architecture
docs and the module graph updated.
|