Master's transactional loader made the invalid-provider PTY case regress: the HMR main watcher's initial scan refreshed the include mid-initial-apply, the concurrent group updates stranded the include fiber, and once serialized the failing apply's rollback deadlocked on HMR's refresh drain — dsh exited 13 with no diagnostic and the terminal stranded, the exact symptom this branch fixes. Serialize every include child-tree mutation through one queue and pass ignoreInitial to the HMR main watcher; the failing boot now settles through boot()'s labelled rejection with the tree disposed and exit 1. The PTY case asserts the settled diagnostic; the fail-loud release remains the guard for rejections boot cannot see.
13 KiB
Vendored Packages
This directory contains source-vendored copies of the Cordis framework and its foundation libraries. They are copied into this monorepo instead of being depended on via npm, so that the harness fully owns its framework layer (auditable, patchable, pinned).
All vendored packages keep their original npm names and are marked private: true — they are never published from this repo. pnpm-workspace.yaml#linkWorkspacePackages makes matching upstream semver ranges resolve these pinned workspaces, including imports from built lib/; disabling it substitutes npm copies behind the same names. The hygiene gate verify-vendored-links asserts every vendored name resolves to a workspace link: in pnpm-lock.yaml with no registry copy alongside. Schemastery's manifest additionally declares a conditional exports map (import → .mjs, require → .cjs): pnpm links the directory itself, so without exports Node's ESM resolver would fall back to main and load the CJS entry whose lazy require('cosmokit') can race ESM loading of the same linked module under module-hook hosts (vitest). Upstream MIT LICENSE files are preserved in each package directory.
This file covers the manifest, the local-modification log, and the procedure for updating an existing vendored package. To add a new one, see the cookbook guide: docs/cookbook/adding-a-vendored-package.md.
Manifest
Upstream workspace: cordis-workspace (local checkout: ~/repos/cordis-workspace).
| Directory | npm name | Version | Upstream repo | Commit |
|---|---|---|---|---|
cosmokit/ |
cosmokit |
1.8.1 | https://github.com/deepseek-harness/cosmokit | 16f6fc058ade66e8ac5da0033d35a8d0f279f544 |
schemastery/ |
schemastery |
3.18.0 | https://github.com/deepseek-harness/schemastery (packages/core) |
e67cee00ad725bd1534aee930a979ea3eec6f698 |
cordis/ |
cordis |
4.0.0-rc.7 | https://github.com/cordiverse/cordis (packages/core) |
56b3d4f725681cf4556c1a8695a709cc3b6eed74 |
loader/ |
@cordisjs/plugin-loader |
1.0.0-rc.5 | https://github.com/cordiverse/cordis (packages/loader) |
56b3d4f725681cf4556c1a8695a709cc3b6eed74 |
include/ |
@cordisjs/plugin-include |
1.0.4 | https://github.com/deepseek-harness/cordis (packages/include) |
abb0a307cb1d3b0947f455d590cf5ba922d4caa4 |
group/ |
@cordisjs/plugin-group |
1.0.0 | https://github.com/deepseek-harness/cordis (packages/group) |
abb0a307cb1d3b0947f455d590cf5ba922d4caa4 |
timer/ |
@cordisjs/plugin-timer |
1.1.2 | https://github.com/deepseek-harness/cordis (packages/timer) |
abb0a307cb1d3b0947f455d590cf5ba922d4caa4 |
hmr/ |
@cordisjs/plugin-hmr |
1.0.15 | https://github.com/deepseek-harness/cordis (packages/hmr) |
abb0a307cb1d3b0947f455d590cf5ba922d4caa4 |
logger-console/ |
@cordisjs/plugin-logger-console |
1.0.0 | https://github.com/deepseek-harness/cordis (packages/logger-console) |
abb0a307cb1d3b0947f455d590cf5ba922d4caa4 |
Third-party dependencies of the vendored packages stay on npm: @standard-schema/spec, js-yaml, chokidar, picomatch, @babel/code-frame, supports-color, node-addon-require-builtin.
Intentionally not vendored (verified unused by this set): reggol, @cordisjs/utils, @cordisjs/element, @cordisjs/unyaml (dev-time YAML import hook only).
Local modifications
Keep this log exhaustive — every divergence from upstream must be listed.
hmr/src/index.ts: removed the./locales/en-US.yml/./locales/zh-CN.ymlimports, the.i18n({...})call on theConfigschema, and thesrc/locales/directory. Rationale: those imports require a runtime YAML loader hook (@cordisjs/unyaml) that we do not vendor; the i18n texts only localize config descriptions.- All
package.jsonfiles: regenerated — addedprivate: true, added precisefilesentries for bundled runtime files andlib/types/**/*.d.ts/.d.ts.map, preservedsrcinfilesonly for packages whose previous file list already shipped it, added a./src/*export where missing, pointed declaration metadata atlib/types, and removed upstreamdevDependencies/scripts/repositoryfields. Dependency and peer-dependency ranges preserved, excepthmrdeclaresesbuildas a direct dev dependency because its source imports theBuildFailuretype and pnpm's strict workspace resolution requires the owner package to name that dependency. - All
tsconfig.jsonfiles: regenerated to extend the repo-roottsconfig.base.json, emit TypeScript intermediates tolib/types, and declare project references. - Vendored TypeScript source internal specifiers: changed local relative imports/exports from upstream's specifier shape to explicit
.tsspecifiers so TypeScript rewrites emitted JS to.jswhile declarations keep explicit, NodeNext-safe.tsspecifiers. This includesloader/src/config/isolate.tsusingdeclare module './entry.ts'. schemastery/tsdown.config.tsandlogger-console/tsdown.config.ts: ours, not upstream files — per-package build-shape overrides (dual ESM+CJS output; separate node/browser entries) for the repo-root tsdown build. They read the JS emitted underlib/typesand then write the publish runtime entries underlib/. Like the regenerated tsconfigs, they are not part of the upstream sync surface.cordis/src/fiber.tslifecycle hardening: locally closes three reentrant disposal gaps. An effect's owner-list wrapper is registered before its setup body runs, so an unload begun from inside setup awaits setup and every collected cleanup; synchronous setup failure removes the wrapper and rolls back collected cleanup. Async cleanup stays owner-visible until quiescence, and Cordis's internal effect composition joins an already-running cleanup while repeated public disposer calls retain their upstream single-shot result. Effect creation is rejected while the owner isUNLOADING(whilePENDINGandLOADINGremain legal), preventing cleanup-time registrations from escaping the unload snapshot. Child fibers register and receive their parent-owned disposer beforeinternal/pluginpublication, resolve dependency declarations added by that notification before activation, drain effects attached while pending, skip plugin execution when reentrant disposal invalidates the load epoch before its first checkpoint, and contain teardown-notification failures per observer so one callback cannot starve peers or interrupt ownership cleanup.Fiber.update()returns itsinternal/updatewaterfall result, allowing Loader callers to await a restart while preserving synchronous config validation.cordis/src/*.tsJSDoc enrichment: added@param/@returnstags and contract documentation (disposal semantics, waterfall veto, bail conditions, error cases) across the public plugin-author surface —Context(class, statics, and theContextinterface properties incl.root),EventsService,Fiber,RegistryService,ReflectService,Service,LoggerServiceand theirdeclare module './context.ts'overloads. Comment-only; no code changes. Motivation: the website API-reference generator renders these docs and hard-errors on undocumented members. Retire this entry when the enrichment is upstreamed to the fork.- Transactional Loader/Include config reconciliation: Loader imports a changed entry name before disposal, awaits lifecycle settlement, and restores the previous plugin or config when candidate application fails. Loader settlement rechecks service-gated fibers after current tasks drain, rejects failures, and leaves fibers with absent dependencies pending. Group updates start candidates concurrently, await every outcome, undo changes and additions on failure, await removal, preserve programmatic option identity, and persist direct or tree-level mutations only after success. Include reads and validates detached candidate content, applies patches to a clone, reconciles the tree, and only then commits its cached content/data; direct refresh failures propagate for the caller to contain. A non-array parse is invalid, patches re-apply on every file or Include-config update, an omitted patch list clears the overlay, and initial content falls back to
initialonly onENOENT. Covered bypackages/ui/app-boot/tests/config-reload.spec.tsandpackages/host/webserver/tests/webserver.spec.ts. hmr/src/index.tsexact config watching:registerConfig()watches one absolute config path outside module roots, including a path under missing parents, serializes and coalesces refreshes, and returns an async disposer that closes the watcher and drains active work. Refresh failures are normalized toError, logged, and broadcast through the parallelhmr/config-update-failedevent; observer failures are contained. Config-file changes discovered by the ordinary HMR watcher use the same serialized path. Covered bypackages/ui/app-boot/tests/hmr-config.spec.ts.loader/src/repository.ts,loader/tsdown.config.ts, and the@cordisjs/plugin-loader/repositoryexport: the Node-onlyRepositoryCacheinstalls one exact dependency specifier through the bundledpnpm@11.7.0, single-flights callers, and atomically publishes only a prepared package plus marker under the specifier hash. The subpath stays out of the browser-reachable Loader entry. Identical specifiers permanently reuse that entry; callers change the ref/specifier for another generation. The isolated workspace permits dependency build scripts because a configured repository is executable code, while the child drops ambient credential-shaped variables. Covered bypackages/ui/app-boot/tests/repository-cache.spec.ts, including a keyless local-Git prepare run through the bundled pnpm.- Vendored Node-compatible TypeScript: marked erased imports explicitly across
cordis,loader,include,hmr, andschemasteryso Node's native TypeScript transform does not request types as runtime exports. Schemastery's source uses an ESM default export and its package declarestype: module; its built ESM/CJS entries retain explicit.mjs/.cjsextensions. include/src/index.tspatch-semantics export: extracted the privateapplyPatchesbody into the exported pure functionapplyEntryPatches(data, patches, warn)(the method delegates to it) and exported the!!jsYAML dialect asentryListSchema, sodsh --dump-configcomposes and prints exactly what the include would mount without booting a tree. Behavior-preserving for mounting; the extraction exists because config tooling must never reimplement (and drift from) the patch algorithm.applyEntryPatchesalso indexes eachinserted entry as it is added, so a later patch in the same list can configure or disable a row an earlier patch inserted; upstream built the id index once before the patch loop, leaving inserted rows silently unpatchable. That matters becausedshcomposes one shared base (apps/cli/config/base.cordis.yml) with a surface overlay, an optional--configoverlay, and the personal~/.dsh/config.yamlas sibling patch lists at one include level — patches never cross an include boundary, so surface-only rows would otherwise be unreachable from user config. Covered bypackages/ui/app-boot/tests/config-reload.spec.ts.include/src/index.tsserialized child-tree mutation andhmr/src/index.tsmain-watcher initial-scan suppression: every Include child-tree mutation (initial apply, refresh,internal/updatepatch re-application) runs through one per-Include queue, because the group's transactionalupdateis not reentrant — two concurrent applies interleave create and rollback on the same entries and strand the Include fiber without ever settling. The HMR main watcher passesignoreInitial: true: the initial scan re-announced files boot had just consumed, and itsaddfor a config file refreshed an Include mid-initial-apply; once serialized, a failing initial apply's rollback disposed HMR, whose teardown drain waited on the queued refresh sitting behind that same apply — a deadlock that exited 13 with no diagnostic and the TUI's terminal state stranded.registerConfig()keeps its ownignoreInitial: falsewatcher because a personal config present at registration must apply once. Covered by thedshinvalid-provider PTY case inapps/cli/tests/tui-keyless-smoke.e2e.ts.
Sync procedure
To update a vendored package from upstream:
- In the upstream workspace, note
git rev-parse HEADof the relevant submodule. - Copy the package's
src/(andbin.js,README.md,LICENSEif changed) over the vendored directory. - Re-apply the local modifications listed above (or drop them if upstream made them unnecessary — update the log either way).
- Update the version and commit hash in the manifest table.
- Run
pnpm install && pnpm run test && pnpm run buildat the repo root.