fix(workspace-context): recompose the baseline on resume, skip only on remount

The mount-local baseline guard was seeded from "a baseline already exists in
the log", which a resumed session and a hot plugin remount both satisfy. That
made a resume skip its baseline, so offline AGENTS.md edits or removals never
reached the first resumed request — violating the documented resume contract.

Distinguish the two by agent/session-start: a startup or resume emits it before
the first step, while a remount attaches to an already-live session and never
witnesses it. Only a remount (no witnessed start, baseline already logged)
keeps the single logged baseline and skips; a resume falls through and
re-composes from current files. Adds a regression that resumes a session with
an offline baseline edit and asserts the fresh baseline reflects it.
This commit is contained in:
_Kerman
2026-07-27 01:35:36 +08:00
parent 9676696a31
commit 107e05e79b
5 changed files with 62 additions and 9 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-24-workspace-context.md: c1539840b404f8601c4a98917c8e0ca31257f688
2026-06-24-workspace-context.zh.md: 704a369c38a83c19d5a37faf54eab0c66e0e464f
2026-06-24-workspace-context.md: 9591e1ab169f1883e1831b50308c86e55599296b
2026-06-24-workspace-context.zh.md: 6317ad6e2e6c85768e74b5b2657903723ddf72d3

View File

@@ -32,7 +32,7 @@ At the first `agent/step` of an agent-loop instance, the plugin injects one sour
The injection becomes a durable `user/message` with plugin provenance. In the product spine workspace instructions are registered before the skills catalog, so their `agent/step` listener injects first. The loop drains both messages before deriving the first request.
A resumed agent creates a new loop instance and injects a baseline composed from current files before its first request. This permits current baseline content on resume without mutating an earlier history event.
A resumed agent creates a new loop instance and injects a baseline composed from current files before its first request. This permits current baseline content on resume without mutating an earlier history event. A resume and a hot plugin remount both face a log that already holds a baseline; they are told apart by `agent/session-start`, which a startup or resume emits before the first step while a remount attaches to an already-live session and never sees it. Only a remount (no witnessed lifecycle start, baseline already logged) keeps the existing baseline and skips; a resume re-composes.
The baseline is a user-role `<system-reminder>` with `Instructions from: <path>` sections and explicit authority and precedence language. This familiar model-facing frame avoids a harness-specific XML vocabulary. Project paths are root-relative and the user-global path is `~/.dsh/AGENTS.md` for the default home or `$DSH_HOME/AGENTS.md` for a configured home. A literal `</system-reminder>` inside file content is escaped. The package README owns the exact current [prompt shape](../../../../packages/context/workspace-context/README.md#prompt-shape).

View File

@@ -32,7 +32,7 @@ Status: implemented
该注入成为一条带插件来源的持久 `user/message`。在产品主干中,工作区指令的注册先于 skill 目录,所以其 `agent/step` 监听器先注入。循环会在派生第一次请求前 drain 这两条消息。
恢复 agent 会创建新的循环实例,并在其第一次请求前注入由当前文件组合的基线。这样,恢复时可以使用当前基线内容,而无需修改先前的历史事件。
恢复 agent 会创建新的循环实例,并在其第一次请求前注入由当前文件组合的基线。这样,恢复时可以使用当前基线内容,而无需修改先前的历史事件。恢复与插件热重挂都会面对日志中已存在基线的情况;二者通过 `agent/session-start` 区分:启动或恢复会在第一步前发出该事件,而热重挂附着到一个已存活的会话、永远不会看到它。只有热重挂(未见证生命周期开始、且日志已有基线)才保留既有基线并跳过;恢复则重新组合。
基线是一条 user 角色的 `<system-reminder>`,包含 `Instructions from: <path>` 章节,以及明确的权威性与优先级说明。这种熟悉的模型可见框架避免引入 harness 专用的 XML 词汇。项目路径相对于根目录;使用默认 home 时,用户全局路径为 `~/.dsh/AGENTS.md`,使用已配置 home 时则为 `$DSH_HOME/AGENTS.md`。文件内容中的字面量 `</system-reminder>` 会被转义。包 README 负责规定当前准确的[提示词形态](../../../../packages/context/workspace-context/README.md#prompt-shape)。