docs: make technical prose concrete
This commit is contained in:
@@ -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 packages/plan/plan-mode/README.md
|
||||
README.md: c404cfa73024804bc9f166cfb84fa5f87f723459
|
||||
README.zh.md: 275a87669802f38cd98886236ca63a09ffb3e410
|
||||
README.md: d7e19cc473695455df667cfd717703c2c303aafa
|
||||
README.zh.md: e89b75df184d2283452ab069a2d559650f15bfef
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Logged, per-agent plan collaboration state with deployment-owned guidance, direct `/plan [message]` entry and `/plan off` exit commands, and the reviewed `exit_plan_mode` exit. Plan mode is soft guidance; sandbox mode and approval policy remain independent enforcement axes.
|
||||
Logged, per-agent plan collaboration state with deployment-owned guidance, direct `/plan [message]` entry and `/plan off` exit commands, and the reviewed `exit_plan_mode` exit. Plan mode is soft guidance; sandbox mode and approval policy enforce restrictions independently and do not read or write plan state.
|
||||
|
||||
## Durable state
|
||||
|
||||
`plan/mode` (`{ active: boolean }`) is a log-only, whole-value-replace `SessionEventMap` member. `foldPlanMode(events)` returns the last logged value or `false`, so resume, fork, and compaction recover plan state directly from the session log. UIs observe committed flips through `session/event`.
|
||||
|
||||
`ctx.planMode.set(agent, active)` commits immediately when the agent is idle — no boundary would arrive until the next prompt, so the standalone `plan/mode` event lands at once — and holds a pending selection for the next accepted in-turn pre-step while the agent is running; it returns which of the two happened (`committed`/`queued`), a `cancelled` reversal, or a `noop`. `get(agent)` returns `{ active, pending? }`, separating the logged state shaping the current step from a user's mid-turn selection. Initial and continuation pre-step boundaries are covered; a same-step request-recovery retry reuses its frozen assembly and leaves the selection pending for the next pre-step. A changed user selection contributes one plugin-sourced `user/message` notice when the last logged request header described the other state (both commit paths).
|
||||
`ctx.planMode.set(agent, active)` appends the standalone `plan/mode` event immediately when the agent is idle, because no in-turn pre-step runs before the next prompt. While the agent is running, it holds a pending selection for the next accepted in-turn pre-step. It returns which happened (`committed`/`queued`), a `cancelled` reversal, or a `noop`. `get(agent)` returns `{ active, pending? }`, separating the logged state used to assemble the current step from a user's mid-turn selection. Initial and continuation pre-steps both apply pending selections; a same-step request-recovery retry reuses its frozen assembly and leaves the selection pending for the next pre-step. A changed user selection contributes one plugin-sourced `user/message` notice when the last logged request header described the other state (both commit paths).
|
||||
|
||||
## Model and human surfaces
|
||||
|
||||
@@ -22,7 +22,7 @@ The Web client consumes the plugin-owned `/plan` command; other entry points may
|
||||
|
||||
## Session projection
|
||||
|
||||
When the composition mounts `ctx.sessionProjections` ([`@deepseek-ai/dsh-session-projection`](../../session/session-projection/README.md)), this package registers the `plan` projection unit under an injected child. The unit folds two event kinds: a `command/run` record named `plan` with recorded `args` sets the wanted target (`off` → inactive, anything else → active), and `plan/mode` commits the logged state and clears it; every other event returns the same state reference. `view` derives `{ active, pending }`, where `pending` is true only while an outstanding selection differs from the logged state — a pure replay quantity, so host restarts, other tabs, and cold reads all recover it from the log alone (the `/plan` handler calls `set()` before any failing path, keeping the logged request and the run plane from forking). The key merges into `SessionProjectionMap` from `src/types.ts` (served to host consumers via `./types` and client aggregates via `./client`); the framework drives the unit and carriers serve the value on the history tail page and the `session/projection` push frame. Compositions without the registry are unaffected.
|
||||
When the composition mounts `ctx.sessionProjections` ([`@deepseek-ai/dsh-session-projection`](../../session/session-projection/README.md)), this package registers the `plan` projection unit under an injected child. The unit folds two event kinds: a `command/run` record named `plan` with recorded `args` sets the wanted target (`off` → inactive, anything else → active), and `plan/mode` commits the logged state and clears it; every other event returns the same state reference. `view` derives `{ active, pending }`, where `pending` is true only while an outstanding selection differs from the logged state — a pure replay quantity, so host restarts, other tabs, and cold reads all recover it from the log alone (the `/plan` handler calls `set()` before any failing path, so a failed handler cannot leave a recorded command without its plan selection). The key merges into `SessionProjectionMap` from `src/types.ts` (served to host consumers via `./types` and client aggregates via `./client`); the framework drives the unit and carriers serve the value on the history tail page and the `session/projection` push frame. Compositions without the registry are unaffected.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -91,8 +91,8 @@ Mode transitions do not change the tool catalog; plan arguments and review resul
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- Plan mode guides rather than enforces; deployments needing a hard boundary must combine independent sandbox and approval controls.
|
||||
- A pending selection made while idle is lost if the process exits before the next boundary, so the UI must reapply it.
|
||||
- Plan mode guides rather than enforces; deployments that need enforced restrictions must configure sandbox and approval controls independently.
|
||||
- A selection made after the turn's final accepted pre-step is lost if the process exits before another accepted in-turn pre-step, so the UI must reapply it.
|
||||
- Forked agents inherit logged plan state, while newly spawned agents begin inactive; there is no creation-time plan option.
|
||||
- A live child owned by another agent cannot open the `exit_plan_mode` review. The failed call tells the child to include the unresolved decision in its final result; durable fork lineage alone does not prevent a session resumed as a runtime root from opening the review.
|
||||
- Only the Web UI has a specialized `plan-review` renderer; another interaction provider may present the same request through its generic option flow.
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
按 agent(智能体)分别记录到日志的 plan 协作状态,提供由部署方配置的引导内容、用于直接进入的 `/plan [message]` 命令、用于直接退出的 `/plan off` 命令,以及经用户评审的 `exit_plan_mode` 退出方式。Plan mode 是软引导;沙箱模式和批准策略仍是独立的强制执行维度。
|
||||
按 agent(智能体)分别记录到日志的 plan 协作状态,提供由部署方配置的引导内容、用于直接进入的 `/plan [message]` 命令、用于直接退出的 `/plan off` 命令,以及经用户评审的 `exit_plan_mode` 退出方式。Plan mode 是软引导;沙箱模式和批准策略各自强制执行限制,且不读写 plan 状态。
|
||||
|
||||
## 持久状态
|
||||
|
||||
`plan/mode`(`{ active: boolean }`)是一个仅存在于日志中、每次以完整值替换的 `SessionEventMap` 成员。`foldPlanMode(events)` 返回最后记录的值,如果没有则返回 `false`,因此恢复、fork 和压缩(compaction)都能直接从会话日志恢复 plan 状态。UI 通过 `session/event` 观察已提交的切换。
|
||||
|
||||
`ctx.planMode.set(agent, active)` 在 agent 空闲时立即提交——下一个 prompt 之前不会有任何边界到来,因此独立的 `plan/mode` 事件当场落账——在 agent 运行中则持有待生效选择,并等待下一个被接受的轮内 pre-step;返回值区分 `committed`、`queued`、表示反转的 `cancelled` 和 `noop`。`get(agent)` 返回 `{ active, pending? }`,将塑造当前步骤的日志状态与用户的轮中选择分开。初始与续步 pre-step 边界都在覆盖范围内;同一步骤的请求恢复重试会复用已冻结的 assembly,并将该选择保留到下一个 pre-step。当最后记录的请求头描述了另一状态时,用户选择的变更会贡献一条插件来源的 `user/message` 通知(两条提交路径皆然)。
|
||||
`ctx.planMode.set(agent, active)` 会在 agent 空闲时立即追加独立的 `plan/mode` 事件,因为下一个 prompt 之前不会运行轮内 pre-step。agent 运行时,该方法会保留待生效选择,直到下一个被接受的轮内 pre-step。返回值区分 `committed`、`queued`、表示反转的 `cancelled` 和 `noop`。`get(agent)` 返回 `{ active, pending? }`,将用于组装当前步骤的日志状态与用户的轮中选择分开。初始与续步 pre-step 都会应用待生效选择;同一步骤的请求恢复重试会复用已冻结的 assembly,并将该选择保留到下一个被接受的轮内 pre-step。当最后记录的请求头描述了另一状态时,用户选择的变更会贡献一条插件来源的 `user/message` 通知(两条追加路径皆然)。
|
||||
|
||||
## 模型与人类交互
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
评审问题声明 `plan-review` 呈现意图,并指名 `Approve` 为表示批准的标签,因此有能力的 UI 会把计划呈现为一次决定而非通用问题;两种情况下该工具读到的回答完全相同。放弃审阅 —— 用户关掉请求改用说话 —— 会如实报告给模型,要求它留在 plan mode 中等待那条消息;其余每一种评审失败都保留 seam 自身的消息。
|
||||
|
||||
组合 `ctx.commands` 时,该包会注册 `/plan [message]`,并将参数恰好为 `off` 的情况保留给直接退出。不带参数的 `/plan` 会启用 plan mode;任何其他非空参数都会先启用 plan mode,再通过 `agent.steer()` 提交,因此它会在 plan 引导下成为下一步骤的常规已记录用户消息。`/plan off` 会选择停用状态,不发送模型输入;它还可以在启用 plan mode 的待处理选择到达请求边界之前将其取消。
|
||||
组合 `ctx.commands` 时,该包会注册 `/plan [message]`,并将参数恰好为 `off` 的情况保留给直接退出。不带参数的 `/plan` 会启用 plan mode;任何其他非空参数都会先启用 plan mode,再通过 `agent.steer()` 提交,因此它会在 plan 引导下成为下一步骤的常规已记录用户消息。`/plan off` 会选择停用状态,不发送模型输入;它还可以在启用 plan mode 的待处理选择由轮内 pre-step 追加之前将其取消。
|
||||
|
||||
Web 客户端使用该插件提供的 `/plan` 命令;其他入口可以直接驱动同一服务,无需定义第二套 mode 词汇。
|
||||
|
||||
## 会话投影
|
||||
|
||||
当组合挂载 `ctx.sessionProjections`([`@deepseek-ai/dsh-session-projection`](../../session/session-projection/README.md))时,本包会在一个注入的子插件中注册 `plan` 投影单元。该单元折叠两类事件:名为 `plan` 且携带已记录 `args` 的 `command/run` 记录会设置目标状态(`off` → 未激活,其余 → 激活),`plan/mode` 会提交已记录状态并清除该目标;其他任何事件都返回同一个状态引用。`view` 推导 `{ active, pending }`,其中 `pending` 仅在尚未落实的选择与已记录状态不同时为 true。该值完全由日志回放得出,因此 host 重启、其他标签页和冷读都能仅凭日志恢复它。`/plan` 处理器会在任何可能失败的路径之前调用 `set()`,避免已写入日志的请求与运行面分叉。key 由 `src/types.ts` 通过声明合并加入 `SessionProjectionMap`:host 消费方经 `./types` 获取,client 聚合经 `./client` 获取。框架负责驱动该单元,载体通过历史尾页和 `session/projection` 推送帧提供其值。未挂载注册表的组合不受影响。
|
||||
当组合挂载 `ctx.sessionProjections`([`@deepseek-ai/dsh-session-projection`](../../session/session-projection/README.md))时,本包会在一个注入的子插件中注册 `plan` 投影单元。该单元折叠两类事件:名为 `plan` 且携带已记录 `args` 的 `command/run` 记录会设置目标状态(`off` → 未激活,其余 → 激活),`plan/mode` 会提交已记录状态并清除该目标;其他任何事件都返回同一个状态引用。`view` 推导 `{ active, pending }`,其中 `pending` 仅在尚未落实的选择与已记录状态不同时为 true。该值完全由日志回放得出,因此 host 重启、其他标签页和冷读都能仅凭日志恢复它。`/plan` 处理器会在任何可能失败的路径之前调用 `set()`,因此处理器失败时不会留下缺少对应 plan 选择的已记录命令。key 由 `src/types.ts` 通过声明合并加入 `SessionProjectionMap`:host 消费方经 `./types` 获取,client 聚合经 `./client` 获取。框架负责驱动该单元,载体通过历史尾页和 `session/projection` 推送帧提供其值。未挂载注册表的组合不受影响。
|
||||
|
||||
## 配置
|
||||
|
||||
@@ -91,8 +91,8 @@ mode 转换不改变工具目录;plan 参数与评审结果按常规方式扩
|
||||
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- Plan mode 只进行引导,而不强制执行;需要硬边界的部署必须组合独立的沙箱与批准控制。
|
||||
- 如果进程在下一个边界之前退出,空闲时作出的待生效选择会丢失,因此 UI 必须重新应用它。
|
||||
- Plan mode 只进行引导,而不强制执行;需要强制限制的部署必须分别配置沙箱与批准控制。
|
||||
- 如果进程在另一个被接受的轮内 pre-step 之前退出,某轮最后一个被接受的 pre-step 之后作出的选择会丢失,因此 UI 必须重新应用它。
|
||||
- Fork 的 agent 会继承已记录的 plan 状态,新 spawn 的 agent 则从未激活状态开始;不存在创建时 plan 选项。
|
||||
- 由另一个 agent 所有的存活子级无法打开 `exit_plan_mode` 审阅。该调用失败时会提示子级在最终结果中包含尚未解决的决策;仅有持久化 fork 谱系并不会阻止恢复为运行时根的会话打开该审阅。
|
||||
- 只有 Web UI 具备专用的 `plan-review` 渲染器;其他交互提供方可以通过通用选项流程呈现同一请求。
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
/**
|
||||
* Plan mode is logged per-agent collaboration state: while active, a
|
||||
* deployment-owned guidance section shapes each model request, and
|
||||
* deployment-owned guidance section is included in each model request, and
|
||||
* `exit_plan_mode` presents the completed plan for user review, while the
|
||||
* `/plan off` command lets a user leave directly. Plan mode is independent of
|
||||
* sandbox mode and approval policy; those enforcement axes do not read or
|
||||
* write plan state.
|
||||
* `/plan off` command lets a user leave directly. Sandbox mode and approval
|
||||
* policy enforce restrictions independently and do not read or write plan
|
||||
* state.
|
||||
*
|
||||
* The state in force is folded from the session log (`plan/mode`, last one
|
||||
* wins), so resume and fork restore it without a live mirror. User selections
|
||||
* are held as pending intent until an in-turn step boundary. The service
|
||||
* projects pending intent into the proposed step assembly, then flushes it
|
||||
* remain pending until the next accepted in-turn pre-step. The service includes
|
||||
* the selected state in the proposed step assembly, then appends `plan/mode`
|
||||
* from `agent/pre-step` only when the step is accepted. Same-step request
|
||||
* retries reuse their assembly.
|
||||
*
|
||||
* The exit tool remains registered while plan mode is inactive so crossing a
|
||||
* boundary changes only the prompt section, not the request tool catalog.
|
||||
* The exit tool remains registered while plan mode is inactive, so entering
|
||||
* or leaving plan mode changes only the prompt section, not the request tool
|
||||
* catalog.
|
||||
*
|
||||
* Agent Note:
|
||||
* - .agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md
|
||||
@@ -97,7 +98,7 @@ function firstHeading(plan: string): string | undefined {
|
||||
|
||||
/**
|
||||
* Validate deployment-owned plan guidance. Missing, blank, non-string, or
|
||||
* unknown fields fail at plugin load rather than silently shaping nothing.
|
||||
* unknown fields fail at plugin load rather than being ignored.
|
||||
*
|
||||
* @param config Raw plugin config.
|
||||
* @returns A detached validated config.
|
||||
@@ -176,7 +177,7 @@ function planModeAtLastHeader(events: readonly SessionEvent[]): boolean | undefi
|
||||
}
|
||||
|
||||
/**
|
||||
* `ctx.planMode`: owns logged plan state, boundary application and narration,
|
||||
* `ctx.planMode`: owns logged plan state, applies and narrates selected state at step start,
|
||||
* the `plan:policy` section, the `/plan` command, and the stable exit tool.
|
||||
* UIs observe committed flips through `session/event`; there is no live mirror.
|
||||
*/
|
||||
@@ -187,7 +188,7 @@ export class PlanModeService extends Service {
|
||||
private readonly section: string
|
||||
|
||||
/**
|
||||
* Latest selection per session awaiting an in-turn request-boundary flush.
|
||||
* Latest selection per session awaiting the next accepted in-turn pre-step.
|
||||
* `narrate` is true for user selections and false for the exit tool, whose
|
||||
* result already narrates the transition.
|
||||
*/
|
||||
@@ -197,10 +198,10 @@ export class PlanModeService extends Service {
|
||||
super(ctx, 'planMode')
|
||||
this.section = resolveConfig(config).section
|
||||
let disposed = false
|
||||
// Pre-step is outside Session.append publication, so its log-only mode
|
||||
// event can land between turns or inside an open turn without re-entering
|
||||
// the session. A failed append remains pending for a later boundary, and
|
||||
// policy cannot block the step.
|
||||
// Pre-step is outside Session.append publication, so it can append the
|
||||
// log-only mode event inside an open turn without re-entering the session.
|
||||
// A failed append remains pending for a later accepted in-turn pre-step,
|
||||
// and policy cannot block the step.
|
||||
ctx.on('agent/pre-step', async (
|
||||
{ agent, signal },
|
||||
next,
|
||||
@@ -212,7 +213,7 @@ export class PlanModeService extends Service {
|
||||
try {
|
||||
this.onBoundary(agent.session)
|
||||
} catch (error) {
|
||||
ctx.logger.warn('dsh-plan-mode: boundary flush failed: %o', error)
|
||||
ctx.logger.warn('dsh-plan-mode: failed to append selected plan mode at step start: %o', error)
|
||||
return decision
|
||||
}
|
||||
return !pending.narrate || narration === undefined
|
||||
@@ -234,8 +235,9 @@ export class PlanModeService extends Service {
|
||||
// The plan projection unit (session-projection RFC): a pure double-event
|
||||
// fold serving clients the whole {active, pending} value. `command/run`
|
||||
// records the user's logged /plan selection (the handler calls `set()`
|
||||
// before any failing path, so log and run-plane cannot fork); `plan/mode`
|
||||
// is the boundary commit that resolves it. Pending is thereby a pure
|
||||
// before any failing path, so a failed handler cannot leave the recorded
|
||||
// command without its plan selection); `plan/mode` records that selection
|
||||
// and clears it. Pending is thereby a pure
|
||||
// replay quantity: host restarts, other tabs, and cold reads all recover
|
||||
// it from the log alone. The unit child activates only when a projection
|
||||
// registry is composed (headless assemblies stay unaffected).
|
||||
@@ -280,8 +282,9 @@ export class PlanModeService extends Service {
|
||||
case 'cancelled':
|
||||
return { kind: 'success', text: 'Plan mode entry cancelled.' }
|
||||
case 'noop':
|
||||
// Repeat the queued wording while an exit still awaits its
|
||||
// boundary; only a truly inactive session reads idempotent.
|
||||
// Repeat the queued wording while an exit still awaits the
|
||||
// next accepted pre-step; only a truly inactive session reads
|
||||
// idempotent.
|
||||
return foldPlanMode(agent.session.events)
|
||||
? { kind: 'success', text: 'Leaving plan mode (applies from the next step).' }
|
||||
: { kind: 'success', text: 'Plan mode is already inactive.' }
|
||||
@@ -357,8 +360,8 @@ export class PlanModeService extends Service {
|
||||
}
|
||||
throw cause
|
||||
})
|
||||
// A review may outlive this plugin fiber. Without boundary listeners,
|
||||
// an approved result could never land, so fail and keep planning.
|
||||
// A review may outlive this plugin fiber. Without its pre-step listener,
|
||||
// an approved selection could never be appended, so fail and keep planning.
|
||||
if (disposed) {
|
||||
throw new Error('the plan-mode service was reloaded while the plan was under review; present the plan again')
|
||||
}
|
||||
@@ -371,7 +374,8 @@ export class PlanModeService extends Service {
|
||||
: `The user chose to keep planning; their feedback: ${feedback}`)
|
||||
}
|
||||
// Keep plan guidance for the rest of this assistant tool batch. The
|
||||
// silent intent flushes after the step, before the next assembly.
|
||||
// silent selection is appended at the next accepted in-turn pre-step,
|
||||
// before its request assembly.
|
||||
this.pendingIntents.set(agent.session, { active: false, narrate: false })
|
||||
return { approved: true }
|
||||
},
|
||||
@@ -390,7 +394,8 @@ export class PlanModeService extends Service {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the logged plan state and any selected state awaiting a boundary.
|
||||
* Read the logged plan state and any selected state awaiting the next
|
||||
* accepted in-turn pre-step.
|
||||
*
|
||||
* @param agent The agent to read.
|
||||
* @returns Current logged state plus a pending selection, when present.
|
||||
@@ -402,20 +407,20 @@ export class PlanModeService extends Service {
|
||||
}
|
||||
|
||||
/**
|
||||
* Select whether plan mode should be active. Between turns the change
|
||||
* commits immediately — no request boundary would arrive until the next
|
||||
* prompt, so a queued intent would hang (the open-turn fold is the idle
|
||||
* signal: agent status stays `running` through post-turn checkpointing,
|
||||
* where a boundary equally never comes). During an open turn the
|
||||
* selection is held as pending intent for the next in-turn request
|
||||
* boundary. Repeated selection of the current or already-pending state is
|
||||
* a no-op.
|
||||
* Select whether plan mode should be active. Between turns the method
|
||||
* appends the change immediately because no in-turn pre-step will run until
|
||||
* another prompt starts a turn. The open-turn fold is the idle signal:
|
||||
* agent status stays `running` through post-turn checkpointing, when no
|
||||
* further in-turn pre-step runs. During an open turn the selection remains
|
||||
* pending until the next accepted in-turn pre-step. Repeated selection of
|
||||
* the current or already-pending state is a no-op.
|
||||
*
|
||||
* @param agent The agent to switch.
|
||||
* @param active Whether plan mode should be active.
|
||||
* @returns what happened: `committed` (logged now), `queued` (awaiting the
|
||||
* next boundary), `cancelled` (an opposite pending selection was cleared;
|
||||
* the logged state already matches), or `noop` (already in that state).
|
||||
* next accepted in-turn pre-step), `cancelled` (an opposite pending selection
|
||||
* was cleared; the logged state already matches), or `noop` (already in that
|
||||
* state).
|
||||
*/
|
||||
set(agent: Agent, active: boolean): 'committed' | 'queued' | 'cancelled' | 'noop' {
|
||||
const session = agent.session
|
||||
@@ -439,7 +444,7 @@ export class PlanModeService extends Service {
|
||||
return 'committed'
|
||||
}
|
||||
|
||||
/** Flush one pending selection before the next request assembly. */
|
||||
/** Append one pending selection before the next request assembly. */
|
||||
private onBoundary(session: Session): void {
|
||||
const pending = this.pendingIntents.get(session)
|
||||
if (pending === undefined) return
|
||||
@@ -449,8 +454,8 @@ export class PlanModeService extends Service {
|
||||
return
|
||||
}
|
||||
session.append('plan/mode', { active: target })
|
||||
// Delete only after append succeeds so a later boundary can retry a failed
|
||||
// durable write.
|
||||
// Delete only after append succeeds so a later accepted in-turn pre-step
|
||||
// can retry a failed durable write.
|
||||
this.pendingIntents.delete(session)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
/**
|
||||
* The plan projection's wire value. `active` is the logged state in force
|
||||
* (the last `plan/mode`, inactive before the first); `pending` is true while
|
||||
* a logged `/plan` selection (`command/run`) awaits its request-boundary
|
||||
* `plan/mode` commit and targets a state other than `active`. Capability
|
||||
* a logged `/plan` selection (`command/run`) targets a state other than
|
||||
* `active` and no later `plan/mode` event has recorded that state. Capability
|
||||
* absence (plan-mode not composed) is the key's absence, never a value.
|
||||
*/
|
||||
export interface PlanProjection {
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('plan projection unit', () => {
|
||||
expect(bench.values()).toEqual({ plan: { active: false, pending: false } })
|
||||
})
|
||||
|
||||
it('a logged /plan selection reads pending until the boundary commit resolves it', async () => {
|
||||
it('a logged /plan selection reads pending until plan/mode records it', async () => {
|
||||
const bench = await harness(true)
|
||||
runPlanCommand(bench.session, '', 0)
|
||||
expect(bench.values().plan).toEqual({ active: false, pending: true })
|
||||
|
||||
Reference in New Issue
Block a user