AVALANCHEAI
Phase 1 liveUpstream
DocsForkFork strategy9 min read

Fork Strategy

This fork turns DeepSeek Harness into Avalanche AI by composition, not replacement. This document records the fork mechanics, the extension pattern, the upstream-merge policy, and the decisions that keep the two codebases reconcilable.

Repository topology

RemoteURLPurpose
upstreamhttps://github.com/deepseek-ai/deepseek-harness (local mirror /root/dsh-upstream)Original project; never pushed to
origin/root/avax-origin.gitThis fork; all Avalanche work lands here

The fork is a full clone with history. master tracks upstream release tag dsh-v0.2.2-rc.1 unchanged; all Avalanche work is additive on top. Upstream attribution and the MIT license are preserved: LICENSE, package manifests (repository, license: MIT), and the README attribution line are untouched.

Extension pattern

The fork extends the monorepo in place, with a new package group and one upstream registration:

  1. packages/avalanche/avalanche-* — 19 new packages, all names @deepseek-ai/dsh-avalanche-* matching their directories so scripts/gen-tsconfig-paths.ts generates aliases automatically. Version matches upstream: 0.2.2-rc.1.
  2. packages/util/official-bundles/src/bundles.ts — one entry in PROFILE_TEMPLATES:
    avalanche: { bundles: ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-avalanche-app'] },
    
  3. Generated artifacts: tsconfig.base.json references and pnpm-lock.yaml.

Documentation-gate registries also gain the new group's audited entries: scripts/doc-standard.spec.ts (README kind for avalanche-schemas), scripts/verify-subsystem-pages.ts (group exemption), scripts/verify-package-readme-model-experience.ts (Model Experience allowlist), and scripts/gen-cordis-catalog.ts (service/event walk exemptions with their documentation owners). These are test-and-doc contract registries, not runtime behavior. Every other change is new files under packages/avalanche/ plus documentation and generated catalogs.

Why not a plugin outside the monorepo

The package group lives inside the monorepo because:

  • The workspace build, TypeScript project references, Vitest alias resolution, and Loader package resolution all key off the workspace globs. An out-of-tree plugin would need a separate build, a separate lockfile, and a profile installation step, and would still not participate in the repo's own gates.
  • The domain packages are plugin rows mounted by one bundle patch; they must resolve as workspace packages from the profile fixture and from the built CLI.
  • Keeping the packages in-tree makes the upstream merge policy trivial: upstream never touches packages/avalanche/, and the fork never touches upstream packages beyond the one registration.

Package inventory (19)

GroupPackages
Foundationsavalanche-schemas, avalanche-context, avalanche-memory
Securityavalanche-policy, avalanche-evidence, avalanche-verification
Runtimeavalanche-network, avalanche-tooling, avalanche-agents, avalanche-orchestrator
Domainsavalanche-domain-developer, -security, -devops, -data, -protocol, -testing, -defi
Surfaceavalanche-cli, avalanche-app

Seven domains are separate packages because the build emits at most two entries per package (index, startup) and because each domain is independently mountable and independently testable. They share one registry and one runtime.

Build and test integration

  • scripts/gen-tsconfig-paths.ts scans workspace packages and generates path aliases; no manual alias edits.
  • Package tsconfig.json references are generated from manifest dependencies. Re-run the generator after any dependency change.
  • Tests resolve workspace packages from source through vite-tsconfig-paths; Vitest does not require built lib/ output.
  • The package group is included in the root workspace glob packages/*/*, so install, typecheck, and test discovery are automatic.

Upstream merge policy

  1. Never edit upstream package sources. The only upstream runtime file the fork edits is bundles.ts; if upstream restructures that file, re-apply the one-entry change. Documentation-gate registries are edited only to register the new group's audited entries.
  2. Never fork a core package to add an Avalanche hook. If a hook is needed, it must be expressible through an existing Cordis event, service, or config knob. If it is not, the gap is documented and upstreamed separately.
  3. Keep packages/avalanche/ self-contained: it may import upstream packages and their public types only.
  4. Conflicts are resolved by rebasing master onto upstream and re-running the avalanche test suite. Because the avalanche group is untouched by upstream, conflicts are expected only in generated files (tsconfig.base.json, pnpm-lock.yaml) and the single bundles.ts entry.
  5. Version numbers track upstream release lines (0.2.2-rc.1) so the fork is installable beside upstream packages without peer-range drift.

Decisions and their reasons

DecisionReasonRejected alternative
Extend in place, not rewritePreserves upstream's plugin system, boot, CLI, session, and tool pipeline; smallest merge surfaceStandalone app with its own runtime (loses every upstream benefit and duplicates the harness)
One bundle, one insert blockThe entire runtime is one patch layer; row order stays readable and every row is patchable by a user layerSeven bundle patches or seven profiles (seven runtimes, explicitly forbidden)
Domains as data packagesRouting, prompts, and tool masks are data; adding a domain is a package and one rowDomain classes with bespoke logic (routing becomes model-dependent and untestable)
Capability policy, not tool-name policyA tool can be renamed or reached through a nested dispatch; capabilities describe what an action may doTool allowlists in prompts or schemas (bypassable; not enforcement)
Executor-level tools/pre-execute gateEvery caller passes through it; agent, nested dispatch, and future HTTP surfaces cannot skip itFiltering tool schemas or prompt instructions (advisory only)
Evidence separate from session logSession text is model output; evidence must record what the runtime actually didReceipts built from model text (would imply reasoning correctness)
Deterministic keyword routingModel-agnostic, explainable, testable; same objective always routes the same wayLLM-based router (provider-dependent, untestable, and non-deterministic)
JSONL hash chain, not a databaseTamper-evident, dependency-free, readable by an operator; sufficient for the phasePostgreSQL event store (Phase 3 candidate; unnecessary for the slice)
CLI under dsh --profile avalancheUpstream rule: only dsh profiles launch apps; package bins are forbiddenA new avalanche-ai bin (violates the application-entrypoint gate)
Commander parse → service → runnerUpstream startup/runner pattern; avoids a service-read race while rows activate asynchronouslyExecuting command bodies in the parse action (raced tree activation in the Loader test)

Deliberate deviations from the brief

  • No avalanche-ai executable. The brief names a CLI avalanche-ai .... The upstream application-launch rule forbids a separate Node application path, so the CLI is dsh --profile avalanche <command>. The command surface is the release surface; a wrapper script may be added later outside the repository if a literal binary name is required.
  • Dashboard deferred. The Vite + React + TypeScript dashboard (no Next.js) is Phase 4. Phase 1 ships the runtime, the CLI, and the API-shaped services the dashboard will consume.
  • No Rust package yet. Security-sensitive execution is TypeScript in this phase; Rust is planned for the signing/verification boundary in Phase 5, where the risk justifies it. The package boundary is already drawn so a Rust executor can replace the TypeScript one behind the same service contract.
  • No PostgreSQL, Redis, or vector search. The phase's persistence is file-based; memory recall is lexical. Both are deliberate simplifications with documented upgrade paths.

What to verify after an upstream merge

pnpm install --no-frozen-lockfile
pnpm run gen-tsconfig-paths
pnpm exec vitest run packages/avalanche

Expected: all avalanche specs pass, including avalanche-app/tests/loader-composition.spec.ts, which boots the real shipped cordis.patch.yml through the real Loader and asserts every row mounts.