When a visible shared file became a content duplicate, reconciliation emitted
a removal notice and discarded its metadata. Its durable state then remained
remove, while the metadata fast path accepted only candidates without any
previous record. Every subsequent unchanged pass therefore reread the file,
even after a later read had populated the cache. This also affected the path
where an edited earlier winner made an unchanged later file a duplicate.
Retain the still-present duplicate's metadata with the removal's deferred
version update. Pass the already-cached state when removal follows an unchanged
metadata probe, and the newly read state when the duplicate's content changed.
Apply this metadata only when the associated removal survives rendering, using
the existing exact-transition filter; confirmed absence still clears metadata.
Allow both never-rendered candidates and previous removals to skip content reads
when unchanged provider metadata and an earlier kept digest identify the file as
a duplicate. Continue probing metadata. Once the winner changes or disappears,
read and promote the shared file normally, then suppress unchanged follow-ups.
Document the metadata guarantee in the local cache types and bilingual README.
Model-visible messages, source records, ordering, and byte budgets stay the same.
Add four production-plugin regressions covering duplication after either file
changes, then promotion after the winner is edited or deleted. They inspect the
provider's actual content reads across two unchanged passes and require exactly
one shared read for promotion, rather than trusting the context's self-report.
Validation:
- All four new cases failed before the fix because each unchanged pass reread
the shared file despite unchanged metadata.
- pnpm exec vitest run packages/context/agent-instructions/tests/agent-instructions.spec.ts -t 'caches a removed duplicate': 4 passed after the fix.
- README pairing was rerecorded; git diff --check passed.
- The removal regression narrows its source kind and compares the change as a
plain typed value; no matcher carrying any is assigned into an expected record.
- Type-aware Oxlint passed for the three affected packages and the new
metadata-failure fixture; focused cache regressions passed again after the
assertion correction.
A content-deduplicated shared file has no visible change record. If its
metadata became unavailable while the harness-home winner changed or vanished,
the reconciler skipped the failed candidate and still published the winner's
replacement or removal. Guidance that remained in the shared file disappeared
from the model's effective instructions. Content-read failures had the same
partial-group behavior even when metadata probing succeeded.
Rollback the probed candidate group whenever any metadata probe is unavailable
or its bounded content read cannot complete. Include candidates without a
visible predecessor and candidates previously removed as duplicates. Reuse
one rollback operation for proposed messages, deferred version updates,
metadata mutations, and retained digest bookkeeping, so a later successful pass
can reconstruct the complete group independent of cache warmth.
A filesystem path can also appear as a project instruction when the Harness
home is the project root. Releasing observed paths on rollback let that later
alias publish the very content withheld by the failed global group. A transient
failure of the first member also left failed and unvisited paths unreserved.
Factor the existing scope-to-file path calculation and reserve every eligible
member's absolute path for the rest of that pass when its group rolls back.
Project aliases therefore cannot bypass group preservation, without additional
filesystem operations. The shared resolver keeps probing and rollback path
calculations identical. Add metadata, content-read, and one-shot first-member
failure regressions for this supported same-file layout and its recovery.
Keep cancellation propagating to the caller. Keep explicit baseline-budget
exclusions outside observation rollback, because their removals are deliberate
policy decisions. No new session fields, baseline identities, polling, retries,
or process-global state are needed. Clarify the metadata/content and hidden
candidate guarantee in the bilingual README.
Add four real-plugin outage/recovery tests for metadata or content failure while
the retained home file is edited or deleted, plus a cold-cache reconciliation
regression. Add an authored headless session scenario with instance-local
provider failure injection: request two keeps baseline A while shared metadata
is unavailable; request three receives the home B replacement and shared A
addition together after recovery. The fixture restores the provider method on
disposal and asserts the real home/shared files before restoring observation.
Declare its test-only filesystem dependency without changing runtime packages.
Regenerate the persistence catalog's source anchors after the shared path
helper import shifts AgentInstructionSource by one line. Its persisted fields
and SHA-256 type fingerprint remain unchanged; no type acknowledgement or
Session migration is required for a source-location-only update.
Validation:
- All five new regressions failed with the intended partial-group emissions
before the source fix.
- pnpm exec vitest run packages/context/agent-instructions/tests/agent-instructions.spec.ts -t 'hidden duplicate has|cold global group|unavailable active candidate|unavailable scope': 8 passed.
- pnpm exec vitest run packages/context/agent-instructions: 174 passed.
- The focused authored headless snapshot refresh and replay passed.
- Restoring the early skip for an unavailable hidden candidate makes the
headless snapshot fail with the premature harness-home replacement.
- README translations were updated together and their pairing rerecorded.
- Three project-alias regressions failed on the initial group rollback fix,
then passed after reserving failed and unvisited candidate paths.
- pnpm exec vitest run packages/context/agent-instructions/tests/agent-instructions.spec.ts -t 'frozen across a project alias|hidden duplicate has|cold global group|caches a removed duplicate': 12 passed.
A baseline can deduplicate identical harness/shared instructions before the
byte renderer omits their retained winner. Removing every deduplicated scope
from excludedScopes then made the shared copy eligible on the next unchanged
step. It injected the broader instructions the baseline had deliberately
omitted, contradicting the retained-baseline budget policy. Project sibling
copies had the same problem.
Allow a duplicate to leave the exclusion set only when the included baseline
contains its candidate group and trimmed digest. Match the digest as well as
the group: a different sibling may fit the budget while the duplicate's winner
does not. Use the included files' original content so truncated and empty
represented winners retain their ordinary duplicate-promotion behavior.
Copies of omitted content inherit that omission for the visible baseline.
Duplicates of represented content remain probed and can be promoted when their
winner changes or disappears. Keep both README languages, the existing shared
root decision, and the local result-field documentation consistent with this
rule. No durable event fields or baseline identities change.
Add plugin regressions for identical global copies across repeated steps and
resumes, and an omitted project digest with a different retained sibling. Add
an authored recorded-session scenario that runs the shipped headless profile
with identical 1900-byte global files and a 700-byte budget: its two model
requests retain one project-only baseline and never inject the shared copy.
Validation:
- Both new plugin regressions failed on the reviewed implementation with two
contexts instead of one, then passed with the fix.
- pnpm exec vitest run packages/context/agent-instructions/tests/agent-instructions.spec.ts -t 'duplicate budget-omitted|different digest fits|content-deduplicated shared|changed shared file': 4 passed.
- pnpm run test:snapshot:refresh snapshots/session/headless.snapshot.ts -t agent-instructions-budget: passed.
- pnpm run test:snapshot snapshots/session/headless.snapshot.ts -t agent-instructions-budget: passed.
- Reverting only src/index.ts to the reviewed head makes that snapshot fail
with an extra agents-global set and 304 bytes of omitted global content.
- pnpm run test:snapshot scripts/session-snapshot-corpus.corpus.ts: 3 passed.
- Scoped bilingual pairing was rerecorded and verified; git diff --check passed.
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.
The removed dshHome row key still redirected instruction loading away from
DSH_HOME. Schemastery's non-strict object validator preserves unknown keys,
and resolveConfig forwarded the validated row wholesale to the library
discovery normalizer, which intentionally accepts an already-resolved home.
Removing the public field and schema entry therefore left the old runtime
override active and contradicted the upgrade guide.
Copy only the declared discovery controls when normalizing a plugin row.
Home resolution now follows process policy even when the row retains a stale
dshHome or another undeclared home field such as agentsHome. This allowlist
also applies when the shared-agents-root stack layer adds its internal home
parameter, without widening the deployment-facing configuration.
Keep the exported discovery and baseline-loading helpers' explicit home
parameter: it carries a library owner's already-resolved path, rather than
granting a plugin row its own home. Extend that regression through baseline
loading to preserve this distinction. Document the ignored stale row key in
both package READMEs and refresh their pairing record. No Session format,
candidate ordering, budget, or instruction rendering changes are required.
Add a production Cordis/schema regression that mounts a row with both stale
home keys and checks model-visible baseline and refresh content. Before the
fix, its baseline contained the stale row instruction instead of the process
home instruction. Both stages now use the process home, and the same test
will reject an agentsHome leak after propagation into the upper stack layer.
Validation:
- pnpm exec vitest run packages/context/agent-instructions/tests/agent-instructions.spec.ts -t 'ignores a stale row home'
failed before the source fix with the intended content mismatch.
- pnpm exec vitest run packages/context/agent-instructions/tests/agent-instructions.spec.ts -t 'ignores a stale row home|uses an already-resolved home'
passed both focused tests after the fix.
- pnpm exec vitest run packages/context/agent-instructions/tests/agent-instructions.spec.ts
passed all 157 tests with both stale row keys present.
- pnpm exec tsc -b packages/context/agent-instructions/tsconfig.json passed.
- pnpm run doc-sync passed all 42 gates.
- pnpm run test:snapshot snapshots/session/headless.snapshot.ts -t 'agent-instructions'
passed the selected recorded-session replay.
- pnpm run verify-translation-pairing --write packages/context/agent-instructions/README.md
refreshed the bilingual record; doc-sync verified it.
- git diff --check passed.
`resolveDiscoveryConfig` accepted `DiscoverOptions.dshHome` but resolved the
home from the environment alone, so a caller passing an already-resolved home
silently received the process home while the option comment claimed otherwise.
It now normalizes the passed value, which keeps the plugin's mount-time
resolution authoritative for discovery and reconciliation. A test pins that an
owner-resolved home wins over `$DSH_HOME`.
Review follow-ups:
- The guide claimed the removed field was the last per-row home override;
other providers (skill-filesystem, shell-env, credentials-local,
subagent-dsh-sdk, attachment-local) keep their own fields, so it now states
that the removal covers the instruction loader, and step 1 also names the
home-level `$DSH_HOME/cordis.patch.yml` layer.
- `DiscoverOptions` regains an already-resolved `dshHome` that `apply()` passes
from its mount-time `resolveConfig()`, so baseline discovery and
reconciliation share one resolution point instead of reading the environment
separately.
- The package README says the loader reads the process home rather than "the
same home as sessions, skills, and storage", and the resolver Agent Note
states what the skill provider's fields actually select.
- `discoverBaselineInstructionFiles` JSDoc no longer lists a removed option.
The agent-instructions row accepted `dshHome`, letting one consumer read a
different harness home than sessions, skills, and storage. Home is process
policy: the plugin now resolves `$DSH_HOME`, else `~/.dsh`, through
`resolveDshHome()` from `@deepseek-ai/dsh-home-paths`, and the config field and
the library's explicit-home option are gone.
Tests and both snapshot fixtures pin the home with `vi.stubEnv('DSH_HOME', ...)`
and the harness's existing `DSH_HOME` export instead of row configuration.
`dsh-skill-filesystem` keeps its own `dshHome`/`agentsHome` fields.
The removed cordis.yml key gets an upgrade guide.
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.
* feat(i18n): key translation pairing records by heading section
Replace the whole-file blob hashes in *.i18n.yaml with one entry per
heading section that still holds language-specific content, keyed by the
English heading-slug path. Blocks identical across the pair (generated
table rows, code blocks, shared regions) stay out of the hash, so edits to
different sections and identical regenerations merge with Git's default
text merge, including on GitHub.
Remove the dsh-translation-pairing merge driver, its resolver command and
installer wiring; gen-translation-brief recovers last-confirmed text from
commit history. Migrate all active records, archive the superseded Agent
Note, and record the replayed conflict reduction in the new one.
* test(i18n): refresh translation prompt snapshot for the terminology note
* feat(i18n): exclude only code blocks and generated regions from pairing records
Replace byte-equality masking with an explicit rule: a record hash skips
fenced code blocks and BEGIN/END GENERATED regions, the two kinds of
content the pairing gate already requires to be identical on both sides.
Move generated data into regions written into both pages:
gen-config-catalog renders both languages and the record with per-package
regions labelled by code identifiers; gen-module-graph keeps its table in a
region; gen-doc-graphs splices the event matrix regions into the Chinese
page. Add a generic region splice beside the region grammar.
Keep the master refresh separate from the V3-to-V4 lossless-conversion and
checkpoint JSON fixes so the release baseline and each feature change remain
independently reviewable. This checkpoint extends the already-published
4435f6f519 merge before the #4583 child is rebased.
Integrate exactly master 5ec3ffe337062ab5bb4955933008afe8d0a0b915. Its #4156
changes add watched file/document refresh, filesystem watch readiness,
directory expansion restoration, and chat bottom-follow fixes. They update
shared filesystem and Client consumers without changing Session format or
snapshot generation.
The merge has no conflicts. Its 114-file scope and line counts match upstream;
code and prose additions/removals are unchanged. Only the two translation
pairing records incorporate the release branch's retained V4 documentation.
Validation: 762 owner tests passed (1 platform skip), 11 focused Web tests
passed, full build passed, and all 41 doc-sync checks passed. Runtime closure
and Client package checks passed; fs-local publint completed with the existing
source-wildcard export warning.
Give durable user context its producer-owned source kind instead of a generic plugin wrapper. The V3-to-V4 conversion preserves the frozen rename and collision rules, all own JSON metadata, event coordinates, and historical request-only kinds. Native catalog and JSONL admission retain source validation so unknown attribution can survive without its producer while malformed owned sources are refused.
Use an explicit user-only RequestUserInput for auxiliary LLM prompts that do not own durable message identity. Auto Review and compaction remove their request-only source registrations while retaining prompt content and freezing; provider serializers and projections keep the same wire data, assistant replay isolation, image ordering, and tool correlation. Session writes, Agent delivery, and recorded title requests continue to require identified durable messages.
Bind the saved compatibility policy only to user sources and qualify tmux location attribution without weakening fixed system, model, or tool semantics. Rebuild the current inventory and unpublished V4 acknowledgement on the actual tool-role parent, preserving its rationale and all accepted historical records. The catalog format version records tooling metadata; Session remains V4. Developer-role policy and activation belong to their own feature.
The latest-master Messages adapter, serialization, request-input typing, EpochHeader reservation, and attribution checks pass 103 tests across five files. The retained V3 Messages input-history snapshot passes through the source profile; Host/Client typechecks and all 41 documentation gates pass. The generated comparison records four changed roots and 447 unchanged type fingerprints. Latest-master ARIA changes are retained, with producer source labels applied independently; new Messages fixtures retain their producer names as declared source kinds and use the fixed system-message helper; recorded Session generations are unchanged by this regrouping.
Co-authored-by: Tianyi Cui <[email protected]>
Represent tool results as tool-role messages and migrate released wrappers
without losing outer own JSON fields, call identity, error state, or writer
field order. Refuse nested or wrapper-only information that has no lossless
current representation before successor publication.
Validate current event relationships directly instead of projecting V4 data
back into V3. Keep lifecycle, PTC, title/command, compaction, system-field and
retired-syntax checks on catalog restoration and ordinary JSONL opens. The
Worker JSON equality helper must inspect own keys so it cannot admit PTC
arguments that Node rejects.
Keep the source role change, required native admission and consumer fixture
updates in one complete review layer. Explain shared-type changes in the
persistence record and the native reader rationale in its existing note.
Use checked branded fixture identities and discriminant narrowing instead
of unknown double casts; fixture values and assertions remain intact.
This recomposes the original tool-role work by _Kerman with the independently
validated migration and admission repairs by Tianyi Cui. Frozen generations
and snapshot input version selection are retained.
Co-authored-by: Tianyi Cui <[email protected]>