-
Notifications
You must be signed in to change notification settings - Fork 26
feat(gastown): Rig-level settings — per-rig overrides for town-level configuration #2012
Description
Summary
Add a rig settings page that mirrors key town-level settings but applies per-rig. Rig config is more specific than town config — when a rig setting is defined, it overrides the town default for that rig. When it's not defined, the town default applies.
This follows the standard specificity cascade: rig config > town config > system defaults.
Motivation
Different repos have different needs:
- A production service repo should always go through refinery review (
review_mode: 'always'), while a prototype repo can skip review (review_mode: 'never') - A large monorepo needs a more capable model for polecats, while a small utility repo can use a cheaper one
- One repo needs
git push --no-verify(heavy pre-push hooks), another doesn't - One repo uses pnpm, another uses npm — custom polecat instructions differ per repo
Currently all rigs in a town share the same settings. Users who want different behavior per repo have to create separate towns.
Design
Config Resolution
function resolveConfig<K extends keyof RigConfig>(
townConfig: TownConfig,
rigConfig: RigConfig | null,
key: K
): RigConfig[K] | TownConfig[K] {
return rigConfig?.[key] ?? townConfig[key];
}At dispatch time, every configurable value is resolved through this cascade. The reconciler and dispatch code use resolveConfig() instead of reading from townConfig directly.
Rig Config Schema
// All fields optional — undefined means "inherit from town"
type RigConfig = {
// Models (overrides townConfig.default_model / townConfig.role_models)
default_model?: string;
role_models?: {
polecat?: string;
refinery?: string;
};
// Note: mayor is town-level only (mayor is per-town, not per-rig)
// Review mode (overrides townConfig.review_mode)
review_mode?: 'always' | 'never' | 'pr_only';
// Custom instructions (overrides townConfig.custom_instructions)
custom_instructions?: {
polecat?: string;
refinery?: string;
};
// Note: mayor instructions are town-level only
// Git behavior
git_push_flags?: string; // e.g. "--no-verify"
default_branch?: string; // override auto-detected default branch
// PR / review behavior
auto_resolve_pr_feedback?: boolean;
auto_merge_delay_minutes?: number | null;
// Agent limits
max_concurrent_polecats?: number; // default: unlimited
max_dispatch_attempts?: number; // override town-level max
// Convoy behavior
default_convoy_merge_mode?: 'review-then-land' | 'review-and-merge';
};Storage
Rig config is stored in the TownDO's SQLite alongside rig metadata. Options:
Option A: JSON column on the rigs table
ALTER TABLE rigs ADD COLUMN config TEXT DEFAULT '{}';The config column stores the RigConfig JSON. Parsed and validated with Zod at read time.
Option B: Separate rig_config table
CREATE TABLE rig_config (
rig_id TEXT PRIMARY KEY REFERENCES rigs(rig_id),
config TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL
);Recommendation: Option A — simpler, rig config is always read alongside rig metadata anyway.
UI: Rig Settings Page
/gastown/:townId/rigs/:rigId/settings (personal) or /organizations/:orgId/gastown/:townId/rigs/:rigId/settings (org)
The settings page mirrors the town settings structure but every field has an "Inherit from town" toggle (or a "Use default" option in dropdowns). When inheriting, the town's value is shown grayed out as a reference.
Rig Settings: acme-api
Models
Default Model [Inherit from town: kilo/balanced ▾]
Polecat Model [anthropic/claude-opus-4.6 ▾] ← overridden
Refinery Model [Inherit from town: kilo/frontier ▾]
Review Mode
● Inherit from town (Refinery reviews all changes)
○ Skip review — merge directly
○ PR only — no automated review
Custom Instructions
Polecat [Inherit from town ▾]
┌─────────────────────────────────────────┐
│ (town default shown grayed out) │
└─────────────────────────────────────────┘
[Override for this rig]
Refinery [Override ▾]
┌─────────────────────────────────────────┐
│ Fix linting errors during review. Do │
│ not request rework for style issues. │
└─────────────────────────────────────────┘
Git
Push flags [--no-verify ]
Default branch [Inherit from town (auto-detect) ]
PR Feedback
Auto-resolve [Inherit from town (disabled) ]
Auto-merge delay [Inherit from town (disabled) ]
Agent Limits
Max concurrent polecats [Inherit from town (unlimited)]
Max dispatch attempts [Inherit from town (5) ]
Convoy Defaults
Merge mode [Inherit from town (review-then-land)]
Dispatch Path Changes
Every place that reads town config for dispatch decisions needs to use the cascade:
| Current | Change |
|---|---|
townConfig.default_model |
resolveConfig(townConfig, rigConfig, 'default_model') |
townConfig.role_models?.polecat |
rigConfig?.role_models?.polecat ?? townConfig.role_models?.polecat ?? townConfig.default_model |
townConfig.review_mode |
resolveConfig(townConfig, rigConfig, 'review_mode') |
townConfig.custom_instructions?.polecat |
rigConfig?.custom_instructions?.polecat ?? townConfig.custom_instructions?.polecat |
The key call sites are:
buildKiloConfigContent()incontainer/src/agent-runner.ts— model resolutionapplyEvent('agent_done')inreconciler.ts— review mode checkapplyAction('dispatch_agent')inactions.ts— model + custom instructions injectionreconcileReviewQueueRule 5 — review mode check before popping MR beads
Navigation
Add a "Settings" link/button to the rig detail view (where agents and beads for that rig are shown). The rig settings page uses the same scrollspy sidebar pattern as town settings (#1417).
What Stays Town-Level Only
| Setting | Why |
|---|---|
| Mayor model / instructions | Mayor is per-town, not per-rig |
| Billing / spending cap | Billing is per-town (or per-org) |
| Wasteland connections | Federation is per-town |
| Slack integration | Slack channel maps to a town |
| GitHub OAuth token | Auth is per-town (user identity) |
Acceptance Criteria
-
RigConfigschema defined (all fields optional) - Config stored as JSON column on the rigs table
-
resolveConfig()utility function for cascading resolution - Rig settings page at
/gastown/:townId/rigs/:rigId/settings - Every field shows "Inherit from town" with the town value as reference
- Override toggle per field — set a rig-specific value or inherit
- Model, review mode, custom instructions, git flags, PR feedback, and agent limits configurable per rig
- Dispatch path uses cascaded config (not raw townConfig)
- Town-level settings remain the default when rig config is not set
- Mayor settings stay town-level only
- Works for both personal and org-scoped towns
References
- feat(gastown): Per-role model configuration in town settings — mayor, refinery, polecat #1512 — Per-role model configuration (mentions future per-rig override)
- feat(gastown): Make the refinery optional — allow polecats to merge directly without review #1739 — Optional refinery (mentions
rigConfig.review_modeoverride) - feat(gastown): Custom per-role prompt instructions in town settings #1794 — Custom per-role prompt instructions
- feat(gastown): Auto-resolve PR feedback — review comments and failing CI checks #1002 — Auto-resolve PR feedback + auto-merge (per-rig toggle)
- [Gastown] Support configurable refinery (reviewer) agent count per rig #1723 — Configurable refinery count per rig (agent limits)