Skip to content

fix(code): make plan approval primary option match user's prior mode#1909

Merged
jonathanlab merged 1 commit intomainfrom
04-28-fix_code_make_plan_approval_primary_option_match_user_s_prior_mode
Apr 29, 2026
Merged

fix(code): make plan approval primary option match user's prior mode#1909
jonathanlab merged 1 commit intomainfrom
04-28-fix_code_make_plan_approval_primary_option_match_user_s_prior_mode

Conversation

@jonathanlab
Copy link
Copy Markdown
Contributor

Problem

Changes

How did you test this?

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 28, 2026

Comments Outside Diff (1)

  1. packages/agent/src/adapters/claude/claude-agent.ts, line 1044-1065 (link)

    P2 modeBeforePlan not rolled back on failure

    modeBeforePlan is written before the await setPermissionMode(...) call, but the catch block only restores permissionMode — leaving modeBeforePlan pointing at a stale value if the RPC fails. On the next successful plan entry the value is overwritten, so this rarely matters in practice, but it could produce a wrong "continue in X mode" label if a failed plan-entry is followed immediately by a successful one from a different prior mode in an unusual error-recovery path.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/agent/src/adapters/claude/claude-agent.ts
    Line: 1044-1065
    
    Comment:
    **`modeBeforePlan` not rolled back on failure**
    
    `modeBeforePlan` is written before the `await setPermissionMode(...)` call, but the `catch` block only restores `permissionMode` — leaving `modeBeforePlan` pointing at a stale value if the RPC fails. On the next successful plan entry the value is overwritten, so this rarely matters in practice, but it could produce a wrong "continue in X mode" label if a failed plan-entry is followed immediately by a successful one from a different prior mode in an unusual error-recovery path.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/agent/src/adapters/claude/permissions/permission-options.test.ts
Line: 15-43

Comment:
**Prefer a parameterised test for mode relabeling cases**

The three tests for `default`, `auto`, and `acceptEdits` are structurally identical — call with a `previousMode`, assert `options[0]` has the expected `optionId` and `name`. Per the team's rule of always preferring parameterised tests, these should be collapsed into one table-driven test:

```typescript
it.each([
  ["default", "default", "Yes, continue manually approving edits"],
  ["auto", "auto", 'Yes, continue in "auto" mode'],
  ["acceptEdits", "acceptEdits", "Yes, continue auto-accepting edits"],
])(
  "promotes %s to the first position with the correct continue label",
  (previousMode, expectedId, expectedName) => {
    const options = buildExitPlanModePermissionOptions(previousMode);
    expect(options[0]).toMatchObject({ optionId: expectedId, name: expectedName });
    expect(options[options.length - 1].optionId).toBe("reject_with_feedback");
  },
);
```

This also folds in the "reject option is always last" assertion, making the coverage obvious at a glance.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/agent/src/adapters/claude/claude-agent.ts
Line: 1044-1065

Comment:
**`modeBeforePlan` not rolled back on failure**

`modeBeforePlan` is written before the `await setPermissionMode(...)` call, but the `catch` block only restores `permissionMode` — leaving `modeBeforePlan` pointing at a stale value if the RPC fails. On the next successful plan entry the value is overwritten, so this rarely matters in practice, but it could produce a wrong "continue in X mode" label if a failed plan-entry is followed immediately by a successful one from a different prior mode in an unusual error-recovery path.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(code): make plan approval primary op..." | Re-trigger Greptile

Comment on lines +15 to +43
expect(options[0]).toMatchObject({
optionId: "default",
name: "Yes, continue manually approving edits",
});
expect(options[options.length - 1].optionId).toBe("reject_with_feedback");
});

it("relabels the auto option when it is the previous mode", () => {
const options = buildExitPlanModePermissionOptions("auto");
expect(options[0]).toMatchObject({
optionId: "auto",
name: 'Yes, continue in "auto" mode',
});
});

it("relabels the acceptEdits option when it is the previous mode", () => {
const options = buildExitPlanModePermissionOptions("acceptEdits");
expect(options[0]).toMatchObject({
optionId: "acceptEdits",
name: "Yes, continue auto-accepting edits",
});
});

it("ignores an unknown previous mode", () => {
const options = buildExitPlanModePermissionOptions("plan");
expect(options[0].name).toMatch(/^Yes, /);
expect(options[0].name).not.toMatch(/^Yes, continue/);
expect(options[options.length - 1].optionId).toBe("reject_with_feedback");
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Prefer a parameterised test for mode relabeling cases

The three tests for default, auto, and acceptEdits are structurally identical — call with a previousMode, assert options[0] has the expected optionId and name. Per the team's rule of always preferring parameterised tests, these should be collapsed into one table-driven test:

it.each([
  ["default", "default", "Yes, continue manually approving edits"],
  ["auto", "auto", 'Yes, continue in "auto" mode'],
  ["acceptEdits", "acceptEdits", "Yes, continue auto-accepting edits"],
])(
  "promotes %s to the first position with the correct continue label",
  (previousMode, expectedId, expectedName) => {
    const options = buildExitPlanModePermissionOptions(previousMode);
    expect(options[0]).toMatchObject({ optionId: expectedId, name: expectedName });
    expect(options[options.length - 1].optionId).toBe("reject_with_feedback");
  },
);

This also folds in the "reject option is always last" assertion, making the coverage obvious at a glance.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agent/src/adapters/claude/permissions/permission-options.test.ts
Line: 15-43

Comment:
**Prefer a parameterised test for mode relabeling cases**

The three tests for `default`, `auto`, and `acceptEdits` are structurally identical — call with a `previousMode`, assert `options[0]` has the expected `optionId` and `name`. Per the team's rule of always preferring parameterised tests, these should be collapsed into one table-driven test:

```typescript
it.each([
  ["default", "default", "Yes, continue manually approving edits"],
  ["auto", "auto", 'Yes, continue in "auto" mode'],
  ["acceptEdits", "acceptEdits", "Yes, continue auto-accepting edits"],
])(
  "promotes %s to the first position with the correct continue label",
  (previousMode, expectedId, expectedName) => {
    const options = buildExitPlanModePermissionOptions(previousMode);
    expect(options[0]).toMatchObject({ optionId: expectedId, name: expectedName });
    expect(options[options.length - 1].optionId).toBe("reject_with_feedback");
  },
);
```

This also folds in the "reject option is always last" assertion, making the coverage obvious at a glance.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Copy Markdown
Contributor

@adboio adboio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving to unblock but i do have a "strong opinion, loosely held" here that i think it's a better UX to have the options always sorted the same way instead of re-sorting every time one is chosen

personally i flip back and forth between the options pretty regularly and this would be mildly frustrating for me

can we check some data to see if that's common, or if users do typically just stick with one option all the time to assess the impact (i.e. would we be getting a net reduction of user inputs)?

Comment on lines +15 to +43
expect(options[0]).toMatchObject({
optionId: "default",
name: "Yes, continue manually approving edits",
});
expect(options[options.length - 1].optionId).toBe("reject_with_feedback");
});

it("relabels the auto option when it is the previous mode", () => {
const options = buildExitPlanModePermissionOptions("auto");
expect(options[0]).toMatchObject({
optionId: "auto",
name: 'Yes, continue in "auto" mode',
});
});

it("relabels the acceptEdits option when it is the previous mode", () => {
const options = buildExitPlanModePermissionOptions("acceptEdits");
expect(options[0]).toMatchObject({
optionId: "acceptEdits",
name: "Yes, continue auto-accepting edits",
});
});

it("ignores an unknown previous mode", () => {
const options = buildExitPlanModePermissionOptions("plan");
expect(options[0].name).toMatch(/^Yes, /);
expect(options[0].name).not.toMatch(/^Yes, continue/);
expect(options[options.length - 1].optionId).toBe("reject_with_feedback");
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@jonathanlab jonathanlab force-pushed the 04-28-fix_code_make_plan_approval_primary_option_match_user_s_prior_mode branch from c67bb56 to 271ba9c Compare April 29, 2026 10:26
@jonathanlab jonathanlab force-pushed the 04-28-fix_sidebar_show_created_task_immediately branch from f004c1c to fb0d5fe Compare April 29, 2026 10:26
Copy link
Copy Markdown
Contributor Author

jonathanlab commented Apr 29, 2026

Merge activity

@jonathanlab jonathanlab force-pushed the 04-28-fix_sidebar_show_created_task_immediately branch from fb0d5fe to c904c1b Compare April 29, 2026 11:06
@jonathanlab jonathanlab force-pushed the 04-28-fix_code_make_plan_approval_primary_option_match_user_s_prior_mode branch from 271ba9c to 1640b9e Compare April 29, 2026 11:06
@jonathanlab jonathanlab changed the base branch from 04-28-fix_sidebar_show_created_task_immediately to graphite-base/1909 April 29, 2026 11:21
@jonathanlab jonathanlab changed the base branch from graphite-base/1909 to main April 29, 2026 11:21
When the agent exits plan mode, the dialog now promotes the mode the
user was in before plan mode to the top of the option list and labels
it "Yes, continue ..." so the natural choice is to resume the prior
mode rather than picking from a flat list.

Generated-By: PostHog Code
Task-Id: 8845a96b-6bc0-42f8-9a9f-c9d572a7b29d
@jonathanlab jonathanlab force-pushed the 04-28-fix_code_make_plan_approval_primary_option_match_user_s_prior_mode branch from 1640b9e to a0d41ef Compare April 29, 2026 11:22
@jonathanlab jonathanlab merged commit f543286 into main Apr 29, 2026
16 checks passed
@jonathanlab jonathanlab deleted the 04-28-fix_code_make_plan_approval_primary_option_match_user_s_prior_mode branch April 29, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants