feat(github-bot): UI-driven model selector with inline directive override#619
feat(github-bot): UI-driven model selector with inline directive override#619donnfelker wants to merge 3 commits into
Conversation
…ride Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds inline directive parsing to GitHub comment handling, shared model alias resolution, session-level model/reasoning resolution with directive→config→env precedence, control-plane schema/validation, web UI controls for defaults and directive override toggle, Linear bot alias delegation, tests, and model onboarding docs. ChangesInline Directive Override Feature
Sequence Diagram (high-level request flow): sequenceDiagram
participant GitHub
participant Handler
participant Parser
participant Resolver
participant Session
participant Store
GitHub->>Handler: event
Handler->>Parser: comment body
Parser-->>Handler: ParsedDirective
Handler->>Resolver: env+config+directive+allowFlag
Resolver-->>Handler: ResolvedModelSettings
Handler->>Session: create session
Session->>Store: persist and log
🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/github-bot/src/inline-directive.test.ts`:
- Line 1: Move the test file inline-directive.test.ts out of the package source
folder into the package's test directory (i.e., relocate it from the src tree
into test/), update any relative import paths inside the file to reflect the new
location, and ensure the package's test discovery (vitest/glob or package.json
test config) still includes test/*.test.ts so this file is picked up; no code
changes to tests themselves are required beyond fixing imports/paths.
In `@packages/github-bot/src/model-resolution.test.ts`:
- Line 1: The test file model-resolution.test.ts is placed under src/ but should
live in the package test folder; move the file from
packages/github-bot/src/model-resolution.test.ts to
packages/github-bot/test/model-resolution.test.ts and update any import paths if
necessary (ensure the vitest imports: describe, it, expect remain valid and any
relative imports inside model-resolution.test.ts still resolve after
relocation).
In `@packages/github-bot/src/model-resolution.ts`:
- Around line 64-66: The current early return when modelFromDirective is true
always sets reasoningEffort: null and prevents the config fallback; change the
logic so that if modelFromDirective is true you only return immediately when the
directive explicitly provided a reasoning value (use that value for
reasoningEffort), otherwise do not override reasoningEffort and allow the
subsequent config fallback to run. Specifically, adjust the branch around
modelFromDirective to check for the presence/definedness of the directive's
reasoning (instead of unconditionally assigning null), returning { model,
reasoningEffort: <directiveReasoning> } only when that reasoning is present.
In `@packages/shared/src/models.ts`:
- Around line 206-223: The fallback in resolveModelAlias passes the original raw
string to normalizeModelId which does case-sensitive prefix checks, so
mixed-case inputs like "GPT-5.4" bypass normalization; update resolveModelAlias
to call normalizeModelId with the already-lowercased variable (use lower instead
of raw) so normalizeModelId sees canonical lowercase prefixes and correctly
normalizes bare "claude-"/"gpt-" ids.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1e99d115-f7b6-4a19-a131-76e0f6118ff1
📒 Files selected for processing (17)
docs/MODELS.mdpackages/control-plane/src/db/integration-settings.tspackages/control-plane/src/routes/integration-settings.tspackages/control-plane/test/integration/integration-settings.test.tspackages/github-bot/src/handlers.tspackages/github-bot/src/inline-directive.test.tspackages/github-bot/src/inline-directive.tspackages/github-bot/src/model-resolution.test.tspackages/github-bot/src/model-resolution.tspackages/github-bot/src/utils/integration-config.tspackages/github-bot/test/handlers.test.tspackages/github-bot/test/integration-config.test.tspackages/linear-bot/src/__tests__/pure-functions.test.tspackages/linear-bot/src/model-resolution.tspackages/shared/src/models.tspackages/shared/src/types/integrations.tspackages/web/src/components/settings/integrations/github-integration-settings.tsx
| if (modelFromDirective) { | ||
| return { model, reasoningEffort: null }; | ||
| } |
There was a problem hiding this comment.
Model-only directives currently null out reasoning unintentionally
On Line 64, modelFromDirective triggers reasoningEffort: null even when no directive reasoning was supplied. That skips the Line 69 config fallback and is broader than the comment on Lines 62-63.
Proposed fix
- if (modelFromDirective) {
+ if (modelFromDirective && input.directiveReasoningEffort) {
return { model, reasoningEffort: null };
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (modelFromDirective) { | |
| return { model, reasoningEffort: null }; | |
| } | |
| if (modelFromDirective && input.directiveReasoningEffort) { | |
| return { model, reasoningEffort: null }; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/github-bot/src/model-resolution.ts` around lines 64 - 66, The
current early return when modelFromDirective is true always sets
reasoningEffort: null and prevents the config fallback; change the logic so that
if modelFromDirective is true you only return immediately when the directive
explicitly provided a reasoning value (use that value for reasoningEffort),
otherwise do not override reasoningEffort and allow the subsequent config
fallback to run. Specifically, adjust the branch around modelFromDirective to
check for the presence/definedness of the directive's reasoning (instead of
unconditionally assigning null), returning { model, reasoningEffort:
<directiveReasoning> } only when that reasoning is present.
There was a problem hiding this comment.
Declining this change — the current behavior is intentional and is covered by an explicit test in model-resolution.test.ts ("directive model with no directive reasoning: drops config reasoning entirely"). The design choice: when a user supplies a fresh model via inline directive, we deliberately do not carry the previous config reasoning over, because reasoning is tightly coupled to the model and the override is treated as a clean reset. Adopting the suggested fallback would require updating both the test and the surrounding doc comment to flip the documented contract.
There was a problem hiding this comment.
@donnfelker, understood — thank you for the detailed explanation. The "clean reset" contract makes good sense: since reasoning effort is model-specific, inheriting config reasoning when the user explicitly switches models via directive could silently apply an incompatible or unexpected effort level. The existing test case documents this contract clearly, so no change is needed here.
( ´・‿・`) ( )
✏️ Learnings added
Learnt from: donnfelker
Repo: ColeMurray/background-agents PR: 619
File: packages/github-bot/src/model-resolution.ts:64-66
Timestamp: 2026-05-14T19:35:18.813Z
Learning: In `packages/github-bot/src/model-resolution.ts`, when a user supplies a model via an inline directive (`modelFromDirective = true`) but provides no directive reasoning effort, the resolver intentionally returns `reasoningEffort: null` and does NOT fall back to `configReasoningEffort`. This is by design — a directive model override is treated as a "clean reset" because reasoning effort is tightly coupled to the model. This contract is covered by the test "directive model with no directive reasoning: drops config reasoning entirely" in `packages/github-bot/src/model-resolution.test.ts`.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
Move github-bot test files to test/ to match the per-package convention documented in CLAUDE.md, and fix a case-sensitivity bug in resolveModelAlias where the raw (non-lowercased) input was passed to normalizeModelId, causing mixed-case bare ids like "GPT-5.4" to skip prefix normalization. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub bot gains plan-mode-by-label. An issue or PR labeled `plan` (or
`plan-<alias>` to pick a plan model) opens a planning session. `approve`
/ `reject` comments route to the control plane's `/plan/{approve,reject}`
endpoints. The bot remembers PR → session mapping in KV (7-day TTL)
so subsequent comments on the same PR resolve to the right session.
Label conventions match the linear-bot (kept in sync via the shared
`MODEL_ALIAS_MAP`):
- `plan` — trigger plan mode
- `plan-<alias>` — trigger plan mode with a specific plan model
- `model-<alias>` — pick the build model (no plan mode)
- `build-<alias>` — pick the build model on plan approval
- `review-<alias>` — pick the model for review automations
ADR 0001 (single-provider SCM): all changes live inside
`packages/github-bot`. No GitHub-specific URL or token construction
is added to router / session / slack layers. `label-resolution.ts`
is a pure string parser with no network calls; `handlers.ts` only
consumes the existing GitHub provider abstraction.
Other changes:
- New `label-resolution.ts` is a pure parser of the GitHub label set
into a resolved decision (`{ mode: "plan" | "build", model?, planModel? }`).
- `handlers.ts` integrates the resolution into PR/issue handlers, plus
KV persistence of PR → session for follow-up comment routing.
- `prompts.ts` adopts the shared `buildUntrustedUserContentBlock`
helper from `@open-inspect/shared/prompt-safety.ts` (moved in PR A)
for safe XML-tagged user content wrapping in code-review prompts.
- `integration-config.ts` resolves `defaultModel` via `fetchModelDefaults`
(PR A's helper) instead of reading `env.DEFAULT_MODEL` directly.
- README documents the dash-separated label table + the
`DEFAULT_MODEL` / `DEFAULT_PLAN_MODEL` fallback chain.
Coordination with upstream PR ColeMurray#619 (`feat(github-bot): UI-driven model
selector with inline directive override`): ColeMurray#619 adds inline `/model`
directives in comments, complementary to this PR's label-based
mechanism. The two can co-exist (different signal sources) but if
ColeMurray#619 lands first this PR will rebase to harmonize.
Stack: A → B → { C | D | **E** | F → G }
Depends on: A (plan backend, shared types, prompt-safety),
B (defaultModel / defaultPlanModel).
Parallel to: C, D, F.
Verification: `npm run typecheck`, `npm run lint`, `npm test`
(github-bot 6/6 files) — all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
@<bot> model: <alias> reasoning: <effort>inline directives in PR comments to override the configured model for a single run.MODEL_OPTIONS,MODEL_ALIASES,resolveModelAlias,normalizeModelId) in@open-inspect/sharedso the GitHub and Linear bots share one source of truth for model ids, aliases, and reasoning-effort compatibility.modelandreasoningEffortselection in the control-plane'sintegration-settingsstore, plus a booleanallowInlineDirectiveOverrideflag.packages/github-bot/src/inline-directive.tsand a near-clone of the Linear bot's resolver aspackages/github-bot/src/model-resolution.ts(kept separate so the two bots' policies can drift).docs/MODELS.md.Screenshots
Summary by CodeRabbit
New Features
Documentation
Tests