diff --git a/docs/agent-lifecycle.i18n.yaml b/docs/agent-lifecycle.i18n.yaml
index 3766d07124..0a03ee7f38 100644
--- a/docs/agent-lifecycle.i18n.yaml
+++ b/docs/agent-lifecycle.i18n.yaml
@@ -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 docs/agent-lifecycle.md
-agent-lifecycle.md: 529676a7e5bf9eaba7743cb7794edb4bc5528cb5
-agent-lifecycle.zh.md: 9dc89641552474b1c4970885c82fbfefd4b15c2b
+agent-lifecycle.md: 6ffe3c2b47e766ac985b3192a1c08787821fc46e
+agent-lifecycle.zh.md: c4b2fa70dbdb0487c3871b6e65227eac8b062e24
diff --git a/docs/agent-lifecycle.md b/docs/agent-lifecycle.md
index 529676a7e5..6ffe3c2b47 100644
--- a/docs/agent-lifecycle.md
+++ b/docs/agent-lifecycle.md
@@ -48,7 +48,7 @@ sequenceDiagram
Driver-->>SDK: agent/assistant-stream committed end
Driver->>Hooks: agent/request-error waterfall
Hooks-->>Driver: return retry action or preserve the original error
- Note over Driver,LLM: retry in the open step: prepare and reconcile the same rendered assembly; no repeated pre-step or users
+ Note over Driver,LLM: retry in the open step: prepare and reconcile the same rendered assembly without repeating pre-step or users
else model request succeeded
Driver->>Session: assistant/message
Driver-->>SDK: agent/assistant-stream committed end
diff --git a/docs/agent-lifecycle.zh.md b/docs/agent-lifecycle.zh.md
index 9dc8964155..c4b2fa70db 100644
--- a/docs/agent-lifecycle.zh.md
+++ b/docs/agent-lifecycle.zh.md
@@ -50,7 +50,7 @@ sequenceDiagram
Driver-->>SDK: agent/assistant-stream committed end
Driver->>Hooks: agent/request-error waterfall
Hooks-->>Driver: return retry action or preserve the original error
- Note over Driver,LLM: retry in the open step: prepare and reconcile the same rendered assembly; no repeated pre-step or users
+ Note over Driver,LLM: retry in the open step: prepare and reconcile the same rendered assembly without repeating pre-step or users
else model request succeeded
Driver->>Session: assistant/message
Driver-->>SDK: agent/assistant-stream committed end
diff --git a/docs/subsystems/session.i18n.yaml b/docs/subsystems/session.i18n.yaml
index 89742ad1ec..8671cce6c9 100644
--- a/docs/subsystems/session.i18n.yaml
+++ b/docs/subsystems/session.i18n.yaml
@@ -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 docs/subsystems/session.md
-session.md: c753c4ee3e174a937eb136ffb2c9142b197193f9
-session.zh.md: 317d67823f8abfcc2563044bdac404b32d45b61c
+session.md: 415672da8415ccd5ea8d1e2f20a005ebf6cd57de
+session.zh.md: d2e4cf2e9c3bd9f7bdbf1e88b3d4686ed3e23e68
diff --git a/docs/subsystems/session.md b/docs/subsystems/session.md
index c753c4ee3e..415672da84 100644
--- a/docs/subsystems/session.md
+++ b/docs/subsystems/session.md
@@ -56,13 +56,14 @@ interface SessionEventMap {
/**
* The rendered system prompt on the model-visible surface. The loop appends
* the first one as surface node 0 before the step's first `user/message`.
- * When the rendered prompt changes it replaces the latest system node
- * (`surfaceOp: { op: 'replace' }` over exactly that node) or, on a route
- * whose `request/context` declares `systemPromptUpdate: 'in-history'` and
- * inside a continuing request series, appends the changed prompt after the
- * cached history, so the latest system node is the effective prompt and
- * every request stays derived history. Empty `message.content` records "no
- * system prompt" and projects to no message.
+ * A prepared in-history route can append nonempty changes in a continuing
+ * series. An incapable route or new series normalizes text to the first system
+ * node. Normalization empties nonempty later nodes, then rewrites the head if
+ * needed, through logged per-node replacements. An empty rendering always
+ * clears all active system nodes, leaving no older instructions model-visible.
+ * Empty later nodes are dormant and project to no message; an empty head with
+ * no active later node records "no system prompt". Restored nonempty text follows
+ * the same route and series rule; empty nodes never restore older text.
*/
'system/message': { turn: number; step: number; message: SystemMessage }
/**
@@ -127,8 +128,8 @@ interface SessionEventMap {
/**
* Route metadata for the next request, logged only when the route, capacity,
* or system prompt update mode changes. It does not participate in request
- * reconstruction or header equality; the loop reads the latest snapshot's
- * `systemPromptUpdate` when it decides how to commit a changed system prompt.
+ * reconstruction or header equality. Prompt admission uses the bound prepared
+ * call's capability, not this snapshot from an earlier request.
*/
'request/context': RequestContext
/**
diff --git a/docs/subsystems/session.zh.md b/docs/subsystems/session.zh.md
index 317d67823f..d2e4cf2e9c 100644
--- a/docs/subsystems/session.zh.md
+++ b/docs/subsystems/session.zh.md
@@ -56,13 +56,14 @@ interface SessionEventMap {
/**
* The rendered system prompt on the model-visible surface. The loop appends
* the first one as surface node 0 before the step's first `user/message`.
- * When the rendered prompt changes it replaces the latest system node
- * (`surfaceOp: { op: 'replace' }` over exactly that node) or, on a route
- * whose `request/context` declares `systemPromptUpdate: 'in-history'` and
- * inside a continuing request series, appends the changed prompt after the
- * cached history, so the latest system node is the effective prompt and
- * every request stays derived history. Empty `message.content` records "no
- * system prompt" and projects to no message.
+ * A prepared in-history route can append nonempty changes in a continuing
+ * series. An incapable route or new series normalizes text to the first system
+ * node. Normalization empties nonempty later nodes, then rewrites the head if
+ * needed, through logged per-node replacements. An empty rendering always
+ * clears all active system nodes, leaving no older instructions model-visible.
+ * Empty later nodes are dormant and project to no message; an empty head with
+ * no active later node records "no system prompt". Restored nonempty text follows
+ * the same route and series rule; empty nodes never restore older text.
*/
'system/message': { turn: number; step: number; message: SystemMessage }
/**
@@ -127,8 +128,8 @@ interface SessionEventMap {
/**
* Route metadata for the next request, logged only when the route, capacity,
* or system prompt update mode changes. It does not participate in request
- * reconstruction or header equality; the loop reads the latest snapshot's
- * `systemPromptUpdate` when it decides how to commit a changed system prompt.
+ * reconstruction or header equality. Prompt admission uses the bound prepared
+ * call's capability, not this snapshot from an earlier request.
*/
'request/context': RequestContext
/**
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index 68476f1452..8fcfcfdce7 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -1347,7 +1347,7 @@ function renderLifecycle(): string {
` Driver-->>SDK: ${mermaidCode('agent/assistant-stream')} committed end`,
` Driver->>Hooks: ${mermaidCode('agent/request-error')} waterfall`,
' Hooks-->>Driver: return retry action or preserve the original error',
- ' Note over Driver,LLM: retry in the open step: prepare and reconcile the same rendered assembly; no repeated pre-step or users',
+ ' Note over Driver,LLM: retry in the open step: prepare and reconcile the same rendered assembly without repeating pre-step or users',
' else model request succeeded',
` Driver->>Session: ${mermaidCode('assistant/message')}`,
` Driver-->>SDK: ${mermaidCode('agent/assistant-stream')} committed end`,