Skip to content

Grok transfer lesson: the same weatherwise program on a second model #3

Description

@rdtiv
driver: grok
ref: (this issue)
worktrees: (not yet provisioned)
branches: (not yet)
port: (none — CLI lesson, no dev server)

How to start (fresh session only)

Do not implement from the planning session that filed this. New Grok Build session, repo root.

tirocine does not yet have .grok/skills/xmission/. Claude /mission is not the contract. Fetch the Grok contract from rdtiv/1269j .grok/skills/xmission/SKILL.md and follow it.

First package of this kickoff: port a thin .grok/ into tirocine (xmission skill + dual-driver rule + scripts/landed.sh). Drop 1269j’s operator-exec.md and site-law.md. Add the namespace table to CLAUDE.md. Then provision:

.grok/worktrees/wt-<this#>-grok-lesson
branch: x/feat/grok-transfer
base: origin/main

Issue #1 / PR #2 is the Claude Python mission (.claude/worktrees/wt-1-python-build/). Do not touch that worktree. Namespaces:

Driver Command Worktrees Branches Ports
Claude /mission .claude/worktrees/ house scheme 3000–3999
Grok /xmission .grok/worktrees/ x/<type>/<slug> 4000–4999

This lesson has no web server; record port: none.

Grok does not reliably expand $ARGUMENTS. This issue body is the source of truth.


Goal

A developing programmer who finished docs/typescript.md rebuilds the same weather assistant against Grok and can point at every difference. Which ideas were Claude, and which were real.

Python (issue #1) remains “which ideas were TypeScript?” This document stays in TypeScript on purpose.

Not a 13-part Grok clone. Not a second npm package. Not the app / AI SDK chapter.

Researched 2026-08-14 against docs.x.ai and rdtiv/1269j.


Decisions (locked)

Decision Choice Why
Layout src/grok-*.ts next to the Claude files Same cwd, same .env, same tsx --env-file=.env. Split-pane comparison.
Package One package.json, one tsconfig.json Root grows openai. Claude-only readers install it and never import it.
.env One file at repo root; add XAI_API_KEY Never --env-file=../.env. Never copy keys.
Run Always from repo root "grok:agent": "tsx --env-file=.env src/grok-agent.ts"
SDK openai + Responses API xAI’s preferred JS path. baseURL: 'https://api.x.ai/v1' is the first beat.
Not used @ai-sdk/xai, Chat Completions as the main path AI SDK hides the tool loop (docs/app.md). Chat Completions is a footnote.
Shared src/weather.ts, src/usage.ts, src/usage-report.ts Weather was never Anthropic. One ledger, both vendors.
Not shared src/text.ts, src/config.ts Claude-typed. Grok gets grok-text.ts and grok-config.ts.
Series slot New document 3; Python becomes 4; app becomes 5 Model-switch before language-switch.

August 2026 research (re-check before pinning prices or IDs)

Use these

  • Model: grok-4.6 (docs: default for code). Pin like claude-sonnet-5. Knowledge cutoff 2026-02-01.
  • Key / host: XAI_API_KEY, https://api.x.ai/v1, console.x.ai.
  • First call:
const client = new OpenAI({
  apiKey: process.env.XAI_API_KEY,
  baseURL: 'https://api.x.ai/v1',
});
const response = await client.responses.create({
  model: 'grok-4.6',
  input: '...',
});
console.log(response.output_text);
  • Prices (grok-4.6, prompt < 200k): $2.00 / 1M input, $0.50 / 1M cached input, $6.00 / 1M output. At ≥200k the entire request doubles. Implement the cheap tier; comment the threshold; date Verified 2026-08-14. Sources: models, pricing.
  • Responses usage: input_tokens, output_tokens, input_tokens_details.cached_tokens, output_tokens_details.reasoning_tokens.
  • Caching is automatic. No cache_control, no 1.25× write, no 1,024 minimum. Responses sticky key: prompt_cache_key via extra_body (official JS uses @ts-expect-error — do not leave that in a beginner file). prompt caching.
  • Memory fork: server store 30 days by default; store: false is local-only; previous_response_id continues a stored turn. generate text.
  • Client tools: { type: "function", name, description, parameters }. Output item function_call, arguments is a JSON string, call_id. Return { type: "function_call_output", call_id, output }. function calling.
  • Server-side search: { type: "web_search" }. xAI runs it. No runTool. Citations possible. $5 / 1k invocations + tokens. web search. First-class file, not a footnote.
  • Structured output: same Zod schema, different envelope. structured outputs.

Not 1:1 with Claude

Claude Grok
cache_control + 1.25× / 0.1× Automatic prefix cache; $0.50 vs $2.00 = 0.25×, no write premium
input_tokens is uncached remainder Responses input_tokens is documented as the full prompt; cached_tokens is a subset. Adapter must subtract. Confirm with one live call.
You always resend history Optional
Model never executes anything True of your functions. False of web_search.
max_tokens required Optional

Confirm live before locking

  1. Streaming + Responses on api.x.ai. Official streaming JS is Chat Completions. openai supports responses.create({ stream: true }). Try Responses first; if it fails, Chat Completions and one honest sentence. Do not teach both as equals.
  2. responses.parse + Zod vs text.format + parse. Same WeatherRequest either way.
  3. prompt_cache_key typesextra_body, no @ts-expect-error in lesson files.
  4. openai version — add current; typecheck is the pin.
  5. web_search output item names + citation field — print what the live API returns.

Files the reader builds

File Claude twin Teaches
src/grok-config.ts config.ts MODEL = 'grok-4.6'
src/grok-text.ts text.ts output is typed items; output_text vanishes on function_call
src/grok-index.ts index.ts Client + baseURL; print raw object, then text
src/grok-chat.ts chat.ts store: false + local array, then previous_response_id
src/grok-parse.ts parse-request.ts Identical WeatherRequest Zod object
src/grok-agent.ts agent.ts Hand-written loop; same getWeather; JSON.parse(arguments)
src/grok-search.ts (none) web_search alone, then web_search + get_weather
src/grok-assistant.ts assistant.ts Finished program. Local weather only.
src/grok-stream.ts stream.ts Short coda. Path from live probe.
src/grok-injection.ts injection.ts Same POISON, same moral. Not a fix.
src/grok-models.ts models.ts Documented extra.

No files for: truncate (one paragraph), bench, weather-test, a second weather client, a Grok usage-report, retries as a part (timeout: 360_000 comment only).


Mapping table (top of docs/grok.md)

Idea Claude Grok (Responses)
Client new Anthropic() new OpenAI({ apiKey, baseURL: 'https://api.x.ai/v1' })
Key ANTHROPIC_API_KEY XAI_API_KEY
The call messages.create responses.create
What you send messages + top-level system input (system is an item)
The reply content[] output[] (message, function_call, reasoning)
The text textFrom() output_text or grok-text.ts
Memory Resend everything Resend or previous_response_id
Wants a tool stop_reason === 'tool_use' output item type === 'function_call'
Tool request tool_use + input object function_call + arguments string
Tool result user tool_result + tool_use_id function_call_output + call_id
Schema field input_schema parameters
Who runs it Always you You (function) or xAI (web_search)
Structured out messages.parse + zodOutputFormat Responses format — same Zod
Cache explicit marker, 1.25× / 0.1× automatic; prompt_cache_key; $0.50 vs $2.00

If a difference is not in this table, the table is wrong.


Two ideas that are actually new

  1. Someone still has to remember. It does not have to be you. store: false first, then previous_response_id. Watch usage in both modes.
  2. Some tools run on their servers. Full section + src/grok-search.ts.

grok-search.ts

  1. Search only: tools: [{ type: "web_search" }]. Current headline, not weather. One responses.create. Print raw output, text, citations. No while loop.
  2. Both tools: web_search + same get_weather schema. Question that wants both. Loop fires only for function_call.

Do not put web_search on grok-assistant.ts. No image search / image understanding / x_search. Do not add the $5 / 1k fee to usage.csv; say the row is missing it.


Ledger

Do not fork usage.csv. Do not change the 15 columns.

type LedgerUsage = {
  input_tokens: number;   // uncached remainder (keep Claude’s convention)
  cache_read: number;
  cache_write: number;    // always 0 for Grok
  thinking_tokens: number;
  output_tokens: number;
};
  • fromAnthropic — current math, extracted.
  • fromResponses — treat cached_tokens as a subset of input_tokens unless a live call proves otherwise.
  • Grok costOf: uncached * 2 + cached * 0.50 + output * 6 per million. Comment 200k doubling; do not implement it.
  • logCall stays Claude. Grok files call logGrokCall (no Anthropic types).
  • usage-report.ts: do not apply 0.1× / 1,024-token copy to mixed or Grok rows. Savings from cost_usd.

docs/grok.md shape

Transfer voice, not a second course. Type the examples. Open the Claude twin beside each file.

  1. What this is / is not / prerequisite / cost + spend limit
  2. Mapping table
  3. Key + openai install
  4. First call
  5. Memory fork
  6. Same Zod schema
  7. Tool loop
  8. Who runs the tool (grok-search.ts)
  9. Finished assistant (local weather only)
  10. Caching note (not Part 11)
  11. Streaming coda
  12. Injection coda
  13. Where this sits

Series nav on every existing series doc becomes: Setup → TypeScript (Claude) → Grok → Python → App. Python and app keep their gap banners.

Fence markers: same as typescript.md (// File — src/grok-agent.ts, etc.).


verify:docs

Today, adding grok-*.ts fails CI. Ownership:

Files Owner Extra
src/*.ts except grok-* docs/typescript.md src/models.ts
src/grok-*.ts docs/grok.md src/grok-models.ts

Run the extract → classify → compile staged → diff → coverage pipeline twice. openai must be a root dep before the first grok-*.ts lands.


Other required edits

  • package.json: openai; scripts grok, grok:chat, grok:parse, grok:agent, grok:search, grok:assistant, grok:stream, grok:injection, grok:models
  • .env.example: XAI_API_KEY — optional; Claude scripts must work without it
  • README series + scripts tables; three keys (Grok optional)
  • CLAUDE.md: unprefixed = Claude, grok-* = Grok; fix stale cost.ts / logCostusage.ts / logCall
  • Setup docs: one short optional-third-key note
  • CI commands unchanged; still keyless

Invariants

  • Comments explain why, in the existing lesson voice.
  • ESM imports keep the .js extension.
  • Do not merge Claude and Grok assistants.
  • Do not present BOUNDARY as a security control.
  • Do not use the AI SDK in this document.
  • A reader can open src/agent.ts beside src/grok-agent.ts and point at every difference.

Out of scope

Python companion code (issue #1). The Next.js app. Workspaces / second package / grok/ folder. Tool-invocation fees in the CSV. web_search on the assistant. 200k price branch. A third provider. Runtime tests (optional keyless costOf check only).


Implementation order

  1. Port thin .grok/ from 1269j (skill + dual-driver + landed.sh + CLAUDE.md table).
  2. Scaffold (keyless): openai, ledger adapters, usage-report copy, check-docs two-document ownership, scripts, .env.example, series nav.
  3. Live probe (XAI_API_KEY, not committed): usage semantics, store: false, function_call, streaming, web_search shape. Lock adapters. Delete scratch.
  4. Lesson files + docs/grok.md together so verify:docs stays green.
  5. Surface docs (README, CLAUDE.md, setups).
  6. Verify: npm run typecheck, npm run verify:docs; one live grok + grok:agent + grok:search if a key is present. Do not loop billed calls.

Acceptance

  • Thin .grok/ exists; /xmission is the Grok contract; Claude worktrees untouched
  • src/grok-*.ts exist and are built by docs/grok.md
  • npm run verify:docs covers both documents; CI green on 20.x and 22.x, still keyless
  • One usage.csv; Grok rows cache_write 0; Claude logCall unchanged
  • WeatherRequest schema matches parse-request.ts
  • Mapping table is accurate against the code
  • npm run chat / npm run agent still work
  • README series table lists the Grok document as item 3

Success

A reader finishes the TypeScript build, runs npm run grok:assistant the same evening, opens src/assistant.ts beside src/grok-assistant.ts, and can explain the mapping table — including the two ideas that were not just renaming.

Notes

  • main is protected. PRs required.
  • Tutorial code: clarity and pedagogical comments beat production abstraction.
  • docs/typescript.md is the model for structure, voice, and fence markers.
  • Re-check docs.x.ai on the day you pin prices and model IDs.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions