# Grok wrapper + preset Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use /ship (recommended) or /run-plan to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add a WRAPPER-CONTRACT Grok engine (`wrappers/grok.sh`) plus adapter catalog entry and `presets/grok.json` so the harness can dispatch Grok seats.

**Architecture:** Headless `grok -p` with `--output-format streaming-json` and `--always-approve`, same contract shape as `ca.sh`/`codex.sh`. Dialect `grok` in events schema + normalize branch. Preset: coder tiers → `grok-composer-2.5-fast`; reviewer/fixer → `grok-4.5`.

**Tech Stack:** bash wrappers, Node normalize + presets validate, mock session tests.

**Spec:** `docs/specs/2026-07-10-grok-wrapper-design.md`

---

## Wave Plan

| Wave | Tasks | Files touched | Safe to parallelize? |
|------|-------|---------------|----------------------|
| 1 | Task 1, Task 2 | `spec/events.schema.json`, `lib/normalize-events.js`, `lib/test/*`, `wrappers/grok.sh`, `wrappers/test/grok-session.sh` | yes — zero file overlap |
| 2 | Task 3 | `presets/adapters.json`, `presets/grok.json` | single task (needs wrapper path + dialect) |

## Task 1: Dialect + normalize Grok streaming-json

**Wave:** 1  
**Blocks:** Task 3  
**Blocked by:** —

**Files:**
- Modify: `spec/events.schema.json` — add `"grok"` to `dialect.enum`
- Modify: `lib/normalize-events.js` — `normalizeGrok` + `case 'grok'`
- Create: `lib/test/fixtures/grok.jsonl` — sample stream lines
- Modify: `lib/test/normalize-events.test.js` — cover dialect grok

**Contract:**
- `dialect.enum` MUST include `"grok"` (exact string).
- `normalizeGrok(parsed, event)` maps:
  - `type:"text"` → `kind:"message"`, `subtype:"text"`, `text` from `parsed.data`
  - `type:"thought"` → `kind:"message"`, `subtype:"thought"`, `text` from `parsed.data`
  - `type:"end"` → `kind:"status"`, `subtype:"end"`
  - `type:"error"` → `kind:"error"`, `error:{code:"grok_error", message}` from `parsed.message` (fallback message if missing)
  - other known-ish types → leave `status`/`unknown` (do not throw)
- Wrapper-emitted `kind:"usage"` / `kind:"error"` still handled by existing `normalizeWrapperEvent` before dialect switch.
- Fixture ≥3 lines covering text + end + error.

**Behavior:**
- `normalizeEventLine('grok', line, n)` never returns `unknown_dialect`.
- Schema dialect pattern in tests still loads from schema file (auto-picks new enum).

**Acceptance:**
- Run: `node --test lib/test/normalize-events.test.js`
- Expected: exit 0, all tests pass including new grok coverage

- [ ] Write fixture + tests first (TDD)
- [ ] Implement enum + normalizeGrok
- [ ] Run acceptance
- [ ] Commit: `git add spec/events.schema.json lib/normalize-events.js lib/test/fixtures/grok.jsonl lib/test/normalize-events.test.js && git commit -m "feat: normalize grok streaming-json dialect"`

## Task 2: wrappers/grok.sh + session tests

**Wave:** 1  
**Blocks:** Task 3  
**Blocked by:** —

**Files:**
- Create: `wrappers/grok.sh` — contract wrapper
- Create: `wrappers/test/grok-session.sh` — mock-binary session tests

**Contract (CLI):**
```
wrappers/grok.sh --workspace <dir> --trust <prompt> --task-slug <slug> --model <id>
  [--timeout <secs>] [--resume <sessionId>]
wrappers/grok.sh --health
wrappers/grok.sh --list-models
```

**Behavior (MUST — pin; implementer models after `wrappers/codex.sh` / `wrappers/ca.sh`):**
- Exit codes: `0` / `124` / `2` / `3` / `75` per `spec/WRAPPER-CONTRACT.md`
- `--model` required on dispatch; no silent default
- Binary: `$_GROK_ENGINE_BIN` else `grok` on PATH
- Optional env: source first existing of `$_GROK_ENV`, `$REPO_ROOT/GROK.env`, `$HOME/.claude/workflows/lib/GROK.env`; existing file source-fail → exit 3; absent → ok
- Preflight: `"$BIN" version` (or `--version`) fails → exit 3, no dispatch
- `--health` → version probe JSON `{"ok":true,"version":"..."}` exit 0/3
- `--list-models` → run `"$BIN" models` proxy stdout
- Dispatch ONE args array for log+run:
  `timeout -k 5 $TIMEOUT $BIN -p $PROMPT -m $MODEL --cwd $WORKSPACE --output-format streaming-json --always-approve` + optional `--resume`
- Default timeout 360; log under harness tmp/logs with TMPDIR fallback; sanitize task-slug
- Tee to log + raw; tee `HARNESS_TRANSCRIPT_PATH` when set
- Rate-limit text → 75 + error event `errorKind:"quota"` + usage
- Always emit final usage event (`unknown:true` + model) on every exit path
- Success status JSON: `{"ok":true,"detail":"grok completed","session_id":"..."}` with sessionId from stream `end` event when present
- stdin `</dev/null`; foreground only

**Acceptance:**
- Run: `bash wrappers/test/grok-session.sh`
- Expected: all cases ok, exit 0

- [ ] Write mock session tests (fail against missing wrapper / incomplete)
- [ ] Implement `wrappers/grok.sh` (chmod +x)
- [ ] Run acceptance
- [ ] Commit: `git add wrappers/grok.sh wrappers/test/grok-session.sh && git commit -m "feat: add grok WRAPPER-CONTRACT engine wrapper"`

## Task 3: Adapter + preset

**Wave:** 2  
**Blocks:** —  
**Blocked by:** Task 1, Task 2

**Files:**
- Modify: `presets/adapters.json` — append grok adapter
- Create: `presets/grok.json` — grok-only preset

**Contract — adapter object:**
```json
{
  "id": "grok",
  "wrapper": "wrappers/grok.sh",
  "description": "Grok Build headless wrapper (grok -p).",
  "listModels": true,
  "metered": false,
  "models": ["grok-4.5", "grok-composer-2.5-fast"]
}
```

**Contract — preset** `name:"grok"`, `version:"preset/v1"`:
- `coder.low|medium|high`: wrapper `wrappers/grok.sh`, model `grok-composer-2.5-fast`, timeout 1200
- `reviewer` + `fixer`: wrapper `wrappers/grok.sh`, model `grok-4.5`, timeout 1200
- All three coder tiers present (validator rejects partial tier maps)

**Behavior:**
- No edits to anthropic-less / other presets
- `node presets/_validate.mjs` passes

**Acceptance:**
- Run: `node presets/_validate.mjs`
- Expected: prints validated preset count including grok; exit 0

- [ ] Add adapter + preset
- [ ] Run acceptance
- [ ] Commit: `git add presets/adapters.json presets/grok.json && git commit -m "feat: register grok adapter and preset"`
