Background
`runnerKind` is currently typed as plain `string` across the dispatcher:
- `types.ts` AttemptRecord, RunSpec → `runnerKind: string`
- `schema.sql` attempts.runner_kind → TEXT (no CHECK)
- `idempotency.ts` payload format includes runnerKind unconstrained
Surfaced by Grok 4 batch review of Week 1 Foundations: this is a locked-in API decision that Tasks 11+ (RunnerInterface, ClaudeRunner, CodexRunner) will inherit. A typo or stale literal silently produces a wrong sha256 idempotency key with no compile-time protection.
Proposed fix
After Task 11 ships RunnerInterface, refactor:
```typescript
export type RunnerKind = "claude" | "codex" | "concurrency-gate";
export interface RunSpec {
runnerKind: RunnerKind;
}
```
Plus a CHECK constraint on `schema.sql attempts.runner_kind`:
```sql
runner_kind TEXT NOT NULL CHECK (runner_kind IN ('claude','codex','concurrency-gate'))
```
Migration: existing rows are already in the union (only "claude" + "concurrency-gate" used in Week 1), so a CHECK ADD is non-breaking.
When
After Task 11 (RunnerInterface) defines the canonical runner kind set. Don't do this earlier — risk of premature enum lock-in.
Reference
- Branch: `oit/dispatcher-stage-1`
- Source: `src/adapters/oit/dispatcher/types.ts`, `src/adapters/oit/dispatcher/state/schema.sql`, `src/adapters/oit/dispatcher/idempotency.ts`
Background
`runnerKind` is currently typed as plain `string` across the dispatcher:
Surfaced by Grok 4 batch review of Week 1 Foundations: this is a locked-in API decision that Tasks 11+ (RunnerInterface, ClaudeRunner, CodexRunner) will inherit. A typo or stale literal silently produces a wrong sha256 idempotency key with no compile-time protection.
Proposed fix
After Task 11 ships RunnerInterface, refactor:
```typescript
export type RunnerKind = "claude" | "codex" | "concurrency-gate";
export interface RunSpec {
runnerKind: RunnerKind;
}
```
Plus a CHECK constraint on `schema.sql attempts.runner_kind`:
```sql
runner_kind TEXT NOT NULL CHECK (runner_kind IN ('claude','codex','concurrency-gate'))
```
Migration: existing rows are already in the union (only "claude" + "concurrency-gate" used in Week 1), so a CHECK ADD is non-breaking.
When
After Task 11 (RunnerInterface) defines the canonical runner kind set. Don't do this earlier — risk of premature enum lock-in.
Reference