Joelclaw contributing-to-pi
Contribute fixes, bug reports, and upstream discussions to badlogic/pi-mono without wasting maintainer time. Use when filing pi issues, preparing pi PRs, debugging whether a bug belongs upstream, or responding to maintainer pushback. Triggers on: 'contribute to pi', 'pi-mono issue', 'upstream pi fix', 'open a pi issue', 'why did Mario reject this', or any work in ~/Code/badlogic/pi-mono meant for upstream.
git clone https://github.com/joelhooks/joelclaw
T=$(mktemp -d) && git clone --depth=1 https://github.com/joelhooks/joelclaw "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/contributing-to-pi" ~/.claude/skills/joelhooks-joelclaw-contributing-to-pi && rm -rf "$T"
skills/contributing-to-pi/SKILL.mdContributing to pi
This skill exists to stop us sending half-baked upstream reports to Mario.
The maintainer standard is reasonable: understand the bug, isolate the boundary that actually breaks, and show a concrete repro. If we can't do that yet, we are still in local debugging mode — not upstream contribution mode.
When to Use
Use this skill when:
- working in
~/Code/badlogic/pi-mono - filing or updating an issue on
badlogic/pi-mono - preparing a PR meant for upstream
- deciding whether a failure belongs in pi core, a provider adapter, an extension, or our local patches
- responding to maintainer questions like "which provider/model triggered this?" or "how is this possible?"
Read These First
Always read both files in the local clone before touching upstream threads:
~/Code/badlogic/pi-mono/CONTRIBUTING.md~/Code/badlogic/pi-mono/AGENTS.md
The important bits:
- first-time contributors open an issue first and wait for maintainer approval (
)lgtm - issue and PR text must be concise and written in a human voice
- new issues should carry the right
labelspkg:* - after code changes run
npm run check - do not run the repo's blanket
npm test - if you need tests, run targeted tests from the relevant package root
For the repo/maintainer deep dive, read:
references/pi-mono-research.md
Non-Negotiables
1. Reproduce on clean upstream before claiming a core bug
If the failure only happened in one of these environments, say that plainly and do more work before filing upstream:
- local repo build wired into
~/.local/bin/pi - globally patched installs under
~/.bun/install/global/... - sessions with custom extensions enabled
- branches with local instrumentation or emergency guards applied
Use a clean worktree when in doubt:
cd ~/Code/badlogic/pi-mono git fetch origin git worktree add /tmp/pi-mono-main origin/main cd /tmp/pi-mono-main npm install
If the bug disappears on clean
origin/main, it is not yet an upstream core bug.
2. Identify the broken boundary, not just the crash site
"It crashed in
packages/agent/src/agent-loop.ts" is not enough.
If a type-level invariant says the state is impossible, prove where reality violated the invariant:
- provider adapter emitted an invalid
AssistantMessage - extension mutated message state incorrectly
- local patch/build mismatch changed runtime behaviour
- external API returned something the adapter normalized badly
Upstream wants the cause chain, not just a defensive
?? [] around the symptom.
3. Capture provider/model and runtime conditions every time
For any LLM/runtime bug, record:
- provider
- model
- command path (
, local repo build, published install)pi - whether extensions were enabled
- relevant local patches
- commit SHA / branch
If you cannot answer "which provider/model triggered this?", you are not ready to open the issue.
4. Do not open a PR before the issue gets approval
CONTRIBUTING.md is explicit. New contributors start with an issue. A PR opened before approval is churn and will be closed.
5. Repeated AI slop after maintainer feedback can get you banned
Mario made this explicit on X on 2026-03-09 while linking issue #1993:
you will be banned from the pi-mono repo if:
- you are a dick
- you keep submitting clanker slop repeatedly for the same "issue" to which you got a reply and workaround from yours truely there is no way to appeal my decision.
Source: https://x.com/badlogicgames/status/2031085220221563021
Treat that as policy, not vibes.
If a maintainer already gave a workaround, explanation, or boundary call, do not re-litigate the same thing with a slightly reworded agent-generated issue. Either bring a new repro with stronger evidence, or shut up and go debug more.
Workflow
Step 1: confirm this belongs upstream
Ask, in order:
- Does the failure reproduce on clean
?origin/main - Does it reproduce with extensions disabled?
- Does it reproduce with the published build, not just our repo-wired binary?
- Can I point to the exact layer that produced the bad state?
If any answer is "no" or "not sure", keep digging locally.
Step 2: gather the evidence pack
Before filing an issue, collect this in a scratch note:
- one-sentence problem statement
- exact repro steps from repo root
- provider/model
- extension state (
repro or not)--no-extensions - clean vs patched build
- expected result
- actual result
- minimal log or message payload showing the bad state
- hypothesis for where the invariant broke
- package ownership (
,pkg:agent
,pkg:ai
, etc.)pkg:coding-agent
For GitHub reading, use the maintainer-friendly command from
AGENTS.md:
gh issue view <number> --json title,body,comments,labels,state
Read all comments before replying.
Step 3: reduce the repro
Strip it down until another person can run it without our whole machine state.
Good repros usually look like one of these:
- a small failing test in the affected package
- a fixture that produces the invalid normalized message
- a short command sequence from repo root
- an extension-disabled repro plus a separate note saying extensions make it worse
Bad repros look like:
- "during a longer session"
- "while chasing compaction"
- "somehow got a malformed message"
- "here's the guard I already wrote"
Step 4: write the issue like an adult
Keep it short. Human voice. No agent mush.
Use this shape:
Problem - One sentence describing the failure. Repro 1. Step one 2. Step two 3. Step three Environment - provider/model: - build: clean origin/main | local repo build | published install - extensions: on/off Expected - ... Actual - ... Hypothesis - The invariant appears to break in <provider|adapter|extension|core> because ...
Add the right
pkg:* labels.
If you comment on an issue or PR, write the comment to a temp file first and preview it before posting.
Step 5: only then propose a fix
Once the maintainer agrees the bug is real and upstream-owned:
- keep the patch minimal
- add a failing regression test or fixture that proves the bug
- run
npm run check - run only the targeted tests needed by the touched package
- avoid drive-by fixes or policy changes in the same patch
Lesson from pi-mono issue #1899
What we got wrong:
- we reported the crash site in
packages/agent/src/agent-loop.ts - we proposed a narrow guard immediately
- we did not include provider/model
- we did not provide concrete repro steps
- we did not explain whether the failure came from clean upstream, our repo-wired local pi build, our extensions, or our patched install
- we already had local evidence that this class of
crash also showed up in patched runtime paths aroundcontent.filter()
, which pointed to a broader boundary problem than the issue body admittedgrind_stop
Mario's pushback was correct:
- on the type level,
with missingtoolUse
should be impossiblecontent - without a repro, a guard patch looks like papering over an unknown lower-layer bug
- the likely fault domain is provider normalization or extension/runtime mutation until proven otherwise
Future rule:
If the maintainer can reasonably ask "how can this state even exist?", answer that before opening the issue.
Package Triage Heuristic
Use this to decide where a bug probably belongs:
— provider adapters, streaming normalization, tool-call event translation, malformed usage/stop reasonspackages/ai
— agent loop state machine, tool execution, context handling after normalized messages already existpackages/agent
— interactive mode, compaction UX, session persistence, slash commands, extension interaction surfacespackages/coding-agent- local extensions / joelclaw runtime — if behaviour changes only with our extensions or patched local install
Don't dump provider or extension bugs into
packages/agent because that is where the null dereference landed.
Pre-Submit Checklist
- Read
CONTRIBUTING.md - Read
AGENTS.md - Reproduced on clean
origin/main - Captured provider/model
- Captured extension state
- Captured build path (published vs local repo build)
- Identified likely broken boundary
- Wrote concise human issue body
- Added
labelspkg:* - Waited for maintainer approval before opening a PR
Don't Do This
- don't lead with the patch before the repro
- don't call something a core bug because a null dereference happened in core
- don't hide the fact that we run local patches or repo-wired binaries
- don't dump session-length narrative into the issue body
- don't use GitHub issues as a debugging notebook
- don't make Mario reverse-engineer our machine state