Files
deepseek-harness/.agents/notes/implemented/architecture/2026-07-24-single-harness-home-resolver.md
T
Tianyi Cui 21d0c2e29c feat(agent-instructions): load the shared agents-root AGENTS.md
The user-global instruction scope now reads two roots: $DSH_HOME/AGENTS.md
first, then the shared agents root that other agent tools use
($DSH_AGENTS_HOME, else ~/.agents). Both resolve from the process environment
through @deepseek-ai/dsh-home-paths; no config field is added, because the
stack base (PR #5718) removed the per-row home override.

The two files are candidates of one candidate group: identical trimmed content
renders once from the harness home, an unobservable candidate preserves the
group's last-good state, and reconciliation probes both roots so external
edits, removals, and new files become visible change notices. A candidate
dropped as a duplicate keeps being reconciled, so deleting or changing the
retained candidate promotes it. The harness-home file keeps its recorded
`user-global` scope key; the shared file carries `agents-global`.

A user-global delta states `These user-global instructions apply to all work.`
instead of naming its reconciliation scope, and a project path whose first
component would collide with a user-global scope directory renders with a
leading `.` so it keeps its own scope.
2026-10-05 17:20:30 +08:00

4.0 KiB

Agent Note: One harness home resolver

Status: implemented

English | 中文

Problem

The harness had two inconsistent conventions for "where does DeepSeek Harness user data live":

  • @deepseek-ai/dsh-home resolved configured ?? $DSH_HOME ?? ~/.dsh.
  • @deepseek-ai/dsh-home-paths shipped a second resolveDshHome with the same precedence plus tilde expansion — a near-duplicate of dsh-home that no gate flagged because the two lived in different packages and had already drifted (only one expanded tildes).

Two resolvers for the same cross-cutting fact meant there was no single home policy.

Decision

One resolver owns the harness home, in @deepseek-ai/dsh-home-paths, single-root:

explicit configured path  >  $DSH_HOME  >  ~/.dsh

An empty or whitespace-only $DSH_HOME is treated as unset; otherwise resolve('') would silently place the home at the current working directory. The harness keeps all user data under one root; there is no XDG config/data/cache split. dshHomePath(...segments) joins deployment-owned children onto that root, and dsh-app-boot exposes it to Loader !!js config expressions before mounting entries, so shipped compositions derive sessions and storages without copying the resolver. dshHomeDisplay() names a resolved root symbolically for user-facing paths — ~/.dsh for the default home, $DSH_HOME for any configured home — so the user-global AGENTS.md label never leaks an absolute machine path. It replaces agent-instructions's bespoke default-vs-$DSH_HOME check. Home is process policy rather than row configuration: dsh-agent-instructions accepts no home override, so a composition cannot point instruction loading at a root that sessions, skills, and storage would not read. resolveAgentsHome() and agentsHomeDisplay() apply the same precedence, tilde expansion, and symbolic labelling to the shared agent configuration root ($DSH_AGENTS_HOME or ~/.agents), which stays outside the harness home because other agent tools share it (shared agents root); dsh-skill-filesystem still accepts its own dshHome and agentsHome fields, which select its own skill roots and resolve through the shared resolver in the instruction-loading change.

dshCachePath(...segments) derives paths below the resolved home's cache directory. An initial { dshHome } option preserves a provider's explicit home override. It resolves paths without creating directories; callers own directory creation. attachment-local uses this helper for regenerable request-image variants while retaining durable attachment objects in their versioned storage tree, so clearing the cache cannot remove Session attachments. Existing request-image cache entries are left in place and are not read or copied; a cache miss regenerates the variant from its durable attachment.

@deepseek-ai/dsh-home is deleted. Home-owning providers and boot packages import resolveDshHome from dsh-home-paths; composition bundles contain only the resolved configuration rows.

dsh-telemetry and its separate home policy are absent under the SDK project toolchain removal, leaving this resolver as the sole home policy.

Alternatives considered

Leave the two resolveDshHome copies in place. They had already drifted (one expands tildes, one didn't) and encode the same cross-cutting fact twice. Consolidation is the point of the util/ layer; a duplicate resolver is a latent divergence bug.

Adopt XDG (honor $XDG_CONFIG_HOME, or split config/data/cache into separate trees). Considered and dropped in favor of one obvious root. A single $DSH_HOME || ~/.dsh ground truth matches ~/.claude / ~/.aws, needs no per-kind reclassification of every ~/.dsh consumer, and leaves no resolver asymmetry to reconcile.

Consequences

  • One home fact, one resolver. dsh-home-paths is the sole owner; the util/ group loses the home package.