feat(plan-mode): universal /plan slash commands across all channels (PR-11)#68441
feat(plan-mode): universal /plan slash commands across all channels (PR-11)#68441100yenadmin wants to merge 129 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds universal plan-mode control and approval handling across channels by introducing a built-in /plan command and strengthening plan-mode infrastructure (state persistence, rendering, gating, and prompt guidance).
Changes:
- Introduces
/plancommand handling (UI + backend) and reservesplanto prevent plugin command shadowing. - Persists and surfaces plan-mode session state and last plan snapshot to support channel parity and UI rebuild-after-refresh.
- Adds/extends plan-mode runtime primitives (mutation gate, approval state machine, plan nudges, skill plan templates, prompt tuning) plus extensive tests.
Reviewed changes
Copilot reviewed 119 out of 120 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/src/ui/views/plan-approval-inline.ts | Inline plan approval + question variant rendering above chat input |
| ui/src/ui/types.ts | Adds plan-mode/permission-mode fields to UI session row typing |
| ui/src/ui/chat/slash-commands.ts | Registers /plan as a local UI slash command (autocomplete/dispatch) |
| ui/src/ui/chat/slash-command-executor.ts | Implements /plan execution (on/off/status/view/auto) + patch helpers |
| ui/src/ui/chat/slash-command-executor.node.test.ts | Tests /plan auto behavior and error surfaces |
| ui/src/ui/chat/plan-cards.ts | Adds expandable plan cards + markdown formatting helper |
| ui/src/ui/chat/plan-cards.test.ts | Tests plan card DOM rendering + markdown formatting |
| ui/src/ui/app-view-state.ts | Adds plan-approval and plan-sidebar state/actions to app view state |
| ui/src/ui/app-render.ts | Wires plan approval UI + mode chip behavior + plan sidebar open rendering |
| ui/src/ui/app-render.helpers.ts | Adds plan sidebar toggle button and aria state handling |
| ui/src/ui/app-chat.ts | Handles new slash action toggle-plan-view |
| ui/src/styles/chat/plan-cards.css | Styles for plan cards in message thread |
| ui/src/styles/chat/layout.css | Mode switcher + inline plan approval card styles |
| ui/src/styles/chat.css | Imports plan-cards stylesheet |
| ui/src/i18n/locales/en.ts | Adds chat.planViewToggle string |
| src/plugins/command-registration.ts | Reserves /plan and other built-in command names to prevent shadowing |
| src/infra/heartbeat-runner.ts | Prepends plan “continue” nudge to heartbeat prompt when plan is active |
| src/infra/heartbeat-runner.plan-nudge.test.ts | Tests plan nudge prefix selection logic |
| src/gateway/session-utils.types.ts | Exposes execSecurity/execAsk/planMode on gateway session rows |
| src/gateway/session-utils.ts | Populates execSecurity/execAsk/planMode in gateway session rows |
| src/gateway/server.impl.ts | Threads planSnapshot subscription unsubscribe through server runtime state |
| src/gateway/server-runtime-subscriptions.ts | Starts plan snapshot persister subscription |
| src/gateway/server-runtime-handles.ts | Tracks plan snapshot unsubscribe handle in mutable server state |
| src/gateway/server-methods/sessions.ts | Includes mode/plan state in sessions.changed payloads |
| src/gateway/server-close.ts | Unsubscribes plan snapshot persister on gateway shutdown |
| src/gateway/server-close.test.ts | Updates shutdown tests for new unsubscribe handle |
| src/gateway/protocol/schema/sessions.ts | Extends sessions.patch schema for planMode/planApproval/lastPlanSteps + length caps |
| src/gateway/plan-snapshot-persister.ts | Persists lastPlanSteps to session store on plan events; auto-close on completed phase |
| src/cron/normalize.ts | Consolidates “current sessionTarget” resolution logic (cron resume behavior) |
| src/config/zod-schema.ts | Adds skills.limits.maxPlanTemplateSteps config |
| src/config/zod-schema.agent-runtime.ts | Adds per-agent embeddedPi autoContinue/maxIterations overrides |
| src/config/zod-schema.agent-defaults.ts | Adds embeddedPi autoContinue/maxIterations + planMode defaults schema |
| src/config/types.skills.ts | Adds maxPlanTemplateSteps to skills limits types |
| src/config/types.agents.ts | Adds embeddedPi autoContinue/maxIterations per-agent types |
| src/config/types.agent-defaults.ts | Adds embeddedPi autoContinue/maxIterations + planMode defaults types |
| src/config/sessions/types.ts | Extends SessionEntry.planMode shape (snapshots, nudges, autoApprove, etc.) |
| src/auto-reply/reply/commands-system-prompt.ts | Threads provider/model identity into system prompt builder |
| src/auto-reply/reply/commands-handlers.runtime.ts | Registers new runtime handler for /plan |
| src/auto-reply/reply/agent-runner-execution.ts | Mirrors plan mode + approval state into AgentRunContext at registration |
| src/auto-reply/commands-registry.shared.ts | Registers /plan in builtin commands registry |
| src/agents/transport-message-transform.ts | Improves missing tool_result placeholder text and logging |
| src/agents/tools/update-plan-tool.test.ts | Adjusts expectations + adds close-on-complete + closure gate tests |
| src/agents/tools/sessions-spawn-tool.ts | Tracks parent runId context; forces cleanup keep in plan mode; tracks open subagents |
| src/agents/tools/enter-plan-mode-tool.ts | Adds enter_plan_mode agent tool definition |
| src/agents/tools/cron-tool.ts | Documents cron “resume after wait” recipe (sessionTarget=current) |
| src/agents/tools/ask-user-question-tool.ts | Adds ask_user_question tool (approval pipeline question) |
| src/agents/tools/ask-user-question-tool.test.ts | Tests ask_user_question validation + deterministic IDs |
| src/agents/tool-description-presets.ts | Adds summaries + descriptions for plan-mode tools and ask_user_question |
| src/agents/tool-catalog.ts | Registers plan-mode tools and ask_user_question in tool catalog |
| src/agents/test-helpers/fast-openclaw-tools-sessions.ts | Updates update_plan mock signature + exports PLAN_STEP_STATUSES |
| src/agents/system-prompt.ts | Adds GPT-5 context file reorder + injection-safe context file headings/content |
| src/agents/system-prompt-gpt5-boot-reorder.test.ts | Tests GPT-5 provider/model-specific context file ordering |
| src/agents/system-prompt-contribution.ts | Adds new provider section id tool_enforcement |
| src/agents/subagent-registry-run-manager.ts | Drains completed/killed child runs from parents’ openSubagentRunIds |
| src/agents/skills/workspace.ts | Carries plan templates into workspace skill snapshot |
| src/agents/skills/types.ts | Adds SkillPlanTemplateStep + resolvedPlanTemplates on SkillSnapshot |
| src/agents/skills/skill-planner.ts | Builds update_plan payload from skill plan templates with caps/dedup |
| src/agents/skills/frontmatter.ts | Parses plan-template / planTemplate from skill frontmatter |
| src/agents/skills/frontmatter.test.ts | Tests planTemplate parsing + precedence rules |
| src/agents/plan-mode/types.ts | Adds plan-mode session types + approvalId generation + decision injection |
| src/agents/plan-mode/plan-nudge-crons.ts | Schedules/cleans plan nudge cron jobs |
| src/agents/plan-mode/plan-archetype-prompt.ts | Adds plan archetype prompt + plan filename helpers |
| src/agents/plan-mode/plan-archetype-prompt.test.ts | Tests prompt fragment + filename helpers |
| src/agents/plan-mode/mutation-gate.ts | Implements plan-mode mutation gate + exec read-only allowlist |
| src/agents/plan-mode/mutation-gate.test.ts | Tests mutation gate behavior and safety checks |
| src/agents/plan-mode/index.ts | Re-exports plan-mode modules |
| src/agents/plan-mode/approval.ts | Approval state machine (approve/edit/reject/timeout + stale approvalId guard) |
| src/agents/plan-hydration.ts | Formats active plan injection after compaction |
| src/agents/plan-hydration.test.ts | Tests plan hydration formatting and filtering |
| src/agents/pi-tools.ts | Threads planMode into before-tool-call hook context |
| src/agents/pi-tools.before-tool-call.ts | Enforces mutation gate before plugin hooks run |
| src/agents/pi-embedded-runner/system-prompt.ts | Threads provider/model identity into embedded prompt build |
| src/agents/pi-embedded-runner/run/params.ts | Adds run param for planMode threading |
| src/agents/pi-embedded-runner/run/helpers.ts | Updates max iteration defaults; adds user overrides; subagent cap constant |
| src/agents/pi-embedded-runner/run/attempt.ts | Seeds plan from skill templates; prepends plan-mode prompt rules; passes planMode to tools |
| src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts | Stubs skill-template seeder in test support |
| src/agents/pi-embedded-runner/run.incomplete-turn.test.ts | Updates retry cap expectations; tests escalation and plan-mode carve-outs |
| src/agents/openclaw-tools.ts | Registers plan-mode tools + ask_user_question; threads runId into tools |
| src/agents/openclaw-tools.registration.ts | Adds config gate for plan-mode tool registration |
| src/agents/agent-scope.ts | Adds autoContinue + maxIterations resolvers with per-field merge |
| src/agents/agent-scope.test.ts | Tests autoContinue per-field merge behavior |
| qa/scenarios/gpt54-plan-mode-default-off.md | QA scenario asserting plan mode stays off by default |
| qa/scenarios/gpt54-mandatory-tool-use.md | QA scenario asserting tool use for factual queries |
| qa/scenarios/gpt54-injection-scan.md | QA scenario for injection scanner baseline |
| qa/scenarios/gpt54-cancelled-status.md | QA scenario for cancelled plan step status behavior |
| qa/scenarios/gpt54-act-dont-ask.md | QA scenario for “act on defaults” behavior |
| extensions/openai/prompt-overlay.ts | Adds tool enforcement block + identity/conciseness guidance + plan-mode carve-out |
| extensions/openai/index.test.ts | Updates OpenAI extension tests for new overlay section override |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe274f395a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
PR-12 hardening commit pushed ( Cron-nudge bugs (fixed in this PR)The plan-nudge cron infrastructure (PR-9 Wave B3) had three issues that surfaced under multi-cycle testing:
Tests: +5 in Live-test bugs deferred to PR-13 (next session)
Build: live locally as |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2abc20fec8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
📋 Plan-mode rollout — full PR series overview (10 PRs)Authoritative status snapshot for the entire plan-mode work, current as of The 10 open PRs in dependency order
(Closed/deprecated: #67518 Gemini guidance — explicitly dropped per maintainer.) What's NEW since the upstream PRs were openedPR-12 (cron-nudge fixes) and PR-13 (vertical question layout + inline Other textarea) were applied on top of #68441's branch as additional hardening — they don't have separate upstream PRs because they're polish on top of #68441's surface. Folded into this PR's diff. PR-12 (cron-nudge orphan + interrupt fixes) — commit
PR-13 (question-card UX fixes) — commit
Deferred to PR-14 (next session — well-scoped, ~150 LoC)
Recommended landing order
Co-merge guidance: #67538/#67721/#67840 each ship dead code on their own. Land all three in one merge window to keep Test coverage at the tip
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a6ec73433
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
PR-14 hardening commit pushed (`8e49c0850f`) — Telegram plan-mode visibility via markdown attachmentPer maintainer feedback ("why wouldn't we just add this to the Telegram PR that already exists?"), the Telegram visibility bridge lands here on PR #68441 instead of a separate PR. What it doesWhen the runtime emits a plan-mode approval, it now ALSO:
Resolution stays text-based via PR-11's `/plan accept | accept edits | revise ` slash commands (already work on Telegram). This sidesteps the dual approval-id problem of bridging inline-button approvals through the gateway plugin-approval pipeline. User-visible behavior on Telegram
Architecture (for review)
Tests: +32
All scoped tests passing locally. `pnpm lint` clean. `pnpm build` + `pnpm ui:build` clean. `pnpm tsgo` only flags the pre-existing baseline error from #67542's `plan-store.test.ts:249` (not from PR-14). Live install`OpenClaw 2026.4.15 (8e49c08)` running locally, gateway reachable in 52ms. What's STILL deferred (PR-15+)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8e49c0850f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
PR review-loop pass 1 complete (commit
|
| # | Comment | Outcome |
|---|---|---|
| #3104741699 | sessions-patch state-machine gap | Fixed (server-side pending guard) |
| #3104741709 | onAnswerOption typing | Fixed (runtime fallback + warning UI) |
| #3104742928 P1 | HTML truncation safety | Fixed (step-aware truncation) |
| #3104742929 P2 | Channel registry | Fixed (isMarkdownCapableMessageChannel) |
| #3105040898 P1 | Closure criteria after merge | Fixed (re-validate merged plan) |
| #3105040900 P2 | autoApprove on close-on-complete | Fixed (preserve flag in normal-mode branch) |
| #3105075577 P1 | /plan answer subcommand | Fixed (added to commands-plan.ts + webchat) |
| #3105075579 P2 | Verified criteria normalization | Fixed (normalize-then-compare) |
| #3105134661 P1 | CRITICAL: planMode threading | Fixed (mutation gate now activates) |
| #3105134664 P2 | Preserve lastPlanSteps on /plan off | Fixed (same patch as autoApprove) |
| #3105169600 | Snapshot persister sessions.changed | Fixed (callback wired) |
| #3105169607 | agentId path traversal (security) | Fixed (reject '.' / '..' + realpath confine) |
| #3105169610 | LOCAL_COMMANDS shadows /plan | Fixed (extended webchat executor) |
Live install: OpenClaw 2026.4.15 (c928790) — gateway reachable.
Build/lint/test: lint 0 errors; tsgo only the pre-existing baseline error from #67542's plan-store.test.ts; build + ui:build clean; all touched-surface tests pass.
Pass 2 (planned): re-trigger Greptile + Copilot + Codex; address any new comments. Greptile/Copilot/Codex notifications via @-mention follow.
cc @copilot @greptile-apps please re-review the latest commit c9287908eb with the 13 review fixes.
Resolves the previously-escalated PR-10 review comment #3104743333
(Codex P2 — sidebar refresh in update_plan merge mode) using
option C from the deferred-decision tree:
Option C: re-emit merged steps via the existing agent_plan_event
channel after merge. Lowest perf overhead (no SessionEntry hot-path
read), no new event type, persister already subscribes to this stream.
Implementation:
- src/infra/agent-events.ts — extend AgentPlanEventData with
optional mergedSteps field carrying full structured plan
(status/activeForm/acceptanceCriteria/verifiedCriteria). Legacy
steps[] field kept for backwards compat.
- src/agents/tools/update-plan-tool.ts — emitAgentPlanEvent now
includes mergedSteps in both 'update' and 'completed' phases.
- ui/src/ui/app-tool-stream.ts:
- maybeForwardLivePlanUpdate now SKIPS merge calls (the legacy
args.plan path was reading the delta instead of merged result)
- new maybeForwardMergedPlanEvent subscribes to stream:'plan'
events and refreshes the sidebar from mergedSteps. Authoritative
refresh path for merge mode.
Architecture documentation: docs/plans/PLAN-MODE-ARCHITECTURE.md is
the new single-source-of-truth document tracking:
- All 10 PRs (PR-A..PR-11) + their dependency graph
- Recommended landing order (5 waves)
- Feature behavior diagrams (plan-mode lifecycle, mutation gate,
auto-mode, universal /plan commands, Telegram visibility)
- Critical files reference
- Hardening status per PR
- Process going forward (naming, branch policy, push loop, gates)
- Beta-readiness checklist
This survives Claude Code session compactions.
Notes:
- Pre-existing tsgo baseline error in plan-store.test.ts:249 from
#67542 unchanged (not from this iteration).
- Lint clean, builds clean, tests pass on touched surfaces.
… truncation single-step cap, acceptanceCriteria parse, question-answer recovery
…ecture doc with current state
…p check in replace mode, fix description repeat, normalize step newlines in hydration, clarify test name
…longer mangles emails, header comment scope, bounded warnedStatuses Set, +3 hardening tests
…k O_NOFOLLOW + Windows compat, hard-cap deadman for PID reuse, EEXIST type-narrow, retry-after-stale-unlink, doc 60s, +stale-lock test, fix baseline tsgo error
…scan default allowlist to basenames + always emit warn on bypass, fix m+s comment, +case-insens + warn-on-bypass tests
… find -fprint dangerous flags, align buildPlanDecisionInjection with PlanApprovalState union, lower test maxCycles to fit schema cap, explicit toolCalls assertion in plan-mode-default-off scenario
…r old sessions, content alias for step, kebab-case fall-through to camelCase on invalid, use resolved config in eligibility filter, drop redundant null-check, fix docstrings + warning event wording
…lRequest + decision handler wired, strip ephemeral run IDs from comments + describe blocks, generalize Ctrl+1..N comment
…AN_STEP_STATUSES via import in exit-plan-mode-tool, remove non-existent lcm_grep from internal investigative-tool set
…t-approval state and synthetic-message injection — recentlyApprovedAt fully wired (write → mirror → consume), pendingAgentInjection persistence layer in place (consumer wiring deferred to PR-15)
…jection to PLAN_DECISION on approve/edit/reject, gate plan-snapshot auto-close on approval state, parseTelegramThreadId for scoped thread routing in PR-14 bridge
…itize per Copilot review
…ce of truth for [QUESTION_ANSWER]/[PLAN_DECISION] across all channels), revert lcm_grep removal (real lossless-claw plugin tool), schema-reserved doc note for autoEnableFor + approvalTimeoutSeconds, planApproval edit payload schema doc clarification
…le required, mutation-gate freshness via getLatestPlanMode (Bug 3+4), ACK retry uses live planMode + status-without-yield prompt (Bug 4), pending-guard in auto-close + UI graceful dismissal (Bug 5), [PLAN_COMPLETE] injection (Bug 6)
The v2 fix relied on `params.getActiveSessionEntry()` for live state, but
that callback in `agent-runner.ts:1207` is a closure over a captured
`let activeSessionEntry` (line 921) that only refreshes at compaction /
memory flush / error-recovery — NOT mid-turn. So a `sessions.patch` (UI
approval) flipping `planMode.mode → "normal"` between two tool calls in
the same run left the gate's view stale; agent reported the bug still
firing after v2.
v3 fix: replace closure-snapshot reads with `loadSessionStore(storePath,
{ skipCache: true })` for true fresh disk reads. Disk I/O is negligible
(small file, OS page cache, runs at turn-start + per-tool-call only).
Two snapshot sites converted (the v2 callbacks were already converted
in this branch):
* `registerAgentRunContext` block (~line 647) — fresh-reads all 4
mirrors at turn-start: `inPlanMode`, `planApproval`,
`recentlyApprovedAt`, `pendingAgentInjection`. Falls back to closure
on disk error (no worse than pre-fix behavior).
* `sessionPlanModeMode` capture (~line 1080) — fresh-read for the
initial `planMode` flag passed to `runEmbeddedPiAgent`. Mid-turn
drift is still covered by `getLatestPlanMode`, but this matters for
the first tool call's gate check before the callback fires.
Plus LCM tool allowlist additions (user-reported gap):
`PLAN_MODE_INVESTIGATIVE_TOOL_NAMES` only had `lcm_grep`; the lossless-
claw plugin also exposes `lcm_describe`, `lcm_expand_query`, and
`lcm_expand` (all read-only). Without them in the set, agents using
those tools at planning time triggered premature retry pressure.
Updated the comment block to document the full LCM family catalog,
the prompt example list in `attempt.ts`, and the
`PLAN_MODE_ACK_ONLY_RETRY_INSTRUCTION` user-facing tool list.
Verification:
- pnpm tsgo clean
- 123 scoped tests pass (incomplete-turn, pending-injection,
mutation-gate, exit-plan-mode-tool)
- pnpm check failure pre-exists on cumulative branch (telegram
import cycle, unrelated to this change)
Extracts the inline fresh-disk-read pattern from agent-runner-execution.ts into a dedicated helper `readLatestSessionEntryFresh` and adds a focused test file that locks in the closure-bypass behavior. Refactor: * New `src/auto-reply/reply/fresh-session-entry.ts` (~50 LoC): pure helper with no side effects, documented contract (returns LIVE entry when disk has fresher state, falls back to closure entry when storePath/sessionKey missing or load fails — pure superset semantic). * `agent-runner-execution.ts` Edit 1 + Edit 2 sites collapse from inline IIFEs (~30 LoC each) to single helper calls. Same runtime behavior, less code, single point of truth for the fresh-read pattern. Test coverage (9 tests, all green): * Live wins over stale planMode.mode (the original bug scenario) * Live wins over stale recentlyApprovedAt (yield-detector path) * Live wins over stale pendingAgentInjection (PR-15 injection path) * Falls back to closure when storePath / sessionKey / store-entry missing * Falls back to closure when loadSessionStore throws (corrupt JSON, ENOENT) * Pure-superset guarantee (multi-field fallback fully preserved on load failure) * Returns undefined when no fallback and store empty If any of these tests start failing, the closure-stale-ref class of bug is back — do NOT silence the test, find the regression in loadSessionStore's skipCache contract or in the helper. Verification: - pnpm tsgo clean - 132 scoped tests pass (111 unit-fast + 9 helper + 12 agents)
Live webchat testing surfaced 4 issues; addresses all of them in one
commit. Touched-surface tests pass (180 across 7 test files); tsgo
clean.
5 retry-instruction constants in `incomplete-turn.ts` and the plan-
nudge wake-up text in `plan-nudge-crons.ts` lacked the `[PLAN_*]:`
first-line tag that `[PLAN_DECISION]:` / `[QUESTION_ANSWER]:` /
`[PLAN_COMPLETE]:` already used. Added:
• PLAN_MODE_ACK_ONLY_RETRY_INSTRUCTION{,_FIRM} → `[PLAN_ACK_ONLY]:`
• PLAN_APPROVED_YIELD_RETRY_INSTRUCTION{,_FIRM} → `[PLAN_YIELD]:`
• PLANNING_ONLY_RETRY_INSTRUCTION{,_FIRM,_FINAL} → `[PLANNING_RETRY]:`
• plan-nudge wake-up text → `[PLAN_NUDGE]:`
Sets up a future PR to hide them from user-visible chat with a single
regex. Existing tests assert by constant identity (not literal value)
so they still pass.
Root cause: `SessionEntry.planMode` had NO `title` field. The
`exit_plan_mode` tool emits the title in `agent_approval_event` but
the plan-snapshot-persister never captured it; the UI side panel at
`ui/src/ui/app.ts:265` fell back to "Active plan" generic label.
Fix:
* Added `planMode.title` + `planMode.approvalRunId` fields to
`SessionEntry` (also mirrored in `PlanModeSessionState`).
* `plan-snapshot-persister.ts` adds a second `onAgentEvent("approval")`
listener that captures `title` + parent `runId` + `approvalId` from
the event and writes them to `SessionEntry.planMode` via direct
in-place store mutation (server-internal metadata; per
`protocol/CLAUDE.md`, no wire schema change needed since the
existing sessions.list response already carries the full planMode
object to the UI).
* UI `buildPlanViewMarkdown` accepts a new `title?` param that takes
precedence over `summary` for the header. All 3 call sites
(`hydratePlanViewFromSession`, `refreshLivePlanSidebar`,
`openPlanInSidebar`) read `row.planMode.title` and pass it.
* Pre-`exit_plan_mode` state (only `update_plan` has fired): header
shows `(planning)` — honest signal that no real title exists yet.
* Post-`exit_plan_mode`: header anchors on the agent-supplied title
through every lifecycle phase (planning → submitted → approved →
executing → completed).
The persisted markdown filename pattern `plan-YYYY-MM-DD-<slug>.md`
already worked (verified in `plan-archetype-prompt.ts:164`); no
change there.
Root cause: tool-side gate at `exit-plan-mode-tool.ts:230` reads
live `openSubagentRunIds` at submission time; if a NEW subagent
spawns during the user's approval window between the card showing
and the user clicking Approve, the original gate is already passed
and the approval handler at `sessions-patch.ts:572` had NO subagent
check — agent would proceed mid-flight subagent results.
Server fix (`sessions-patch.ts`):
* Reads `next.planMode.approvalRunId` (set by Bug 2 persister) for
`approve`/`edit` actions and looks up parent ctx via
`getAgentRunContext`. Throws new `PLAN_APPROVAL_BLOCKED_BY_SUBAGENTS`
ErrorCode if `openSubagentRunIds.size > 0`. `reject` is NOT gated
(negative feedback always accepted).
* Extended `invalid()` helper to accept optional code + details so
the error shape carries `openSubagentRunIds` for the UI to
display in a toast tooltip.
* Added `ErrorCode` type re-export from `protocol/index.ts` and
`ErrorCodes.PLAN_APPROVAL_BLOCKED_BY_SUBAGENTS` to the closed
enum.
UI fix (`app.ts` + `app-tool-stream.ts` + `chat.ts`):
* New `SubagentBlockingStatus` type + `@state() subagentBlockingStatus`
field. Mirrors `FallbackStatus` shape.
* `handlePlanApprovalDecision` catches the error code (also matches
on message-substring as a transport fallback), restores the
approval card so the user can retry, sets
`subagentBlockingStatus` and schedules an 8s auto-clear.
* New `renderSubagentBlockingIndicator` in `chat.ts` renders the
toast in the same bottom region as `renderFallbackIndicator`
(the model-fallback toast — the only toast surface the user has
reported actually seeing in webchat). CSS class
`compaction-indicator--fallback` reused.
* Wording: "Subagents still running — try again after subagent
results return" (per user feedback — clearer than the earlier
"try again in a moment" draft).
7 new tests in `sessions-patch.subagent-gate.test.ts` cover the
gate (allow/block per action, fallthrough conditions, error format
including the truncated "and N more" suffix).
Opt-in `OPENCLAW_DEBUG_PLAN_MODE=1` env var that turns on detailed
plan-mode lifecycle logging at every state transition / gate
decision / tool call / synthetic injection / approval event. Off by
default (zero perf impact via short-circuit on env-var check).
* New `src/agents/plan-mode/plan-mode-debug-log.ts` (~115 LoC) with
discriminated event union (`PlanModeDebugEvent` — 8 kinds).
`logPlanModeDebug(event)` no-ops when env unset, otherwise
emits via `createSubsystemLogger("plan-mode")` to
`gateway.err.log` with `[plan-mode/<kind>]` tag prefix.
* 12 tests in `plan-mode-debug-log.test.ts` cover env-var gate
(including late-set env), serialization shape per event kind,
and the no-op fast path.
* Instrumentation at the high-value sites:
- `sessions-patch.ts` — state_transition (planMode flip),
approval_event (success + subagent-gate rejection),
synthetic_injection ([PLAN_DECISION]: write)
- `exit-plan-mode-tool.ts` — gate_decision (subagent in-flight
block + allow path)
- `plan-snapshot-persister.ts` — tool_call (exit_plan_mode →
title persist trigger)
Activation:
OPENCLAW_DEBUG_PLAN_MODE=1 launchctl kickstart -k gui/$UID/ai.openclaw.gateway
tail -F ~/.openclaw/logs/gateway.err.log | grep '\[plan-mode/'
`docs/plans/PLAN-MODE-ARCHITECTURE.md` bumped with a new "Live
testing iteration 1" section listing all 4 fixes + the activation
instructions for Bug 4 debug logging. Long-form rollout history
stays in that doc as the cross-session checkpoint.
* pnpm tsgo clean
* 180 scoped tests pass:
- 12 plan-mode-debug-log
- 7 sessions-patch.subagent-gate (NEW)
- 29 incomplete-turn (with new prefixes — assertions still pass
because tests import constants by reference)
- 41 sessions-patch (existing)
- 12 mutation-gate
- 30 exit-plan-mode-tool
- 9 fresh-session-entry
- misc utility tests
* pnpm check failure pre-exists on this branch (telegram extension
import cycle, unrelated)
…ng fixes Live webchat testing of iter-1 (commit 3024c6b) surfaced 6 deeper edge cases. This commit addresses Bugs A, C, D, E, F (Bug B server- guard + UI auto-dismiss deferred to Phase 2). Touched-surface tests pass (120 across 6 test files); tsgo clean. ## Bug A — getLatestPlanMode false-positives after planMode deletion (THE big one) Root cause traced via log + code: sessions-patch.ts:751 DELETES `next.planMode` entirely on approve/edit (when no autoApprove flag to preserve) ↓ getLatestPlanMode returned `undefined` for the missing-planMode case ↓ Consumers used `?? args.ctx.planMode` fallback chain ↓ fallback returned the STALE "plan" snapshot from run-start ↓ mutation gate kept blocking write/edit AFTER approval; ack-only detector kept firing post-approval; agent stuck in retry loop → "incomplete turn detected, surfacing error to user" Fix: * New `resolveLatestPlanModeFromDisk` helper in `fresh-session-entry.ts` encodes the deletion-as-normal contract: when entry exists but planMode object is missing, return "normal" (NOT undefined). Returns undefined ONLY when the disk truly can't be read (missing path/key, throw, no entry for key). * Both `getLatestPlanMode` callbacks in `agent-runner-execution.ts` refactored to use the helper. Net -73 LoC across the two callbacks (collapsed inline IIFEs + duplicate logic). * Defense-in-depth: `pi-tools.before-tool-call.ts:230` and `run.ts:1834` consumers now explicit-check `liveMode !== undefined` instead of `??` so a future helper change can't reintroduce the stale-fallback bug. Cached snapshot is reserved for true disk-failure cases only. * 8 new tests in `fresh-session-entry.test.ts` lock in the deletion-as-normal contract. ## Bug E — [PLAN_DECISION] format inconsistency Two emission sites had two different formats: * `types.ts:buildPlanDecisionInjection` (block format on reject/timeout) * `sessions-patch.ts:606` (one-line on approve/edit) Unified on the one-line `[PLAN_DECISION]: <decision>` opener so it matches every other plan-mode synthetic message ([QUESTION_ANSWER]:, [PLAN_COMPLETE]:, [PLAN_ACK_ONLY]:, [PLAN_YIELD]:, [PLAN_NUDGE]:, [PLANNING_RETRY]:). Future "hide PLAN_* in webchat" filter can be a single regex. Adversarial-feedback sanitization (closing-marker neutralization) preserved. 6 tests updated in `approval.test.ts` to assert the one-line opener. ## Bug F — Tool description + agent guidance Agent demonstrably: * Posted assistant text AFTER `exit_plan_mode` in same turn (Bug A trigger surface) * Confused `update_plan` with `exit_plan_mode` * Read multi-MB rolling logs from line 1 instead of tailing Updated: * `describeExitPlanModeTool`: STOP-AFTER-TOOL-CALL is the FIRST description sentence. * `describeUpdatePlanTool`: TRACKING-ONLY clarification. * `describeEnterPlanModeTool`: explicit lifecycle pattern. * `attempt.ts:549-557`: log-triage hygiene + "no chat after exit_plan_mode" reminder. ## Bug D — Config-flag activation for plan-mode debug log `OPENCLAW_DEBUG_PLAN_MODE=1` set via `launchctl setenv` produced ZERO `[plan-mode/...]` lines in iter-1 testing. Root cause: macOS `launchctl setenv` only affects FUTURE launchd-spawned processes, not running children of the OpenClaw Mac app. Fix: * New config field `agents.defaults.planMode.debug?: boolean`. * `plan-mode-debug-log.ts:isDebugEnabled` now reads BOTH env var and config (env wins). Fail-closed on config load errors. * 5 new tests cover the config-flag path. Activation: openclaw config set agents.defaults.planMode.debug true launchctl kickstart -k gui/$UID/ai.openclaw.gateway tail -F ~/.openclaw/logs/gateway.err.log | grep '\[plan-mode/' ## Bug C — Always-on diagnostic log at approval-side subagent gate iter-1 added the gate but Eva couldn't tell from observation whether the gate fired (toast didn't visibly surface). With debug log silent (Bug D), there was no way to verify gate firings. Fix: * New always-on subsystem logger `gateway/plan-approval-gate` emits `gate decision: action=approve sessionKey=… approvalRunId=… openSubagents=N result=blocked|allowed` on every approve/edit. * Also emits `gate disabled: …` warn when `approvalRunId` not persisted. ## Architecture doc `docs/plans/PLAN-MODE-ARCHITECTURE.md` updated with iter-2 findings table + activation instructions for the config-flag debug path. ## Verification * pnpm tsgo clean * 120 scoped tests pass: - 17 fresh-session-entry (9 + 8 new) - 17 plan-mode-debug-log (12 + 5 new) - 32 approval (26 + 6 updated) - 7 sessions-patch.subagent-gate - 29 incomplete-turn - 18 exit-plan-mode-tool ## Known follow-up (Phase 2 — not in this commit) Bug B (stale approval card after planMode auto-cleared, double-popup, "requires an active plan-mode session" raw error) needs UI work + new PLAN_APPROVAL_EXPIRED error code. Deferred to next commit so this stays focused on the runtime correctness fix (Bug A).
The TypeScript type for `agents.defaults.planMode.debug` was added in 98ac5e1 but the runtime Zod validation schema in `zod-schema.agent-defaults.ts` rejected the field with `Unrecognized key: 'debug'`. Added the matching `z.boolean().optional()` entry so `openclaw config set agents.defaults.planMode.debug true` validates and persists. Verified end-to-end: `openclaw config set` accepts and writes the value after this fix.
Closes the user-reported meta-gap: "will plan mode work reliably for
ANY install, on ANY agent, including agents that just installed the
patch and have never seen plan mode before?" Audit found 0/5 score
on bootstrap, skills, user docs, and diagrams — agent had a 2-turn
learning curve relying on the in-mode prompt only.
Phase 1 ships 5 self-discovery deliverables; Phase 2 (self-test
slash command + introspection tool) and Phase 3 (robustness fixes)
land in follow-up commits.
## D1 — Plan-mode reference card injected on every in-mode turn
New `src/agents/plan-mode/reference-card.ts` (~140 LoC) — compact
ASCII state-machine diagram + tool contract one-liners + [PLAN_*]:
tag taxonomy + /plan slash-command surface + common pitfalls +
debugging tips.
Wired into `src/agents/pi-embedded-runner/run/attempt.ts:567` so
the card sits alongside PLAN_ARCHETYPE_PROMPT in the in-mode
system prompt. Eliminates the 2-turn learning curve: agent sees
the full reference on EVERY in-mode turn from turn 1.
## D2 — One-shot first-time intro injection
When `sessions.patch { planMode: "plan" }` fires AND the session
has never seen plan mode before (no `planModeIntroDeliveredAt`
marker on `SessionEntry`), write a `[PLAN_MODE_INTRO]: ...`
synthetic message to `pendingAgentInjection`. Agent's next turn
opens with a quick lifecycle overview + pointer to /plan
self-test.
* New `SessionEntry.planModeIntroDeliveredAt?: number` field
(root level, survives planMode delete on approve/edit)
* Wired in `src/gateway/sessions-patch.ts` planMode→"plan"
transition (around line 502)
* One-shot semantic: subsequent enter_plan_mode calls in the
same session skip the intro
* Composes cleanly with existing `pendingAgentInjection`
consumers (e.g. [QUESTION_ANSWER]: from a prior turn) — appends
rather than overwriting
## D3 — Tool descriptions point at the reference card + self-test
Appended to all 3 plan-mode tool descriptions in
`tool-description-presets.ts`:
"For the full plan-mode reference (state diagram, [PLAN_*]: tag
taxonomy, /plan slash commands, common pitfalls, debugging tips):
see the bootstrap-injected reference card visible on every
in-mode turn. Verify your local install with `/plan self-test`."
Gives the agent a discoverable pointer even before it enters plan
mode (tools are visible in normal mode too).
## D4 — User-facing concept doc
New `docs/concepts/plan-mode.md` (~140 LoC) — product-facing "plan
mode for users" doc following the same frontmatter style as
`docs/concepts/context-engine.md`. Covers: when to use, lifecycle,
slash commands, multi-channel behavior, persisted plans,
auto-approve mode, subagent gating, troubleshooting.
Distinct from `docs/plans/PLAN-MODE-ARCHITECTURE.md` (internal
architecture + iteration history). Also discoverable to agents
that grep `docs/`.
## D7 — `plan-mode-101` skill
New `skills/plan-mode-101/SKILL.md` — same content as the
in-mode reference card (D1) but in skill format with frontmatter
description. Trigger phrases: "explain plan mode", "test plan
mode", "plan mode help", "why was my tool blocked in plan mode",
"what does [PLAN_DECISION] mean", "what does /plan accept do".
The skill works in NORMAL mode (where the in-mode reference
card isn't injected), so an agent that's asked "explain plan
mode" or "what does [PLAN_NUDGE] mean" can invoke the skill
on-demand even before it enters plan mode.
## Verification
* pnpm tsgo clean
* 85 scoped tests pass (44 plan-archetype + 41 sessions-patch)
* Reference card content + skill content + concept doc kept in
sync (manual; future iter could automate via codegen)
## Known follow-ups
* Phase 2: `/plan self-test` slash command + `plan_mode_status`
introspection tool (deferred to next commit for review clarity)
* Phase 3: Top 5 robustness fixes from the failure-mode audit
(subagent crash drainage, cron-nudge suppression, title XSS
audit, disk-full graceful error, multi-channel approval dedup
test) — also deferred
* Bug B (stale approval card UI auto-dismiss + new error code) —
still deferred from iter-2; targeted for iter-4
…n tool
Closes the live-test bug the user reported on iter-2 build: subagent
spawned during plan-mode investigation, agent called exit_plan_mode
while subagent still running, gate didn't fire, approval card showed,
user clicked Approve, agent stalled because subagent return arrived
AFTER approval and the agent narrated the result instead of
continuing the plan-mode flow.
## R6a — Always-on diagnostic at exit_plan_mode subagent gate
Symptom: silent gate bypass. Iter-1 added the gate but only logged
via the env-gated plan-mode debug log; bypass cases (no runId, ctx
not registered, openSubagentRunIds undefined) left no trace, so we
couldn't tell from logs whether the gate fired and was empty vs.
silently skipped.
Fix: new always-on `agents/exit-plan-gate` subsystem logger emits
ONE diagnostic line for EVERY exit_plan_mode call:
agents/exit-plan-gate gate decision: result=blocked|allowed
runId=<id> sessionKey=<key> openSubagents=N
reason=<one of: gated|no runId|ctx not registered|
openSubagentRunIds undefined|empty>
Now operators can grep `agents/exit-plan-gate` in gateway.err.log
to see every submission attempt + the precise reason the gate did
or didn't fire. This is the diagnostic prerequisite for proving (or
disproving) future race-window hypotheses.
## R6b — Subagent announce-turn injection plan-mode-aware
Symptom: agent narrated subagent result and stopped (TERMINAL turn),
breaking the plan-mode flow. The standard announce reply instruction
at `subagent-announce.ts:buildAnnounceReplyInstruction` told the
agent "send a user-facing update now" — agent read that as "this is
your final action this turn" and didn't call exit_plan_mode after
incorporating the result.
Fix: announce reply instruction is now plan-mode-aware. When the
requester session's `planMode.mode === "plan"`, an explicit suffix
is appended:
"You are currently in PLAN MODE — do not stop after the
user-facing update. Your next action MUST be either: (a) call
`exit_plan_mode(...)` if this subagent's result completes your
investigation, OR (b) continue investigation with another
read-only tool call. Trailing chat alone is treated as yielding
without acting and will trigger a [PLAN_ACK_ONLY] retry."
Read at announce-build time from `loadRequesterSessionEntry`'s
entry.planMode.mode. Best-effort lookup; falls back to default
instruction (no plan-mode suffix) if the lookup throws.
## D6 — `plan_mode_status` introspection tool
Symptom: agent had to INFER plan-mode state from tool-rejection
errors. No way to programmatically check "am I in plan mode?" /
"what's the title?" / "how many subagents are in flight?" / "is the
debug log enabled?".
Fix: new read-only tool `plan_mode_status` that returns a
structured snapshot of every plan-mode field plus the debug-log
status. Self-resolves storePath via `resolveDefaultSessionStorePath`
so it works without storePath plumbing through the registry. Wired
into the bundled toolset in `openclaw-tools.ts:283` alongside
`enter_plan_mode` / `exit_plan_mode` / `ask_user_question`.
Returns:
- inPlanMode, approval, title, approvalRunId
- planStepCount, openSubagentCount, openSubagentRunIds[]
- recentlyApprovedAt, pendingAgentInjectionPreview
- planModeIntroDeliveredAt, autoApprove, debugLogEnabled
Used by:
- Agent self-diagnosis ("what's my plan-mode state?")
- Future `/plan self-test` slash command (D5, deferred)
- Future debugging skills + workflows
## Verification
* pnpm tsgo clean
* 18 scoped tests pass on exit-plan-mode-tool
## Deferred to iter-3 commit 3 (next focused commit)
* D5 — `/plan self-test` slash command
* R1 — Subagent cleanup on crash/timeout (drain openSubagentRunIds
on error paths so the gate doesn't deadlock indefinitely)
* R2 — Cron-nudge suppression when approval pending (heartbeat path
already does this via `buildActivePlanNudge:742`; cron-fire path
needs the same check)
* R3 — Plan title XSS sanitization audit + test
* R4 — Disk-full graceful error in sessions-patch.ts
* R5 — Multi-channel approval dedup test
* Bug B (still deferred from iter-2) — stale approval card UI
auto-dismiss + new PLAN_APPROVAL_EXPIRED error code
## Architecture doc
`docs/plans/PLAN-MODE-ARCHITECTURE.md` updated with iter-3 status
section listing both Phase 1 (already shipped in c262bff) and
the R6+D6 deliverables in this commit.
…wlist fixes
User-reported live test (17:54-17:58) surfaced TWO critical bugs
plus exposed that iter-1 R3 (approval-side subagent gate) had been
silently broken since landing.
## X1 — plan_mode_status blocked by mutation gate (regression I just introduced)
Iter-3 commit 2 (`ffbdd7cd58`) added the new `plan_mode_status`
introspection tool but did NOT add it to
`PLAN_MODE_ALLOWED_TOOLS`. The mutation gate's default-deny then
blocked the tool in plan mode — the most important place to use it.
User hit this exact scenario: agent called `plan_mode_status`
mid-pending-approval to self-diagnose, got
"Tool 'plan_mode_status' is not in the plan-mode allowlist and is
blocked by default. Call exit_plan_mode to proceed."
Fix: added `plan_mode_status` to `PLAN_MODE_ALLOWED_TOOLS` in
`mutation-gate.ts`. Also added `sessions_list` and `sessions_history`
(read-only sessions tools that are useful during plan-mode
investigation and shouldn't require allowlist gymnastics).
## X2 — Persister phase typo silently broke iter-1 R3 + Bug 2 entirely
ROOT CAUSE for the user's earlier live-test failures (Bug 3 from
iter-1, R6 from iter-3 commit 2 retest) was a SILENT TYPO in the
plan-snapshot-persister's approval-event filter:
Filter checked: phase === "request"
Actual emit was: phase === "requested" (past tense)
The phase enum is `AgentApprovalEventPhase = "requested" | "resolved"`
(see `agent-events.ts:90`). The persister listener silently skipped
EVERY plan submission because `"requested" !== "request"`. Cascading
effects:
* `SessionEntry.planMode.title` was NEVER persisted to disk —
side panel showed "(unset)", `plan_mode_status` reported
`title="(unset)"` even right after `exit_plan_mode` submitted
with a real title.
* `SessionEntry.planMode.approvalRunId` was NEVER persisted to
disk — which means the iter-1 approval-side subagent gate at
`sessions-patch.ts:620+` always fell through silently. The gate
reads `next.planMode.approvalRunId` to look up the parent's
`openSubagentRunIds` via `getAgentRunContext`. With approvalRunId
undefined, the gate emits the "approvalRunId not persisted (Bug 2
wiring may be missing)" warn line and proceeds without checking
subagents.
So iter-1 R3 (the entire approval-side subagent gate) HAS BEEN
SILENTLY BROKEN since iter-1 landed. Tests passed because they
manually injected `approvalRunId` into test fixtures; production
NEVER saw the field populated.
Fix: persister filter now accepts BOTH `"requested"` (canonical) AND
`"request"` (defensive, in case the event shape ever changes back).
Smoking-gun evidence from user's live test:
17:54: enter_plan_mode → spawn subagent → yield (1m14s)
17:55: subagent completes (drained from openSubagentRunIds)
17:56: subagent announce arrives at parent
17:56: parent narrates result + calls exit_plan_mode
→ approval card appears
→ "Plan submitted for approval — Test plan-mode child-return
continuation (4 steps)."
17:56: agent calls plan_mode_status
→ output: title="(unset)" ← SMOKING GUN
(title should have been "Test plan-mode child-return continuation")
This was the persister silently failing for the entire iter-1 +
iter-2 lifetime. Fixed now.
## What this cascade-fixes
* iter-1 Bug 2 (title persistence) — ACTUALLY works now
* iter-1 Bug 3 / R3 (approval-side subagent gate) — ACTUALLY works
now (approvalRunId is finally populated for the gate to look up)
* iter-3 R6 (subagent gate hardening) — already-shipped diagnostic
log + announce-aware reply suffix still apply, but the underlying
gate logic now has its required input data
* User's live-test stall — likely root cause was the cascade of
X2 (gate fell through silently → approve let through with stale
state). Should be substantially better after this commit; retest
needed to confirm.
## Verification
* pnpm tsgo clean
* 82 scoped tests pass (mutation-gate + exit-plan-mode-tool)
## Still deferred
X3 (announce-race micro-window): with X2 fixed, the approval-side
gate now actually fires; this provides a second-line defense against
the subagent-spawned-during-approval-window race. The pure
"subagent drained but announce not yet delivered" race is largely
academic now — the parent's wake-up IS the announce delivery, so
by the time the parent CAN call exit_plan_mode, the announce has
been processed.
X4 (post-approve stall): need a fresh test with X1+X2 fixed to see
if the stall persists. Likely it was a downstream effect of X2 (no
title, no approvalRunId → confused state).
D5, R1, R2, R3, R4, R5, Bug B: still deferred to follow-up commits.
Two type errors surfaced by pnpm tsgo after rebase + one lint cleanup: * src/agents/plan-mode/plan-archetype-bridge.ts:201 referenced ../../plugin-sdk/telegram.js which upstream removed in the plugin-sdk restructure. PR-14 (Telegram visibility) deferred to follow-up; markdown plan file still persists, only Telegram document upload skipped (with warn log line). * src/gateway/server-runtime-subscriptions.ts:82 referenced params.minimalTestGateway which doesn't exist on the param type. Removed the conditional; persister always starts now. * parseTelegramThreadId helper preserved in comments so PR-14 re-wire follow-up can resurrect it without rewriting parsing. Verification: pnpm tsgo clean, scoped plan-mode tests pass (81 across 5 files).
bee5e8c to
01ed636
Compare
|
Closing in favor of consolidated PR (to be opened immediately). Rationale: this branch was 734 commits behind upstream/main and the PR's review feedback was firing against a stale base. Rebased the full 135-commit feature work onto upstream/main @ v2026.4.19-beta.2 (only 5 conflicts to resolve, all incidental file overlaps — no semantic feature collision since plan mode is exclusively our work) and consolidated the 10-PR series into a single umbrella PR for cleaner bot review + faster maintainer context-loading. The full iteration history + decision log lives in Plus this PR's branch ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 01ed63633e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const allowEdits = raw === "accept edits" || raw === "accept edit"; | ||
| try { |
There was a problem hiding this comment.
Tokenize
/plan accept edits before choosing action
The web slash executor treats allowEdits as true only for exact strings ("accept edits" / "accept edit"), so valid variants like /plan accept edits or /plan accept edits please fall back to action: "approve". That silently changes user intent from “accept with edits” to plain approve, which can block expected plan adjustments after approval. The backend parser already tokenizes this command, so the web path should match that behavior.
Useful? React with 👍 / 👎.
Summary
PR-11 of the plan-mode rollout series. Sits on top of PR-10 (#68440) which is a hard prerequisite. Net-new in this PR are 2 commits (`29bf11710e` + `fe274f395a`) — focused review surface is `git diff feat/plan-archetype-and-questions..feat/plan-channel-parity`.
Change Type
Scope
Linked Issue/PR
Root Cause
N/A — feature work + hardening of pre-existing renderPlanChecklist markdown branch.
Testing
Evidence
Human Verification
Review Conversations
Compatibility / Migration
Risks and Mitigations