opensource-contributions
Make good open source contributions — check CONTRIBUTING.md first, follow project norms, be a good citizen. Covers bug reports, feature requests, and pull requests with a defensible default posture when the project hasn't documented expectations. Do not use this skill for unrelat
Install
npx skills add https://github.com/magnus919/agent-skills/tree/main/opensource-contributions
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install magnus919-agent-skills@llmmart
git clone https://github.com/magnus919/agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole magnus919/agent-skills collection as a plugin from our marketplace. Git is the plain clone.
README
Open Source Contributions — Be a Good Citizen
Make good open source contributions — check CONTRIBUTING.md first, follow project norms, be a good citizen. Covers bug reports, feature requests, and pull requests.
Why Install This Skill
When your agent loads this skill, it becomes a model open source contributor who knows how to:
- Read the room — check CONTRIBUTING.md, check existing issues/PRs before acting
- File great bug reports — minimal reproduction cases, clear steps, expected vs actual behavior
- Make good PRs — right-sized scope, clean commits, proper messaging
- Handle review gracefully — respond to feedback, update PRs, know when to follow up
- Work without guidelines — defensible defaults when the project hasn't documented expectations
- Disclose AI assistance — proper agent disclosure on AI-assisted contributions
What You Get
| Directory | Purpose |
|---|---|
SKILL.md |
Golden rule, phase overview, default posture, agent checklist |
references/ |
8 phase references + agent checklist + pitfalls + default posture (comprehensive guidance for every stage of contribution) |
Triggers
Load this when filing an issue, opening a PR, or contributing to any open source project.
Requirements
None. Works with any agent supporting the Agent Skills format.
Quick Start
Start with the setup and first workflow in SKILL.md, then use the linked resources for the specific task you need to complete.
Skill manifest
Open Source Contributions
The Golden Rule
Make it easy for maintainers to absorb your contribution. Maintainers are often under-resourced volunteers or small teams. Every friction point you remove — unclear reproduction steps, missing tests, bad commit messages, force-pushed history — is time they don't have to spend figuring out what you did and why. The whole point is to help them, not create more work.
When to Use This Skill
- Filing a bug report or feature request on a public repository
- Preparing a pull request for any open source project
- Working on a project without documented contributing guidelines (default posture)
- Reviewing your own PR before submission
- Setting up open source conventions for your own project
Load the relevant phase reference for detailed instructions.
Quick Phase Overview
| Phase | What It Covers | Reference |
|---|---|---|
| 0a — Maintainer Conventions | README discipline, LICENSE, CONTRIBUTING.md, issue/PR templates, commit conventions, DCO, release workflow hygiene | references/phase-0a-maintainer-conventions.md |
| 0 — Before You Start | Reading CONTRIBUTING.md, checking existing issues/PRs, triaging bugfix candidates, large-change discussion etiquette | references/phase-0-before-you-start.md |
| 1 — Filing Issues | Bug report and feature request templates, agent disclosure, maintainer workflow ("issue first"), coordinated multi-issue roadmaps, multi-PR plan execution | references/phase-1-filing-issues.md |
| 2 — Pull Requests | Branching conventions, scope assessment, studying existing implementations, cross-repo comparison, commit messages, PR templates, documentation audits, CI setup | references/phase-2-pull-requests.md |
| 3 — After Submitting | CI monitoring, responding to review feedback, what to do if your PR goes stale or gets closed | references/phase-3-after-submitting.md |
| 3.5 — Follow-up After Scope Feedback | Systematic call-site audits, filing comprehensive issues, complementary PRs when maintainer scope notes identify gaps | references/phase-35-followup.md |
| 4 — Release Process | Version bumping, tagging, GitHub Releases vs tags, release workflow anatomy, handling failed releases | references/phase-4-release-process.md |
Default Posture (No CONTRIBUTING.md)
When a project has no contributing guide, load references/default-posture.md for defensible defaults on issue filing, PRs, communication norms, and code of conduct.
Agent-Specific Checklist
If you are an AI agent filing or contributing on behalf of a human, load references/agent-checklist.md before submitting anything.
Pitfalls
Load references/pitfalls.md when you're about to submit an issue or PR, or when something goes wrong. Covers: backtick expansion, silent label failures, force-push etiquette, the "I'll just fix it quickly" trap, cross-fork PR issues, post-merge scope creep, the installed-code trap, CI debugging, and more.
The Agent-Specific Rule
When filing an issue on behalf of a human, always disclose the agentic nature. Add this line at the bottom of the issue body:
Filed by {{AGENT_NAME}} (AI agent on behalf of {{HUMAN_NAME}})
This is a transparency requirement, not a courtesy. Maintainers deserve to know who they're talking to.
Quick Reference Card
| Step | Action | Command / Check |
|---|---|---|
| 0 | Read contributing guide | cat CONTRIBUTING.md or .github/CONTRIBUTING.md |
| 0 | Check existing issues | gh issue list --search "topic" --state all |
| 1 | File a bug/feature | Use template if provided; include reproduction for bugs |
| 2 | Issue first before coding | File issue, wait for maintainer feedback, then branch |
| 3 | Branch | git checkout -b fix/description |
| 4 | Commit | git commit -s -m "fix: description" |
| 5 | Run tests locally | make test or npm test or pytest |
| 6 | Push | git push -u origin HEAD |
| 7 | Open PR | Fill out PR template completely. Use --body-file for complex bodies |
| 8 | Monitor CI | gh pr checks --watch |
| 9 | Address review | Respond to comments, push fixup commits. No force-push after review |
| 10 | After merge | git checkout main && git pull && git tag vX.Y.Z && git push origin main --tags |
| 11 | Create Release | gh release create vX.Y.Z --title "vX.Y.Z — Title" --notes-file /tmp/notes.md |
When NOT to Use This Skill
- You're the sole maintainer of a project with no external contributors and no public collaborators
- You're making a trivial single-line fix to your own code
- The contribution is internal (same organization, same team) with established workflow norms
- You already know the project's contributing guidelines by heart and this is routine
- The issue is a security vulnerability — follow the project's security disclosure policy instead
Files (agent-skills)
-
evals
-
evals.json 3 KB
{ "schema_version": 1, "skill_name": "opensource-contributions", "evals": [ { "id": "opensource-contributions-core-workflow", "prompt": "Use opensource contributions to handle a realistic primary task. Explain the inputs, ordered workflow, and concrete output.", "expected_output": "A opensource contributions response defines the task boundary, identifies required inputs, applies the documented workflow, and produces a concrete output with verification.", "assertions": [ "Names the opensource contributions task and required inputs", "Applies an ordered workflow rather than generic advice", "Produces a concrete output and verification step" ] }, { "id": "opensource-contributions-failure-diagnosis", "prompt": "A opensource contributions task is failing with an ambiguous symptom. Diagnose it and give a bounded recovery path.", "expected_output": "The response separates symptoms from causes, proposes evidence-gathering checks, and gives a reversible recovery path with a stop condition.", "assertions": [ "Separates symptom, hypothesis, and evidence", "Uses targeted diagnostic checks", "Includes a reversible recovery and stop condition" ] }, { "id": "opensource-contributions-safety-boundary", "prompt": "Plan a opensource contributions change that could affect user data or external state. Show the safety gate before acting.", "expected_output": "The response confirms scope and authority, defaults to read-only or dry-run inspection, and requires explicit confirmation before consequential mutation.", "assertions": [ "Confirms target, scope, and authority before mutation", "Uses read-only or dry-run inspection first", "Requires explicit confirmation for consequential changes" ] }, { "id": "opensource-contributions-edge-case", "prompt": "Apply opensource contributions when requirements conflict or an important input is missing. Decide what to do next.", "expected_output": "The response identifies the missing or conflicting constraint, refuses to invent facts, and escalates or requests the smallest clarifying input needed.", "assertions": [ "Identifies the missing or conflicting constraint", "Does not invent unavailable facts", "Requests clarification or escalates with a bounded next step" ] }, { "id": "opensource-contributions-evidence-handoff", "prompt": "Create a review-ready opensource contributions handoff for another practitioner.", "expected_output": "The handoff records assumptions, decisions, artifacts, validation evidence, and unresolved risks so another practitioner can reproduce the result.", "assertions": [ "Records assumptions and decisions", "Links concrete artifacts to validation evidence", "States unresolved risks and reproducible next steps" ] } ] }
-
-
references
-
agent-checklist.md 1.6 KB
## Agent-Specific Checklist When making a contribution on behalf of someone else: - [ ] **CONTRIBUTING.md checked** at repository root (and fallback locations) - [ ] **Existing issues searched** before filing anything new — on the current repo AND on any target repo before redirecting - [ ] **Existing PRs checked** on the target repo too — the fix may already exist — on the current repo AND on any target repo before redirecting - [ ] **Existing PRs checked** on the target repo too — the fix may already exist - [ ] **For large changes:** discussed with maintainers before implementing - [ ] **Agent disclosure** included in issue/PR body - [ ] **PR/issue template compliance** — fetched project template (`.github/PULL_REQUEST_TEMPLATE.md` for PRs, `.github/ISSUE_TEMPLATE/` for issues), body matches required structure - [ ] **PR compliance checker** — `check-pr-template-compliance.py` exits 0 before PR submission - [ ] **Commits authored in the human contributor's name** (`git commit --author="Human Name <email>"` if the agent writes the code directly — but preferably the human writes/approves the commits) - [ ] **Tests pass** before opening PR - [ ] **No force-push** unless the project's contributing guide explicitly asks for rebased history - [ ] **CI monitored** after PR submission, failures fixed promptly - [ ] **Single issue per PR** — if a separate fix is discovered while a PR is open, create a NEW branch from main, new issue, and new PR. Do NOT push to the open PR's branch. - [ ] **Review feedback addressed** — each comment gets a response or action --- -
ci-debugging-loop.md 9.1 KB
# CI Debugging Loop A systematic approach to fixing CI failures on a Python project. Use this anytime CI turns red after a push. ## Step 1: Read the CI Logs ```bash # Get the failed run ID gh run list --limit 3 --json conclusion,displayTitle,status,databaseId # View failed steps gh run view <RUN_ID> --log-failed # View full output for a specific job gh run view <RUN_ID> --log ``` **What you're looking for:** The exact test name that failed, the assertion error, and any clues about why (env var mismatch, missing method, import error, etc.). ## Step 2: Reproduce Locally ```bash # Reproduce just the failing test pytest -xvs tests/test_file.py::test_name # If it passes locally but fails in CI, check for environment differences: # - Python version (CI may use 3.11 vs your 3.13) # - Installed dependencies (CI installs fresh; you may have stale packages) # - Environment variables (CI sets HF_HUB_OFFLINE=1, etc.) # - Entry-point metadata (`pip install -e .` may cache old entry points) ``` ## Step 3: Isolate Pre-existing vs New Failures Before assuming your change broke something, check if the test was already failing: ```bash # Stash your changes, run the failing test on clean main git stash pytest -xvs tests/test_file.py::test_name git stash pop ``` | Result | Diagnosis | |--------|-----------| | Fails on clean main too | **Pre-existing** — your change didn't cause it. Fix separately or skip. | | Passes on clean main, fails with your changes | **Regression** — your change caused it. Debug the interaction. | | Different test fails on clean main | **Test ordering / flaky** — env sensitivity or timing race. Run again. | ## Step 4: Root Cause Categories ### Env Var Leakage The user's development environment may set env vars that leak into tests. Common culprits: - `CASHEW_*` vars from a running Hermes session override config defaults in tests - `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc. causing unintended API calls - Path vars that influence import resolution **Fix pattern:** Add an autouse fixture in `conftest.py` that strips the leaking vars: ```python @pytest.fixture(autouse=True) def _clear_leaking_env_vars(monkeypatch: pytest.MonkeyPatch): for key in list(os.environ): if key.startswith("CASHEW_"): monkeypatch.delenv(key, raising=False) ``` Mirror the existing pattern already used by the project (e.g., `HF_HUB_OFFLINE=1` in conftest). ### Stale Package Metadata `pip install -e .` installs entry point metadata that persists across source changes. If you change `pyproject.toml`'s entry point but don't reinstall, the old metadata is used: ```bash # Check what's actually registered python3 -c "import importlib.metadata as im; eps=list(im.entry_points(group='hermes_agent.plugins')); [print(f'{ep.name} = {ep.value}') for ep in eps]" ``` **Fix:** Update the installed entry point metadata directly, or reinstall: ```bash # Option A: Patch the installed entry_points.txt # Find the dist-info directory: python3 -c "import importlib.metadata as im; d=im.distribution('your-package'); print(d._path)" # Edit entry_points.txt in that directory # Option B: Reinstall pip install -e ".[dev]" --force-reinstall --no-deps ``` **Specific gotcha with entry point suffixed targets:** An entry point declared as `module.path` (without `:suffix`) resolves to a **module** via `ep.load()`. If declared as `module.path:function`, it resolves to the **function**. If you switch between these, any tests that call `ep.load()` and expect a particular type will break. On macOS, stale dist-info may retain the old format — always check with `python3 -c "import importlib.metadata as im; ..."` to see what's actually registered. ### Removed/Refactored Methods After a major refactor, tests that reference old method names or monkeypatch old internals will fail: ```bash # Check if the method still exists python3 -c "from your_module import YourClass; print(hasattr(YourClass, 'old_method_name'))" ``` **Fix pattern:** Update the test to use the new API surface. If mocking internal methods, mock at the boundary instead (the public API or the dependency's interface). ### Immutable Types (Python 3.11+) On modern Python, some C-level types (notably `sqlite3.Connection`) are immutable. `monkeypatch.setattr` appears to succeed but the teardown raises `TypeError`: ```python # Detection: try to set a dummy attribute try: sqlite3.Connection._test_mutability = lambda: None del sqlite3.Connection._test_mutability except TypeError: # Immutable — can't use monkeypatch on this type pass ``` **Fix:** Detect immutability and adapt the test — either skip the mock and test the fallback path directly, or mock at a different level. ### Direct Dependency in PyPI Package If `pyproject.toml` has a git+SHA pinned dependency (e.g., `package @ git+https://github.com/user/repo.git@abc123`), the PyPI publish step will reject the package with `400 Can't have direct dependency`. The build and wheel-smoke steps pass — only the upload fails. **Fix:** Check if the dependency is available on PyPI. If yes, switch to a version specifier: ``` # Before (blocks PyPI): "package @ git+https://github.com/user/repo.git@abc123" # After: "package>=1.0.0,<2.0.0" ``` ## Step 5: Fix and Verify 1. Make the minimal fix 2. Run the failing test locally: `pytest -xvs tests/test_file.py::test_name` 3. Run the full test suite: `pytest` 4. Commit with conventional commit + DCO sign-off: `git commit -s -m "fix: description"` 5. Push: `git push` ## Step 6: Verify CI Passes After pushing, **check CI status proactively** — don't wait for the user to tell you it's red: ```bash # Wait for CI to start and complete gh run watch <RUN_ID> --exit-status # Or check status periodically gh run list --limit 1 --json conclusion,status ``` If still red, go back to Step 1. Repeat until green. ## Step 7: Multiple Fixes, Multiple Commits When fixing a series of related CI issues, use **one commit per root cause** — not one commit per test file, and definitely not one mega-commit. This keeps the history reviewable and makes it easy to revert individual fixes if needed. Example from a real session: ``` fix: switch dependency from git+SHA to PyPI specifier # unblocks release fix: handle repr-style list env vars in config # fixes config parsing fix: align entry-point test with module-load contract # fixes test gap fix: update macos-fallback test for refactored retrieval # fixes stale mock ``` Each commit is a single logical change, has its own DCO sign-off, and could stand alone. ## Step 8: Structural Release-Workflow Fixes After fixing the immediate CI failure, consider whether the *workflow itself* has a structural gap that allowed the bad state to reach production (or PyPI). ### Gate Release Workflow Behind Tests The most common gap: the release workflow runs on tag push but has **no dependency on the test workflow**. A broken tag can publish to PyPI. **Fix pattern:** Add a `test` job as a prerequisite to `build` (and thus `publish-pypi`): ```yaml jobs: test: name: Run tests runs-on: ubuntu-latest env: HF_HUB_OFFLINE: "1" # … any env vars your tests need steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - run: pip install -e ".[dev]" - run: pytest -xvs build: name: Build distribution needs: test # ← never build if tests fail # … rest of build steps publish-pypi: name: Publish to PyPI needs: build # ← never publish if build fails if: contains(github.ref_name, '-') == false # skip RC tags # … publish steps ``` ### Add workflow_dispatch for Retries Without `workflow_dispatch`, the only way to retry a failed release is to delete and recreate the tag — which rewrites history. Add a manual trigger: ```yaml on: push: tags: - "v*" workflow_dispatch: # ← manual retry button in the Actions tab ``` This lets you retry a failed publish without touching the tag. If the release process has a "publish to PyPI" step that depends on an environment with protection rules, `workflow_dispatch` will respect those rules on manual runs too. ### Tag-Delete-and-Recreate Cycle If a release does fail and `workflow_dispatch` wasn't yet added (or the tag itself was pushed before the fix was in): ```bash # Delete the broken tag git tag -d vX.Y.Z git push --delete origin vX.Y.Z # Fix the issue, push the fix git add … git commit -s -m "fix: root cause" git push # Recreate the tag on the new HEAD git tag vX.Y.Z git push origin vX.Y.Z ``` This re-triggers the release workflow. Only do this for failed releases — never for successful ones (would unpublish the package if downstream consumers already depend on it). ## Quick Reference | Step | Command | |------|---------| | Read CI logs | `gh run view <ID> --log-failed` | | Run single test | `pytest -xvs tests/file.py::test_name` | | Check pre-existing | `git stash && pytest ... && git stash pop` | | Check installed entry point | `python3 -c "import importlib.metadata as im; ..."` | | Full test suite | `pytest` | | Commit + push | `git commit -s -m "fix: ..." && git push` | | Watch CI | `gh run watch <ID> --exit-status` | -
contribution-idea-filtering.md 5 KB
# Contribution Idea Filtering: Ideation-to-Issue Gap Assessment ## Problem A brainstorm or `/ideation` session produces a list of improvement ideas. Filing all of them as issues on a repo is wrong — some won't fit, some are premature, and cluttering the tracker wastes maintainer attention. The missing step: **filtering generic ideas against a specific repo's actual state** before any issue is filed. ## The Methodology This session's worked example: 9 ideas from `/ideation` → 3 applicable to chazcheadle/rtlamr-meter-reader. ### Step 1: Run repo reconnaissance (read-only) Before assessing any idea, know what the repo actually has: ```bash # What infrastructure exists? ls Dockerfile docker-compose.yml .github/workflows/ 2>/dev/null ls renovate.json .renovaterc 2>/dev/null ls CONTRIBUTING.md 2>/dev/null # What's the contributor pipeline like? gh pr list --repo owner/repo --state all --json number --limit 1 | jq length gh issue list --repo owner/repo --state all --json number --limit 1 | jq length # What labels are available? gh label list --repo owner/repo --limit 100 # What templates exist? gh api repos/owner/repo/contents/.github/PULL_REQUEST_TEMPLATE.md --jq '.name' 2>/dev/null || echo "none" gh api repos/owner/repo/contents/.github/ISSUE_TEMPLATE --jq '.[].name' 2>/dev/null || echo "none" ``` ### Step 2: Build a repo-attribute matrix Map the repo's characteristics to columns: | Attribute | Value | Signal | |-----------|-------|--------| | Has Docker Compose? | Yes/No | Docker health ideas applicable? | | Uses Renovate? | Yes/No | Renovate auditor applicable? | | Has CI workflow? | Yes/No | CI-based gates applicable? | | Has existing PRs? | Count | PR gardener premature? | | Has stale branches? | Count (besides main) | Branch archiver premature? | | Has issue/PR templates? | Yes/No | Template unifier needed? | | Has a SKILL.md? | Yes/No | SKILL.md compliance check applicable? | | Published on package registry? | Yes/No | Release workflow hygiene relevant? | | Has CONTRIBUTING.md? | Yes/No | Phase 0 content needed? | ### Step 3: Score each idea against the matrix Ask for each idea: 1. **Does the repo have the thing this idea targets?** (e.g., "Docker health layering" needs a Docker Compose file — no Docker = not applicable) 2. **Is there enough volume for this idea to matter?** (e.g., "PR gardener" needs existing PRs — 0 PRs = premature) 3. **Is the idea a net negative at this stage?** (e.g., adding a PR size gate to a repo with 0 contributors adds noise, not health) 4. **Does the user already have the thing this idea would add?** (e.g., "Renovate config auditor" — no Renovate means the idea is "add Renovate first," not "audit the config") ### Step 4: Categorize | Category | Action | Example | |----------|--------|---------| | ✅ **High fit** | File issue now | SKILL.md size enforcer on a repo with oversized SKILL.md | | ⚠️ **Secondary** | File after blocking ideas land | PR size gate after PRs start flowing | | ❌ **Not applicable** | Skip (repo lacks prerequisites) | Docker health on a repo without Docker | | 🌳 **Separate project** | File on a different repo or create new project | `gh health-check` is a standalone CLI tool, not a PR | ### Step 5: Present the filtered list Don't just file the accepted ones — show the user what was excluded and why. This builds trust that the filtering was deliberate: ``` 3 of 9 ideas apply to this repo: ✅ FILE: - SKILL.md size enforcer (directly addresses 5x budget violation) - Issue/PR templates (zero exist — foundational) - Dependency drift detector (rtlamr v0.9.5 pinned, no version checks) ❌ SKIP (not applicable / premature): - PR size gate (0 PRs exist) - Docker health (no Docker) - Renovate auditor (no Renovate) - PR gardener (0 PRs) - Branch archiver (0 stale branches) - gh health-check CLI (separate project, not a PR) ``` ## Decision Table | Repo characteristic | Ideas that fit | Ideas that don't | |--------------------|----------------|-------------------| | Has SKILL.md | SKILL.md size enforcer, progressive disclosure audit | — | | No issue/PR templates | Template unifier | — | | Has Docker Compose | Docker health layering | — | | Has Renovate config | Renovate auditor | — | | Has active PRs (3+) | PR size gate, PR gardener | — | | Has stale branches (3+, 90d+ untouched) | Branch archiver (`git archive-branch`) | — | | Has CI workflow | CI-related gates (size, compliance) | PR size gate (if repo has no PRs) | | Has package registry (PyPI, npm) | Release workflow hygiene | — | | No CI/CD at all | Start with CI foundation | Any CI-dependent gate is premature | | Brand new (0 issues, 0 PRs, 0 contributors) | Templates, CONTRIBUTING.md, dependency drift | Everything else is premature | ## Why This Matters Without this filter, an ideation session that produces 9 reasonable ideas leads to **9 issues filed on a repo where 6 are noise.** The user has to sort through which ones apply, the maintainer scrolls past irrelevant issues, and the tracker accumulates dead entries. The 30 seconds of reconnaissance before filing saves everyone time. -
cross-fork-org-restriction.md 3 KB
# Cross-Fork PR Failure Due to Org Restriction — Case Study ## Context 2026-05-18: Contributing to `shlinkio/shlink` (PHP URL shortener) from fork `magnus919/shlink`. All the usual approaches failed: | Approach | Error | |----------|-------| | `gh pr create --head magnus919:feat/agents-md` | `GraphQL: magnus919 does not have the correct permissions to execute CreatePullRequest` | | `gh api repos/shlinkio/shlink/pulls -f head="magnus919:feat/agents-md"` | `404 Not Found` | | `curl -X POST ...` (raw HTTP) | `404 Not Found` | ## What DID Work The **GitHub compare URL** always works and pre-fills the diff: ``` https://github.com/shlinkio/shlink/compare/develop...magnus919:feat/agents-md ``` This requires a human to click "Create Pull Request" — not fully automatable, but the only reliable fallback. ## Diagnostic Steps These confirmed the setup was correct — the problem was on GitHub's side, not ours: ```bash # 1. Confirm fork exists and is recognized gh api repos/magnus919/shlink --jq '{.parent.full_name, .fork}' → shlinkio/shlink, true # 2. Confirm branch exists on fork gh api repos/magnus919/shlink/branches/feat/agents-md --jq '.name' → feat/agents-md # 3. Confirm upstream can see the branch via compare API (the key check) gh api repos/shlinkio/shlink/compare/develop...magnus919:feat/agents-md --jq '.status' → ahead # 4. Confirm PR creation within the same fork works (isolates the issue to cross-fork) gh api repos/magnus919/shlink/pulls -f title="Test" -f head="feat/agents-md" -f base="develop" → 201 Created # 5. Check token scopes gh auth status → ✓ Logged in, repo scope present # 6. List PRs on upstream repo (read access confirmed) gh api repos/shlinkio/shlink/pulls --jq '.[].number' → returns PR numbers ``` Steps 3-5 all passed, which means the issue was an **org-level restriction on the shlinkio organization** — likely third-party access restrictions preventing the OAuth token from creating PRs via the API, even though the compare endpoint could see the branch. ## Root Cause Hypothesis The `shlinkio` organization likely has one of: - **Third-party application access restrictions** (the OAuth token's app is not authorized for the org) - **SAML SSO enforcement** that requires `gh auth refresh --sso` - **"Allow outside collaborators to submit pull requests" disabled** at the org level Since the compare endpoint works (`status: ahead`) but PR creation returns 404, the token has read access but not the write access needed to create cross-fork PRs to repos in restricted orgs. ## Takeaways For future cross-fork PR contributions to org repos: 1. Try `gh pr create` and `gh api` first — they'll work for most orgs 2. If both fail with 404 despite `gh api repos/org/repo/compare/develop...you:branch` returning `ahead`: - Try `gh auth refresh --sso` to authorize the token for the org - If that doesn't help, hand off via the compare URL 3. The compare URL is the **universal fallback** that works regardless of org restrictions -
cross-fork-pr-workaround.md 2.5 KB
# Cross-Fork PR Workaround — Session Detail ## Background On 2026-05-11, contributing to `NousResearch/hermes-agent` from fork `magnus919/hermes-agent-1` (note: fork name differs from upstream). `gh pr create` repeatedly failed with an opaque GraphQL error despite correct parameters. ## Error ``` pull request create failed: GraphQL: Head sha can't be blank, Base sha can't be blank, Head user can't be blank, Head repository can't be blank, No commits between NousResearch:main and , Head ref must be a branch, not all refs are readable ``` ## Attempted Fixes That Did NOT Work 1. **Correcting `--head` format from `user/repo:branch` to `user:branch`** - `--head magnus919/hermes-agent-1:fix/foo` → `--head magnus919:fix/foo` - Result: same error 2. **Syncing fork's `main` with upstream** ```bash git fetch upstream main git push origin upstream/main:main ``` - Result: `origin/main` now matched upstream, but `gh pr create` still failed 3. **Using `gh pr create` from within the fork checkout** - Without `--repo` flag (let gh infer from cwd) - With `--repo NousResearch/hermes-agent` (explicit) - Result: same error both ways ## Fix That Worked Use the **GitHub REST API directly**, bypassing `gh`'s fork-resolution heuristics entirely: ```bash gh api repos/NousResearch/hermes-agent/pulls \ -f title='fix(agent): description' \ -f head='magnus919:fix/tool-call-regex-nested-json' \ -f base='main' \ -f body='## Summary\n\nPR body here' ``` Key points: - `-f head='username:branch'` — username only, not the repo name - No `-f head='username/repo:branch'` — that causes 422 "head is invalid" - The API always returns 201 Created for valid cross-fork PRs - Response includes full PR object with `html_url`, `number`, etc. ## Root Cause Hypothesis `gh pr create` uses a GraphQL mutation that resolves the head ref through the fork's repository connection. When the fork name differs from the upstream (e.g. `hermes-agent-1` vs `hermes-agent`), `gh`'s fork-resolution heuristics fail silently. The REST API endpoint (`POST /repos/:owner/:repo/pulls`) accepts a simpler `head` string that doesn't require the same repo-name resolution. ## Update: This Is Not the Only Failure Mode On 2026-05-18, contributing to `shlinkio/shlink`, the REST API also failed with 404 despite the compare endpoint confirming `status: ahead` — see `cross-fork-org-restriction.md` for this separate failure mode caused by org-level access restrictions. In that case, the GitHub compare URL is the only reliable fallback. -
default-posture.md 1.7 KB
## Default Posture (No CONTRIBUTING.md) When a project has no contributing guide, assume these defaults: ### Filing Issues - **Bug reports** always include: OS, version, reproduction steps, expected vs actual behavior - **Feature requests** always include: the problem, not just your solution - Search existing issues first — duplicates annoy merchants - Use a clear, descriptive title — "It crashes" is useless; "Null pointer in login redirect when session is expired" is helpful ### Pull Requests - One PR per logical change (small and focused) - Descriptive commit messages explaining *why*, not just *what* - Include tests for new code - Include relevant documentation updates - Reference any related issues - Ensure CI passes before requesting review - Use `git commit -s` (Signed-off-by) unless you know the project doesn't require DCO ### Communication - **Be respectful and constructive** — assume good faith, even when frustrated - **Be patient** — maintainers are often volunteers with limited time - **Explain your reasoning** — code reviews are asynchronous; your reviewer can't read your mind - **Don't demand attention** — "any update?" once after 2 weeks is fine. Daily bumps are not. - **Offer to fix things pointed out in review** rather than arguing about style preferences - **Thank maintainers** for their time — it costs nothing and matters a lot ### Code of Conduct Even without a documented CoC, follow these basic norms: - No personal attacks, insults, or derogatory language - No entitlement — maintainers don't owe you their time - Respect project decisions you disagree with (you can always fork) - Credit others' work — if you build on someone else's contribution, acknowledge it --- -
gh-pr-create-body-pitfall.md 1.3 KB
# gh pr create — body content shell expansion pitfall ## Problem When the PR body contains characters that the shell interprets (backticks, curly braces, JSON, `$` variables), `gh pr create --body "..."` fails because the shell expands the content before passing it to gh. **Example:** A body containing JSON-like text or backtick-wrapped inline code: ```bash gh pr create --title "feat: add widget" --body '{"query": "test", "exclude_tags": ["vault:private"]}' ``` The shell interprets `{}`, `[]`, and other special characters, causing `gh` to error with `unknown argument` or similar. ## Fix: use --body-file Write the body to a temporary file and pass it via `--body-file`: ```bash cat > /tmp/pr-body.md << 'HEREDOC' ## Summary ## Changes - ## Related Issues Closes #N ## Test Plan - [ ] HEREDOC gh pr create --title "feat: ..." --body-file /tmp/pr-body.md --base main --head my-branch ``` The `'HEREDOC'` (quoted) prevents shell expansion of the content. This works for any body content including JSON, backticks, and special characters. ## When to use Any PR body that contains: - JSON or code blocks with `{}` - Backtick-wrapped inline code - Curly braces or square brackets - Markdown with special characters For plain-text bodies with no special characters, `--body "..."` is fine. -
groktocrawl-gaps-worked-example.md 3.8 KB
# Worked Example: Multi-PR Plan Execution Across Dependent Changes ## Context A project needed two parallel feature tracks — backend model changes and browser automation — with a dependency chain of 8 issues spanning multiple microservices. **Gaps discovered during testing:** 1. **No binary content support** — the scraper only produces markdown 2. **No Cloudflare bypass** — the browser service was trivially detectable as a bot ## Phase 0a: Contribution Infrastructure Before filing any issues, set up `.github` templates and update CONTRIBUTING.md: ``` .github/ ├── ISSUE_TEMPLATE/ │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ``` CONTRIBUTING.md updated with Conventional Commits + DCO sign-off + PR template reference. ## Phase 1: Ideation → Issues Gaps were ideated into a dependency graph of 8 issues: | # | Title | Track | Depends on | |---|-------|-------|------------| | #1 | Binary content response model | Gap 1 | — (foundation) | | #2 | Stealth browser config | Gap 2 | — (foundation) | | #3 | CLI download subcommand | Gap 1 | — (independent) | | #4 | Content-type detection | Gap 1 | #1 | | #5 | Cookie persistence | Gap 2 | #2 | | #6 | Auto-recovery pipeline | Gap 1 | #4 | | #7 | CAPTCHA-solving sidecar | Gap 2 | #2, #5 | | #8 | Classification-based routing | Both | #6 | Each issue body included a `## Dependencies` section: - `**Requires:** #1 — cannot implement without this` - `**Independent of:** #3 — separate scope` ### Key lesson: filing issues is the midpoint, not the finish line After filing all issues, the next step is implementing the PRs. Filing issues documents the roadmap; implementing them delivers it. ## Phase 2: Multi-PR Execution 6 PRs were implemented following the dependency chain: | PR # | Issue | Scope | Depends on | |------|-------|-------|------------| | A | #1 — Response model | API layer | — | | B | #2 — Stealth browser | Browser service | — | | C | Phase 0a — Templates | 4 config files | — | | D | #3 — CLI download | CLI tool | — | | E | #4 — Content detection | Scraper pipeline | A | | F | #5 — Cookie persistence | Browser service | B | ### Execution pattern Each PR followed the same workflow: 1. `git checkout main && git checkout -b feat/<descriptive-name>` 2. Implement changes 3. Verify syntax: `python3 -c "import ast; ast.parse(open('file').read())"` 4. `git add` + `git commit -s -m "type: description"` 5. `git push upstream HEAD` 6. `gh pr create --body-file /tmp/pr-body.md` ### Dependency handling For dependent PRs (e.g., PR E depended on PR A's model changes): - Branch was from main (not from the foundation branch) - Code referenced new fields not yet on main - PR body documented the dependency explicitly - Foundation PRs submitted first — reviewer merges in order - No branch stacking — each PR reviewable independently ## What Worked Well - **Issue dependency metadata** made the roadmap navigable from any single issue - **Foundation-first implementation** avoided merge conflicts - **Consistent branch naming** (`feat/<feature>`) made relationships obvious - **Syntax verification before commit** caught errors early - **`--body-file` for PR creation** avoided shell escaping issues with special characters ## Common Pitfalls Encountered - **File truncation during agent tool use**: Reading a file with a line-number-prefixed tool and writing it back can truncate content. Use direct Python file I/O (`open()`/`read()`/`write()`) for round-trips on large files. - **Non-unique search patterns**: The patch tool failed on non-unique patterns in large files. Use precise surrounding context for reliable string matching. ## Key Metrics - 12 distinct work items (4 infrastructure + 8 issues) - 6 PRs submitted - ~490 lines of code changed across 5 microservices - Zero merge conflicts between dependent PRs -
hermes-agent-contributing-case-study.md 3.3 KB
# Case Study: Hermes Agent CONTRIBUTING.md This is a real-world example of a well-written CONTRIBUTING.md from a project we've contributed to. It demonstrates everything a good contributing guide can cover. **Source:** https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md ## What It Covers | Section | What We Learn | |---|---| | **Contribution Priorities** | Not all contributions are equal. This project explicitly prioritizes: bug fixes > cross-platform compatibility > security > performance > new skills > new tools > docs. Knowing this tells you where your contribution effort is most welcome. | | **Skill vs Tool Decision Guide** | A detailed framework for deciding whether a capability should be a skill or a tool. The answer is almost always "skill." Also covers bundled vs optional vs Skills Hub placement. | | **Development Setup** | Exact commands needed to set up a local dev environment. If we'd read this, we'd have known about `--recurse-submodules`, the specific `uv venv --python 3.11`, and `uv pip install -e ".[all,dev]"`. | | **Project Structure** | Maps the entire codebase so you know where your change goes. | | **Architecture Overview** | Core loop, key design patterns, provider abstraction. Essential context before modifying agent behavior. | | **Code Style** | PEP 8 with practical exceptions. Comments only for non-obvious intent. Cross-platform rules. | | **Adding a New Tool** | Self-registering pattern with `registry.register()`, auto-discovery, toolset wiring in `toolsets.py`. | | **Adding a Skill** | SKILL.md format with frontmatter, platform-specific skills, conditional activation, required env vars, skill guidelines (no external deps, progressive disclosure). | | **Cross-Platform Compatibility** | Extensive rules for Windows compatibility (16 specific rules). This is a huge section — cross-platform care matters to this project. | | **Security Considerations** | Existing protection layers, what to do when contributing security-sensitive code. | | **PR Process** | Branch naming (`fix/`, `feat/`, etc.), what to test before submitting, PR description format, Conventional Commits. | | **License** | By contributing, you agree to MIT licensing. | ## Common Contributor Mistakes This Prevents Reading the contributing guide *before* starting work prevents common mismatches between contributor intent and maintainer expectations: 1. **Branch naming** — Using project-standard prefixes (`fix/`, `feat/`) instead of arbitrary names 2. **Commit messages** — Following the project's commit format requirements instead of defaulting to freeform 3. **Tests** — Running the project's test suite before submission 4. **Development setup** — Using the project's exact dev environment commands instead of guessing 5. **PR description format** — Filling out the project's PR template rather than writing a freeform summary 6. **Skill/tool classification** — Understanding where different types of changes belong in the project structure 7. **Cross-platform considerations** — Checking for platform-specific requirements the project enforces ## Key Takeaway Even a single READ of CONTRIBUTING.md before starting work would have caught most of these. The document exists to prevent exactly this kind of mismatch between contributor intent and maintainer expectations. -
hermes-cashew-contributing-worked-example.md 4.1 KB
# Worked Example: Establishing CONTRIBUTING.md Post-Build Phase ## Context A project transitioning from an agent-assisted build phase to a formal open source project needed contributing conventions established. The project had shipped 3 milestones via rapid iteration before any open source conventions existed. - **License**: Apache 2.0 - **Language**: Python - **Build system**: Hatchling - **Test framework**: pytest ## Key Architectural Detail The project used PEP 420 namespace packages (no `__init__.py` at intermediate levels), with a dual-path loading strategy for pip-installed vs. flat-entry loaders. The dev install required a non-obvious symlink — without documenting this, the first external contributor would hit a `ModuleNotFoundError` with no obvious fix. ## What Was Created | Artifact | Key Decisions | |----------|--------------| | `CONTRIBUTING.md` | DCO sign-off required; Conventional Commits; one PR per change; no force-push after review; tests must run locally first | | `.github/ISSUE_TEMPLATE/bug_report.md` | Project-specific env fields (library version, storage backend status) | | `.github/ISSUE_TEMPLATE/feature_request.md` | Problem-first framing with scope estimation | | `.github/PULL_REQUEST_TEMPLATE.md` | Summary + test plan + breaking changes section | ## Convention Choices ### Commit Format: Conventional Commits The existing git history already used `feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `ci:`, `chore:` prefixes from the build phase. Formalizing them codified existing practice. ### Legal: DCO (Developer Certificate of Origin) Apache 2.0 license + DCO is the standard lightweight combination. `git commit -s` on every commit. ### Branch Naming: Match Commit Types ``` fix/description → fix/vector-search-rowid-mismatch feat/description → feat/add-ollama-embedding-support refactor/description → refactor/extract-db-migration-module docs/description → docs/api-reference-typo ci/description → ci/publish-automation ``` ### Merge Strategy: Squash Keeps main history clean. Allows fixup commits during review without force-push. ## What Was Tricky ### The virtual environment + symlink dev install The most non-obvious setup detail: the project's loader inserts itself at the front of `sys.path`, making its bundled `__init__.py` a regular package that blocks PEP 420 namespace resolution. A dev install (`pip install -e .`) must be done in the project's virtual environment AND requires a symlink into the loader's plugin directory. ### Offline requirement in tests The embedding model was ~500 MB. Tests must never trigger a download. Documented as: offline environment variable in `conftest`, enforced by CI log-scan. ### Knowledge graph for orientation The project included a generated knowledge graph directory, documented as an orientation tool for new contributors — cheaper than reading every source file. ## What Got Deferred - **Automated linter** — no CI enforcement (yet). Noted as aspirational. - **Issue labels** — used GitHub's defaults. No domain-specific labels yet. ## Phase 2: Issue Reconciliation After the conventions were established, 9 open issues were reconciled against the new architecture pivot. The conventions tell contributors *how* to contribute; issue reconciliation tells them *what* still needs doing. ### Process 1. Read each open issue through the lens of the new architecture 2. For issues describing functionality now handled upstream: close with explanation 3. For issues needing re-scoping: update the body, change the milestone 4. For still-valid issues: leave open, verify they're actionable ### Outcome | Result | Count | Examples | |--------|-------|---------| | Closed (addressed by refactor) | 2 | Schema fork, novelty gate | | Closed (out of scope for thin adapter) | 4 | Warm daemon, dashboard, domain separation, custom extractors | | Re-scoped | 2 | Think cycles → resolved-by LLM integration; Privacy filter | | Kept | 1 | LLM integration (the core remaining gap) | ### Milestones Cleaned Up Three milestones were closed as all their issues were resolved or determined out of scope. One milestone remained open for the one re-scoped privacy issue. -
phase-0-before-you-start.md 11.9 KB
## Phase 0: Before You Do Anything ### Read CONTRIBUTING.md This is a **hard gate**. Before planning any change, before writing a single line of code, before filing anything: ```bash # Check common locations (run from the repo root) for f in CONTRIBUTING.md .github/CONTRIBUTING.md docs/CONTRIBUTING.md CONTRIBUTING.rst; do if [ -f "$f" ]; then echo "=== Found: $f ===" cat "$f" break fi done # Also scan README for contributing sections if grep -qi "contributing\|how to contribute\|getting started" README.md 2>/dev/null; then echo "=== README.md has contributing info ===" grep -n -A 5 -i "contributing\|how to contribute\|getting started" README.md fi ``` **What you're looking for:** | Thing to Check | Why It Matters | |---|---| | CLA / DCO requirements | Some projects require a signed Contributor License Agreement or Developer Certificate of Origin (`Signed-off-by:` in commit messages) | | Commit message format | Projects like Angular, Linux, and many others enforce conventional commits or specific prefixes | | PR template | May be auto-loaded via `.github/PULL_REQUEST_TEMPLATE.md` — fill it out completely | | Issue templates | May exist in `.github/ISSUE_TEMPLATE/` — use the right one | | Code style / linting | May require specific formatters (prettier, black, eslint, clang-format, etc.) | | Testing requirements | Minimum test coverage, specific test framework, how to run tests | | Branch naming | Some projects want `fix/description` or `bugfix/description` not `feat/desc` | | Communication channels | Whether to discuss before filing (mailing list, Discord, forum) or just file the issue | | Code of Conduct | Be aware of the CoC — you're expected to follow it | | Sign-off required | `git commit -s` (Signed-off-by) may be mandatory for DCO compliance | **If CONTRIBUTING.md exists, follow it to the letter.** It overrides everything below. #### Extracting Structure from CONTRIBUTING.md Read the whole file, then extract these specific answers. Having them in your working context makes the rest of the contribution process smoother: 1. **What's the project's priority order for contributions?** (e.g., bug fixes > cross-platform > security > features). Aim for the highest-priority category your contribution fits. 2. **Branch naming convention** — look for explicit patterns like `fix/description` or `feat/description` 3. **Commit message format** — do they require Conventional Commits? Specific scopes? `Signed-off-by` for DCO? 4. **How to set up a dev environment** — are there specific `uv`, `pip`, `npm`, `cargo` commands? `--recurse-submodules` on clone? 5. **How to run tests** — is there a specific script (`scripts/run_tests.sh`) or framework? 6. **Code style / linting** — any formatters, linters, or style rules to apply before PR? 7. **PR description expectations** — any specific template, sections, or information they want? 8. **Architecture context** — does the document explain where different types of changes go? (tools vs skills, etc.) 9. **Cross-platform considerations** — any platform-specific rules your change needs to follow? 10. **Communication channels** — Discord, mailing list, or GitHub Discussions for pre-PR discussion? 11. **CLA / DCO / License** — do you need to sign anything? What license are you contributing under? 12. **Security reporting process** — if your contribution has security implications, where do they go? For a worked example of extracting these from a real CONTRIBUTING.md, see the reference file `references/hermes-agent-contributing-case-study.md`. ### Check Existing Issues and Discussions Before filing anything new: ```bash # Check issues on the current repo gh issue list --search "keywords related to your issue" --state all --limit 10 # Or for a broader search gh issue list --search "your topic in:title" --state all --limit 10 ``` Also check open and recently closed PRs — someone may already be working on a fix: ```bash # Check PRs on the current repo gh pr list --search "keywords" --state all --limit 10 ``` ### Finding Good Bugfix Candidates (When You Want to Fix, Not File) When you're looking for a bug to fix rather than filing a new issue, the selection process has its own heuristics. A poor candidate wastes maintainer time and your effort. #### Systematic Triage ```bash gh issue list --repo owner/repo --state open --label bug --limit 50 \ --json number,title,labels,updatedAt ``` #### Filter Out Blocked Issues | Red flag | What it means | |----------|---------------| | `NeedsUserFeedback` / `needs info` | Blocked on reporter — can't reproduce | | `NeedsTriage` / `untriaged` | Not yet evaluated — may be invalid | | `DifficultToImplement` / `complex` | Maintainer flagged high difficulty | | `Upstream` / `external` | Bug is in a dependency, not fixable here | | `Won't fix` / `out of scope` | Project has decided not to address | Cleanest candidates have only `bug` (plus maybe a feature-area tag). #### Check for Existing PRs ```bash gh pr list --repo owner/repo --state all --search "ISSUE_NUMBER in:body" \ --json number,title,state ``` #### Read Every Comment Comments are the richest signal: - **Positive:** maintainer isolated root cause, created reproduction, said "should be fixed" - **Negative:** "not obvious," "won't fix," scope ballooned during discussion - **Free issue:** someone volunteered but never followed up #### Find Related Issues After settling on a candidate, search for duplicates. A closed issue with "can't fix this" is a strong risk signal: ```bash gh issue list --repo owner/repo --state all \ --search '"keyword1" OR "keyword2" in:title,body' \ --json number,title,state ``` #### Reproduce in a Clean Environment Before writing fix code, confirm the bug in an isolated environment: ```bash docker run --rm -v "$(pwd)/test-site:/site" -w /site \ PROJECT_IMAGE:latest --gc --cleanDestinationDir ``` #### Decision Matrix | Criterion | Good | Bad | |-----------|------|-----| | Label | Bug only | NeedsUserFeedback, Upstream, DifficultToImplement | | PRs | None open | Open PR exists | | Reproduction | Has steps or test repo | Vague or missing | | Maintainer signal | "Should be fixed" | "Not obvious" | | Code scope | Single package, isolated | Cross-cutting, architectural | For a worked example with real command output (50+ issues triaged on gohugoio/hugo), see the `hugo-contrib` skill's `references/finding-bugfix-candidates.md`. ### Filtering Idea Lists Against Repo Reality: Ideation-to-Issue Gap Assessment When a brainstorm or ideation session produces a list of improvement ideas (especially from `/ideation` or `/council`), the natural next step is to file issues. But generic ideas often don't fit a specific repo. Filing unfit issues wastes maintainer attention and clutters the tracker. **The pre-filing filter:** Before any issue is filed, cross-reference each idea against the repo's actual state. See `references/contribution-idea-filtering.md` for the full methodology — a decision matrix mapping repo attributes (Docker presence, Renovate config, existing PR volume, stale branches, CI setup, etc.) to which idea categories are actionable vs. premature. Quick decision table (full version in the reference): | Repo has | Relevant ideas | Premature | |----------|---------------|-----------| | No CI/CD, no Docker, no Renovate | SKILL.md compliance, issue/PR templates, dependency drift | PR size gate, Docker health, Renovate auditor | | CI but no PRs yet | SKILL.md compliance, templates, PR size gate | PR gardener (nothing to garden yet) | | Active PRs but no stale-branch policy | All of the above + branch archiver | — | This step sits between "ideation output exists" and "Phase 1: Filing Issues." ### Verify Before Redirecting When you identify that an issue belongs in a **different project** (e.g., a core bug that's really a documentation concern, a backend issue that's actually a frontend problem, a feature request that should be an integration library), the natural instinct is to redirect: "you should file this over there." **Resist the redirect impulse until you verify the target repo.** Always check the target repo's issue tracker **and** open/closed PRs first: ```bash # Check target repo for existing issues on the topic gh issue list --repo owner/target-repo --state all --search "topic keywords" --limit 10 # Check target repo for existing PRs that may already fix it gh pr list --repo owner/target-repo --state all --search "topic keywords" --limit 10 ``` If the fix already exists as an open PR on the target repo, the best contribution is to update the original issue with a link to that PR — not to file another one. This applies equally to documentation, code, and configuration repos. **Real example:** The Home Assistant fritz integration had 19+ comments from users hitting an auth failure after repeater firmware updates. The core repo couldn't fix it — it was a docs gap. The natural response would be "file this on home-assistant.io." But someone had already opened PR #45402 on the docs repo with the exact troubleshooting note. The right action was linking the core issue to the existing docs PR, not asking for another issue to be filed. **Exception:** If a thorough search turns up nothing on the target repo *and* the maintainer has explicitly asked someone to address it (e.g., "do you mind editing the docs?"), then filing a new issue or PR on the target repo is the correct next step. ### Verify Before Redirecting When you identify that an issue belongs in a **different project** (e.g., a core bug that's really a documentation concern, a backend issue that's actually a frontend problem, a feature request that should be an integration library), the natural instinct is to redirect: "you should file this over there." **Resist the redirect impulse until you verify the target repo.** Always check the target repo's issue tracker **and** open/closed PRs first: ```bash # Check target repo for existing issues on the topic gh issue list --repo owner/target-repo --state all --search "topic keywords" --limit 10 # Check target repo for existing PRs that may already fix it gh pr list --repo owner/target-repo --state all --search "topic keywords" --limit 10 ``` If the fix already exists as an open PR on the target repo, the best contribution is to update the original issue with a link to that PR — not to file another one. This applies equally to documentation, code, and configuration repos. **Real example:** The Home Assistant fritz integration had 19+ comments from users hitting an auth failure after repeater firmware updates. The core repo couldn't fix it — it was a docs gap. The natural response would be "file this on home-assistant.io." But someone had already opened PR #45402 on the docs repo with the exact troubleshooting note. The right action was linking the core issue to the existing docs PR, not asking for another issue to be filed. **Exception:** If a thorough search turns up nothing on the target repo *and* the maintainer has explicitly asked someone to address it (e.g., "do you mind editing the docs?"), then filing a new issue or PR on the target repo is the correct next step. ### For Large Changes: Discuss First If your change is more than a few hundred lines, touches architecture, or adds significant new features: 1. Open a **discussion** or **issue** first, describing what you want to do and why 2. Wait for maintainer feedback before implementing 3. This prevents wasted work if the maintainers have a different vision **Read the room on feature requests.** If maintainers have already discussed a feature and deferred it with explicit reasoning (e.g., "we need to see adoption before embedding a template"), a PR implementing it directly is unlikely to be accepted even if the implementation is clean. In that situation, the more effective contribution is the one the maintainers asked for: a documented working example the community can use and build adoption around. The embedded PR can come later once adoption is compelling. The exception: trivial bugfixes (one-liner typos, obvious logic errors). Just fix and PR. --- -
phase-0a-maintainer-conventions.md 10.6 KB
## Phase 0a: Establishing Open Source Conventions (Maintainer) When you're setting up open source conventions for *your own project* — whether bootstrapping from scratch or formalizing after a build phase — this phase precedes all others. It's the maintainer mirror of the contributor checklist below. ### README Discipline: Describe What Exists, Not What You Plan When writing the initial README for a repo, **describe what exists on disk right now**. Do not lay out a directory tree of folders you intend to create, a roadmap of features yet to be built, or an aspirational architecture. Ground the README in the present state. This is not about ambition — it's about trust. A README that lists directories that don't exist yet fabricates a reality the reader will discover is false when they clone the repo. That erodes trust before the project has even started. **Good initial README:** - States what the repo is for (one paragraph) - Describes what's actually in it ("Nothing yet — check back") - Defines the concept of a skill (for context) - Links to the license **Bad initial README:** ``` agent-skills/ ├── research/ # Web research, article capture, knowledge extraction ├── content/ # Blog writing, image generation, creative workflows ├── devops/ # Infrastructure management, deployment, CI/CD ├── software-development/ # Coding patterns, debugging, code review ├── thinking/ # Multi-agent debate (council), structured reasoning └── templates/ # Skill templates for creating new skills ``` This directory tree described 6 folders. Zero existed. The README was a wishlist, not a description. When the user reads it and types `ls`, they find an empty folder — the discrepancy undermines confidence in everything else the agent produces. **The principle generalizes:** Any document that describes the current state of a system (README, project overview, architecture doc, status report) must be verifiable against reality. If you can't point to a file on disk or a working feature for every item you list, don't list it. Say "planned" or "roadmap" explicitly, or leave it out until it exists. **Future-proofing:** When you do add a folder or feature, update the README at that moment — not as a separate pass. The README should always be a snapshot of the current state, not an accumulation of past plans. --- ### What to Create | Artifact | Purpose | Location | |----------|---------|----------| | **LICENSE** | The legal terms. Pick before anything else — it dictates DCO vs CLA, contribution terms, and what downstream users can do. | `LICENSE` (repo root) | | **CONTRIBUTING.md** | The single source of truth for how to contribute. Covers dev setup, commit conventions, PR process, DCO/CLA. | `CONTRIBUTING.md` (repo root) | | **Bug report template** | Structured form so contributors provide environment, reproduction steps, expected vs actual behavior. | `.github/ISSUE_TEMPLATE/bug_report.md` | | **Feature request template** | Problem-focused framing so contributors explain *why*, not just *what*. | `.github/ISSUE_TEMPLATE/feature_request.md` | | **PR template** | Summary + test plan + breaking changes + issue links. Keeps PRs reviewable. | `.github/PULL_REQUEST_TEMPLATE.md` | | **Labels** | GitHub's defaults (bug, enhancement, documentation, good first issue, help wanted, question) cover most needs. Add domain-specific labels only when triage volume justifies them. | GitHub repo settings | ### Conventions to Choose Early | Decision | Options | Recommendation | |----------|---------|---------------| | **Commit format** | Conventional Commits, Freeform, Angular | **Conventional Commits** — `feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `ci:`, `chore:`, `perf:`. Widely understood, machine-parseable, generates changelogs. | | **Legal sign-off** | DCO (`Signed-off-by`), CLA, None | **DCO** for most projects — lighter weight than a CLA, legally sufficient for Apache 2.0 / MIT. Require `git commit -s` on every commit. | | **Scope style** | Module names, issue numbers, none | Module/area names when the project has distinct subsystems (e.g., `fix(vec-search):`). Omit for small projects. | | **Branch naming** | `fix/`, `feat/`, `refactor/`, `docs/`, `ci/` prefixes | Match commit types for consistency. One branch per logical change. | | **Merge strategy** | Squash, Rebase, Merge commit | **Squash merge** for feature branches — keeps main history clean, allows fixup commits during review without force-push. | ### CONTRIBUTING.md — What to Cover A good CONTRIBUTING.md answers these questions for a first-time contributor: 1. **How do I set up a dev environment?** Exact commands, venv caveats, system requirements. Include troubleshooting for known issues (namespace collisions, symlink requirements, etc.). 2. **How do I run tests?** Single command, any test framework quirks, offline requirements. 3. **What commit format do you use?** Show examples of good and bad commit messages. 4. **Do I need to sign commits?** If DCO, say `git commit -s` and link to developercertificate.org. 5. **What's the PR workflow?** Branch from main, one change per PR, no force-push after review, CI must pass. 6. **What's the project architecture?** A 3-5 sentence overview so they know where their change lives. 7. **Where do I report security issues?** Separate channel from public issues. ### When to Establish Conventions - **Bootstrapping**: Best time — set conventions before the first external contributor arrives. - **After a build phase**: Common for projects that started as internal tools or agent-assisted builds. The conventions formalize what was implicit during the build phase. **Crucial**: align the commit history with the new conventions retroactively via the CHANGELOG, not by rewriting history. The existing history is what it is — the convention applies going forward. - **Never**: Rewriting history to retroactively enforce new commit conventions. Don't force-push over published history. ### Phase 0b: Reconcile Issues After Architecture Change When a significant refactor changes the project's architecture (e.g., "stop implementing everything yourself, become a thin adapter around upstream"), existing open issues need reconciliation. Feature requests written for the old approach may no longer make sense. **The workflow:** 1. **List all open issues** and group them by type: - Directly addressed by the refactor → close as completed - Now handled by upstream/dependencies → close as out of scope (comment explaining why) - Still valid but need re-scoping → update title and body, link to related issues - Truly still open → leave, but verify they're still actionable 2. **Check milestones** — if the refactor shipped as a release but didn't close its milestone issues, the milestone is in a broken state: - Move re-scoped issues out of the milestone (so closing it doesn't orphan them) - Close issues that are fixed or out of scope - Close the milestone with a description explaining what actually shipped vs what was deferred 3. **Update issue bodies** for re-scoped items — old feature descriptions are misleading. Replace them with: - What changed (the refactor that made the old approach obsolete) - What the new approach should look like (specific acceptance criteria) - Links to related issues (e.g., "Resolved by #N") 4. **Document the reconciliation in the CHANGELOG** — future contributors shouldn't find closed issues in old milestones without knowing why. **Example session output (9 issues → 3):** | Outcome | Count | Examples | |---------|-------|---------| | Closed (fixed by refactor) | 2 | Schema fork → now uses upstream `ensure_schema()` | | Closed (out of scope) | 4 | Dashboard, extractor plugins, warm daemon, novelty gate | | Re-scoped | 2 | Privacy → `exclude_tags` passthrough; Think cycles → resolved-by LLM integration | | Kept | 1 | LLM integration (the remaining core gap) | **CLI commands for bulk reconciliation:** ```bash # Close an issue with a reason gh issue close N --comment "Reason for closing…" --reason "completed" # or "not planned" # Edit issue body (re-scope) gh issue edit N --body "New body text…" # Remove milestone gh issue edit N --remove-milestone # Close milestone gh api repos/owner/repo/milestones/M -X PATCH -f state=closed # Bulk: list all open issues in a milestone gh issue list --state open --json number,title --jq '.[] | .number' ``` ### Release Workflow Hygiene After setting up contribution conventions, check that the release workflow won't publish broken code. Two structural gaps to close: 1. **Gate behind tests**: The release workflow's build job should `needs: test` — never build or publish if tests are red. See `references/ci-debugging-loop.md` Step 8 for the YAML pattern. 2. **Add `workflow_dispatch`**: Manual trigger so a failed release can be retried without deleting and recreating the tag. These are part of the initial setup, not deferred concerns. A PyPI release that bypasses CI is worse than no release at all — downstream users install the broken package. ### Pitfall: PyPI rejects git+SHA dependency pins If your project uses a git+SHA pinned dependency during development (e.g., `package @ git+https://github.com/user/repo.git@abc123`), the PyPI publishing workflow will reject the package with `400 Can't have direct dependency`. PyPI only accepts version specifiers (`package>=1.0.0,<2.0.0`). **When this happens:** Before your first PyPI release, check if your dependency is published on PyPI. If it is, switch the specifier before tagging. If it isn't, you need an alternative distribution strategy (point users to pip install from source, or publish the dependency first). **The gotcha:** Agent-built projects often use git+SHA pins during development because the agent pins the exact version it tested against. This works fine for dev installs but blocks publishing. The fix is trivial (swap the specifier) but it blocks the release workflow silently — the build passes, the wheel passes, only the upload step fails. ### Template: CONTRIBUTING.md Structure A minimal but complete CONTRIBUTING.md needs these sections in order: ``` # Contributing to [Project Name] Table of Contents - Development Install - Running Tests - Understanding the Architecture - Reporting Bugs - Suggesting Features - Pull Requests - Conventional Commits - Signing Your Commits (DCO) ``` Each section should be concrete — exact commands, exact file paths, real examples. Avoid "please follow best practices" — show them what best practices look like in *this project*. For a worked example of a CONTRIBUTING.md created during an agent-assisted post-build transition, see `references/hermes-cashew-contributing-worked-example.md`. --- -
phase-1-filing-issues.md 14.2 KB
## Phase 1: Filing Issues ### Bug Reports A good bug report answers: *what happened, what should have happened, and how do I make it happen?* ```markdown <Clear, concise description of the bug> 1. <First step> 2. <Second step> 3. <See error> <What should happen instead> <What actually happens — include full error output> - OS: <e.g., macOS 15.4, Ubuntu 24.04> - Version/Build: <e.g., v2.1.3, commit abc1234> - Relevant config: <if applicable> - Screenshots, logs, or stack traces - Minimal reproduction (preferred: a script or config that triggers it) ``` **Always include the minimal reproduction.** A bug report without reproduction steps is hard to act on. ### Feature Requests Frame features around the **problem**, not your proposed solution: ```markdown <What user need or pain point this addresses> <Who needs this and when> <How you think it could work — keep this section brief, the maintainers may have a better approach> <Other ways to solve the problem — shows you've thought about it> <Why this is worth the project's time> ``` ### Agent Disclosure When filing an issue on behalf of someone else (human delegating to an AI agent), ALWAYS disclose the agentic nature. Add a line to the issue body: ``` Filed by AI agent on behalf of [contributor name] ``` This goes at the bottom, after the substantive content. It's not a courtesy — it's a transparency requirement. Maintainers deserve to know who they're talking to. ### Maintainer Workflow — Issue First, Code Second When the maintainer gives you direction on a feature ("let's do it", "go"), the natural impulse is to start coding immediately. **Resist it.** Even when you're implementing something the maintainer explicitly asked for, the first step is still an issue: 1. **Write the issue** documenting the problem, proposed solution, and alternatives considered 2. **Wait for maintainer feedback** — they may have a different approach in mind 3. **Only then branch and implement** The issue is the permanent record of the decision. Conversation threads are ephemeral; issues are durable. Future sessions can reference the issue to understand *why* a decision was made, not just *what* was implemented. **Exception:** Trivial bugfixes (one-line typos, clear logic errors in already-merged code). File the issue and fix in one branch, referencing the issue in the commit. #### Pitfall: Docker service code is not exempt When the repo contains Docker-based microservices (like GroktoCrawl, or any project with a `docker-compose.yml`), **changes to the service source code require an issue first** — even when you have write access and even when the change is "just testing" a hypothesis. The trap: you patch a file in `llm-svc/llm_svc/app.py` (or any service's source), rebuild the container with `docker compose build <service>`, restart it, and test. No issue was filed. The change feels local — it's in your own repo, you have write access, the container is running in dev. The same process rules apply: 1. File the issue documenting the problem and proposed fix 2. Branch from main 3. Implement 4. Commit, push, PR 5. Rebuild the container after merge The container doesn't make the change any less of a code change. If the file is tracked in git and deployed via Docker, the full lifecycle applies. **The fix if you already made the change without an issue:** revert the code and rebuild the container with the original code, file the issue, then proceed properly. Do not leave the patched container running — it creates a divergence between what's on disk (the issue's scope) and what's running. This was corrected in a real session: patching the llm-svc fixture to test a recovery pipeline improvement, without an issue filed first, prompted "are you making code changes without an issue filed?" The container was reverted and the issue was filed before proceeding. ### Filing Coordinated Multi-Issue Feature Roadmaps When a feature spans multiple independent work items — especially across service boundaries or involving different architectural layers — filing a single monolithic issue is wrong, but filing isolated issues without cross-references is almost as bad. The pattern: **one issue per logical change, linked by dependency metadata.** #### When to use this pattern - The feature requires changes in multiple services (e.g., API model + fetch pipeline + CLI) - The work has a clear dependency chain (issue B can't start until issue A merges) - Different aspects could be worked on in parallel (separate tracks, same feature) - The total scope is large enough that a single issue would be unwieldy #### Issue body dependency references Each issue's body should include a `## Dependencies` section that explicitly names what it needs: ```markdown - **Requires:** #23 (response model changes) — this issue adds content-type detection that feeds into the new `download` fields created in #23. - **Independent of:** #25 (CLI download command) — separate scope, different code paths. ``` GitHub auto-links `#N` references, so clicking the number jumps to the dependency. This creates a navigable graph from any issue. #### Filing order strategy | Dependency type | How to file | Example | |----------------|-------------|---------| | **Foundation** (no deps) | File first, all at once | `#23` response model, `#24` stealth browser, `#25` CLI download | | **Depends on foundation** | File immediately after, reference dependency | `#26` content-type detection → `Depends on #23` | | **Depends on dependent** | File in same batch but note chain | `#28` LLM recovery → `Depends on #26` | | **Escalation tier** | File alongside what it escalates from | `#29` FlareSolverr → `Depends on #24, #27` | File foundation issues and their dependents in the same batch — the dependency metadata makes the ordering clear to whoever picks them up. The goal is to get the full roadmap onto the tracker, not to serialize filing to match implementation order. #### Use cases for `Depends on` vs. `Blocked by` vs. `Related to` | Term | Meaning | When to use | |------|---------|-------------| | **Requires / Depends on** | Cannot implement without this | The other issue's code changes are a hard prerequisite | | **Blocked by** | External dependency outside the repo | Waiting on upstream release, API key, or maintainer decision | | **Related to** | Shares context but no hard dependency | Alternative approach, adjacent feature, overlapping code area | | **Independent of** | Explicitly not a dependency | Clarifies that these issues can proceed in any order | #### What a coordinated roadmap looks like on the tracker ``` #23 Response model changes ──founded──▶ #26 Content-type detection ──▶ #28 LLM recovery └──▶ #30 LLM Cloudflare classification #24 Stealth browser config ──founded──▶ #27 Cookie persistence ──▶ #29 FlareSolverr #25 CLI download subcommand (independent, enhanced by #23) ``` Each issue body tells the same story in prose. The tracker renders `#N` as clickable links, so the full dependency graph is navigable from any single issue. A future contributor landing on #30 can follow the chain back to the foundations. #### Pitfall: Don't file all issues as one "epic" A single issue that describes seven work items is unfindable — someone searching for "CLI download" won't land on a feature-request titled "Response model changes." Keep each issue focused on one logical deliverable, and use the dependency metadata to connect them. ### Executing a Comprehensive Multi-PR Plan After filing coordinated issues, the PR phase follows the same dependency chain. The pattern is linear: branch from main, implement, commit, push, PR for each issue in order, respecting cross-issue dependencies. #### Dependency tiers at PR time | Tier | What goes here | Strategy | |------|----------------|----------| | **Foundation** (no deps) | Model changes, config-only changes, new independent subcommands | Branch and submit all at once. These PRs have no blockers. | | **Depends on foundation** | Logic that uses the new model fields, services that call the new endpoints | Branch from main *after* foundation PRs exist (commits are independent). Reference the dependency in the PR body. | | **Escalation/meta** | Higher-level features that build on multiple lower tiers | These PRs can be written in parallel with dependents, but should be submitted last. | #### Branch naming for coordinated work Keep branch names consistent so the relationship is obvious: | Dependency | Branch name | PR references | |-----------|-------------|--------------| | Foundation | `feat/binary-content-response-model` | `Closes #23` | | Depends on #23 | `feat/content-type-detection` | `Closes #26 — Depends on #23` | | Depends on #23, #26 | `feat/llm-recovery` | `Closes #28 — Depends on #23, #26` | No need to encode dependency order in the branch name — just keep them single-purpose. #### Tracking dependencies in PR bodies In the PR body, use the same dependency metadata that appears in the issue: ```markdown - **Depends on:** #23 (response model changes) — adds the `download` field this PR populates - **Related:** #25 (CLI download) — separate code path, no conflict ``` GitHub doesn't auto-close or block PRs based on issue dependencies, but showing the chain in the PR body tells the reviewer the order they should merge in. #### What to do when foundation PRs haven't merged yet You branch from main, which doesn't have the foundation changes yet. Your code references new fields that don't exist on main. This is fine — the PRs are in the system, the diff shows what's being added, and the reviewer sees the full chain. Two strategies: 1. **Write independent code** — if your changes only touch files the foundation doesn't touch, there's no merge conflict when you rebase after the foundation merges. 2. **Assume the merge** — if your code directly uses the new fields, write it as if they already exist. The PR diff is clean and the merge order is clear. Do NOT branch from a feature branch (branch stacking). Each PR should branch from main so they're reviewable independently. #### Pitfall: Don't stop at issues — execute the full plan When you've planned a comprehensive change involving multiple issues and PRs, it's tempting to stop after filing the issues. The issues are on the tracker, the plan is documented — feels like progress. **The plan isn't done until the PRs are open.** This was corrected in a real session: after filing 8 issues across two gap areas, the user said "the plan didn't stop at issues" — the next step was implementing the PRs in dependency order, not declaring victory at the issue tracker. The pattern: **issues → PRs → done**. Issues capture the decision; PRs deliver the implementation. Filing issues is the midpoint, not the finish line. For a worked example of this full lifecycle (ideation → gaps → issues → 6 PRs), see `references/groktocrawl-gaps-worked-example.md`. For the companion pattern — resolving merge conflicts when sequential PRs modify the same files — see `references/sequential-pr-conflict-resolution.md`. ### Maintainer Workflow — Don't Skip the PR When you have **write access** to a repo (you're working on your own project or one where you're a maintainer), there's a strong temptation to skip the branch-PR-merge cycle and push directly to main. **Don't.** The same rules apply to maintainers as to contributors: | Scenario | Branch + PR? | Exception | |----------|-------------|-----------| | New feature (any size) | **Yes** — always | None. Features need review and a clean history entry. | | Bugfix (non-trivial logic change) | **Yes** — always | None. Even one-line fixes can have subtle effects. | | Documentation (README, CHANGELOG, CONTRIBUTING) | **Yes** — preferred | Tiny fixes (typo in a comment, single-line docs correction) may push directly if and only if the user explicitly says "ship it" or "just push it." | | CI hotfix (unblocking a release pipeline) | **Conditional** — branch + PR if time permits; direct push if the pipeline is actively broken and a 2-minute delay compounds the damage | After the hotfix, create a follow-up PR to capture the decision rationale. | | Release prep (version bump, CHANGELOG entry) | **Yes** — always | The release commit itself is the merge commit from the PR. | **The rule of thumb:** If you'd want a code review on it, make it a PR. When in doubt, branch. ### The "merged PR branch is dead" rule When a PR is merged, **the branch is dead.** Any remaining unmerged commits on that branch — even single-commit follow-ups — belong to a new issue, a new branch from `main`, and a new PR. **Common scenario that triggers this correction:** You filed PR #1 for feature A. It merged. While it was under review, you also pushed a commit for fix B to the same branch. Now PR #1 is merged but fix B was not included. Do NOT push fix B's branch to update the merged PR. Fix B needs its own issue, its own branch from current `main`, and its own PR. The trap is feeling efficient — "it's just one more commit on the same branch." But the merged PR's branch no longer exists in a meaningful sense: `main` has moved past the merge point, and any additional commits are orphan history. A fresh branch from `main` is the only clean way to propose new work. **Exception:** Trivial one-line documentation changes that were explicitly part of the same PR scope and were simply forgotten (e.g., a CHANGELOG entry for the merged feature that the reviewer asked you to add). Even then, prefer a fast-follow PR — it keeps the audit trail clean. **The "I have write access" trap:** Write access doesn't mean you're exempt from the process — it means you're *responsible* for upholding the process on behalf of contributors who don't have write access. Every direct push to main is a signal that the PR process is optional, which erodes the convention for everyone. **This is especially important in the transition from agent-assisted build phase to open source.** During the build phase, pushing directly to main was efficient — fast iterations, no external contributors, no review needed. But once CONTRIBUTING.md exists and external contributors could arrive, the process has to shift. The first direct push after establishing conventions does more damage than the hundredth one during the build phase. --- -
phase-2-pull-requests.md 16.8 KB
## Phase 2: Pull Requests ### Before Writing Code 1. **Fork and clone** the repo (if you don't have write access) 2. **Create a feature branch** from the main development branch 3. **Check the branch naming convention** in CONTRIBUTING.md — common patterns: | Pattern | Example | |---|---| | `fix/description` | `fix/login-redirect-null-pointer` | | `feat/description` | `feat/add-jwt-auth` | | `bugfix/description` | `bugfix/issue-42` | | `username/description` | `user/fix-stream-timeout` | | `issue-N-description` | `issue-142-fix-typo` | 4. **Make small, focused commits** — one logical change per commit ### Scope Assessment: Trace Before You Build Before writing code, **trace every code path that could be affected by your change.** A feature request or bug fix that seems local may touch multiple independent paths. Missing one produces a partial fix and a second round of review. **The methodology (demonstrated on rreading-glasses issue #96):** 1. **Map the entry points.** List every handler, endpoint, or CLI command that could serve the data you're changing: ```bash grep -n 'func.*Handler.*get\|func.*Handler.*search\|func.*Controller\.' *.go ``` 2. **Follow each path to the data.** For each entry point, trace: handler → controller method → cache/getter call → data transformation. Does it manipulate the field you're changing? At what line? 3. **Build a flow table.** Every path gets a row: | Code Path | Subtitle Handling | Title Set Where | |-----------|------------------|-----------------| | `handler:getAuthorID` → controller | Smart dedup logic | `controller.go:1049-1071` | | `handler:bulkBook` | Always FullTitle (independent) | `handler.go:242-247` | | `handler:getWorkID` | Passthrough (no manipulation) | None | 4. **Identify the true change surface.** Most paths may be irrelevant (passthrough), some may be intentionally different (search shows full titles). The actual change is usually 1-2 condition checks. The table tells you *which* ones. 5. **Check for pre-existing independence.** If one path already hardcodes a different behavior (e.g., search always shows subtitles), it was a deliberate decision. Don't unify it unless you understand why it diverged. **Why this matters:** Without the trace, you might change the right condition in the right file but miss that a separate handler independently overrides the field. Or you might spend time refactoring four paths when only one matters. The output of this step goes in the PR's "Scope Assessment" section to show the maintainer you checked all paths. ### Study Existing Implementations First Before writing code that extends an interface or adds a new plugin/extractor/tool, **read the existing implementations of the same interface.** This is faster and more accurate than guessing the pattern from the base class alone. **What to read:** 1. **The base class / abstract interface** — understand the contract (`extract()`, `get_state()`, etc.) 2. **One or two concrete implementations** — how they handle incremental processing, error handling, model_fn, state persistence 3. **The wiring / registration point** — how the CLI or registry discovers and invokes it 4. **The tests for existing implementations** — understand the test patterns, fixtures, and what edge cases are covered **Technique:** Use `gh api repos/owner/repo/contents/` to read these files remotely without cloning (see `references/remote-code-exploration.md`). By the time you clone or branch, you should already know exactly where your new file goes, what imports it needs, and how it registers. **The signal that you've studied enough:** you can answer "what's the minimal set of methods my class needs to implement, and what pattern does each one follow?" without looking at the base class again. ### Cross-Repo Comparison for Feature Extraction When contributing improvements inspired by a **different project** (a fork, plugin, or parallel implementation that solved the same problem differently), use cross-repo comparison to extract what the other project did better: 1. **Clone both repositories** side by side: ```bash git clone https://github.com/owner/upstream.git upstream git clone https://github.com/owner/fork.git fork ``` 2. **Read the equivalent module in both** — don't guess the differences. Read both implementations end-to-end. 3. **Build a comparison table** with concrete columns: | Dimension | Upstream (old) | Fork (new) | Improvement | |-----------|---------------|------------|-------------| | Scaling strategy | Full-graph O(N²) | Work-capped incremental | Bound compute to subset | | DB writes | Per-pair commit | Batched executemany | ~1000× fewer round-trips | | Architecture | Stateful class | Pure functions | Testable, composable | | Error handling | try/except | Input filtering pre-check | Explicit precondition | 4. **For each improvement, ask**: "Is this a fundamental algorithmic improvement, a performance optimization, a safety feature, or a structural preference?" This determines which belong in the issue and which are style differences. 5. **Draft the issue** before implementing — document the comparison findings, proposed improvements, and a sketch of the new design. Reference the source project where the pattern was proven. This gives the maintainer a chance to correct direction before you invest in implementation. 6. **Implement with a backward-compatible shell** — the maintainer's existing callers (tests, scripts, integrations) should not break. See the section below on backward compatibility. 7. **Preserve existing tests** — every existing test that still tests valid behavior must pass unchanged. Add new tests for the new code paths. If the existing tests exercised the system via a different interface (class methods vs free functions), keep that interface working. 8. **Run both test suites** — the old tests AND the new tests. If the old tests are testing through a deprecated interface, that's fine — those callers still exist and the maintainer needs to know they still work. 9. **Update ALL project docs that reference the changed area** — README tables, CLAUDE.md architecture docs, DESIGN.md specification, and any inline code comments. A PR that changes how a core module works but doesn't update the developer guide leaves the project in a worse state than it started. ### Backward-Compatibility Pattern for Refactoring When rewriting a module that has existing callers (tests, scripts, integrations), use a **shell-and-pipeline** architecture to avoid breaking them: ``` Old interface (class) ──▶ checks dependencies ──▶ New pipeline (free functions) │ │ └── if absent ──▶ Legacy fallback (old code path) ``` **Implementation steps:** 1. **Extract the new algorithm into free functions.** Each phase of the pipeline is a standalone function with explicit `conn` parameter. No class state, no implicit connection fetching. 2. **Keep the old class interface.** Every public method stays — same name, same signature, same return type. Add a deprecation notice in the docstring if desired, but don't remove it. 3. **Make the main orchestration method a dispatcher.** The class's `run()` / `execute()` method checks whether the new infrastructure exists (e.g., required table in the database, required library). If yes, delegate to the new pipeline. If no, fall back to the old per-method approach. 4. **The legacy fallback preserves old behavior unchanged.** It calls the same class methods in the same order as the original implementation. This ensures test databases without the new infrastructure (e.g., no embeddings table) continue to work. 5. **Keep both code paths until the maintainer explicitly removes the old one.** Don't delete the legacy path — that's the maintainer's call once they've verified the new path covers all cases. **When to use this pattern:** Any refactoring where: - The module has existing tests you must not break - The new implementation requires infrastructure the old one didn't (a DB table, a library, a configuration key) - The change is structural enough that callers might rely on specific intermediate behaviors For a worked example of this pattern applied to a real upstream PR (Cashew sleep cycle refactor, 61 tests preserved), see `references/sleep-cycle-refactor-worked-example.md`. **When NOT to use this pattern:** Adding a new module or feature that has no existing callers — there's nothing to be backward compatible with. ### Commit Messages Default format (Conventional Commits, widely used): ``` type(scope): short description Longer body explaining why the change was made, wrapped at 72 characters. Reference related issues (Fixes #42) in the body. Signed-off-by: Your Name <email> ``` Types: `feat`, `fix`, `refactor`, `docs`, `test`, `ci`, `chore`, `perf`, `style` **Sign-off:** If the project uses DCO (Developer Certificate of Origin), every commit MUST have: ```bash git commit -s # Adds Signed-off-by trailer automatically ``` The DCO certifies you have the right to contribute the code under the project's license. It's legally simpler than a CLA and many projects prefer it. **Never force-push to shared branches** unless the project's contributing guide explicitly asks for it (some projects want rebased, clean history). When in doubt, add commits on top. ### 🔴 Mandatory: Use Project Issue & PR Templates **For both issues AND PRs, check for and use the project's templates first.** #### Issue Templates Before filing an issue, check if the project has structured templates: ```bash # List available issue templates gh api repos/$OWNER/$REPO/contents/.github/ISSUE_TEMPLATE --jq '.[].name' 2>/dev/null # If templates exist, pick the right one and fetch it gh api repos/$OWNER/$REPO/contents/.github/ISSUE_TEMPLATE/bug_report.yml --jq '.content' 2>/dev/null | \ python3 -c "import sys,base64; sys.stdout.buffer.write(base64.b64decode(sys.stdin.read()))" \ > /tmp/issue-template.md 2>/dev/null ``` **If issue templates exist:** - Use the correct template type (bug_report vs feature_request vs task) - Fill in ALL required fields — do not skip sections - Keep the YAML form structure intact — don't remove fields - Include agent disclosure at the end of the body **If no issue template exists**, use the standard structure from the Filing Issues section above. #### PR Templates Before writing any PR body, fetch the project's template: ```bash gh api repos/$OWNER/$REPO/contents/.github/PULL_REQUEST_TEMPLATE.md --jq '.content' | \ python3 -c "import sys,base64; sys.stdout.buffer.write(base64.b64decode(sys.stdin.read()))" \ > /tmp/pr-template.md 2>/dev/null ``` **If a template exists, you MUST:** - Use every section header exactly as written — do not rename, reorder, or remove any - Keep every checkbox — leave unchecked boxes in place so reviewers can see what wasn't done - Fill sections that don't apply with "N/A" or "Not applicable" — do not delete them - Include all HTML comments and link reference definitions from the template - Use `--body-file` (not `--body`) for the PR body to avoid shell escaping issues **If no template exists**, use this default structure: ```markdown <What this PR does, in 1-2 sentences> - <Specific change 1> - <Specific change 2> - [ ] Unit tests added/updated - [ ] Integration tests pass - [ ] Manual testing done (describe) Closes #42 Fixes #123 ``` #### 🔴 Mandatory Compliance Gate Before submitting ANY PR (create or edit), run the compliance checker: ```bash python3 scripts/check-pr-template-compliance.py \ /tmp/pr-body.md /tmp/pr-template.md ``` **If this exits non-zero — DO NOT SUBMIT.** Fix the body and re-run. This gate is not optional. Multiple PRs were rejected on May 19 2026 for template violations across different projects. **One PR per logical change.** Don't mix a bugfix with a refactoring with a feature. Maintainers may want to accept one and reject another, and they can't if they're in the same PR. **Include tests** for new code. If the project doesn't have tests, at minimum document how you verified the change works. **Documentation is not optional.** A feature PR that doesn't update the README, CHANGELOG, or relevant docs is incomplete. This applies even (especially) when you're the maintainer shipping your own project — the README is the user's first impression, and if it doesn't describe the new feature, the feature doesn't exist to the user. The rule: **before opening the PR, verify every user-facing change has a corresponding doc update.** If you can't point to the line in the README that describes what you just added, the PR isn't ready. ### 📋 Mandatory Pre-PR Step: Test Locally First **Do NOT open a PR until you have verified the implementation works in the actual environment.** The user explicitly requires this — "I want to test it locally before opening the PR." A branch with unverified code is not ready for review. The local test must verify more than just "tests pass." It must confirm the feature actually produces correct output in the real runtime: ```bash # For a code change: run the actual feature end-to-end python3 scripts/orchestrate.py full --mode new-feature --question "test question" --agents 3 # Verify the output matches expectations ls -la /tmp/output-dir/ cat /tmp/output-dir/results.json | python3 -m json.tool # For a bugfix: reproduce the original issue first, confirm fix resolves it # For a new command: run --help and verify the new option appears ``` **The distinction matters:** Local tests confirm your code doesn't crash. Local *verification* confirms it produces correct results. You need both before the PR exists. If the user says "test it locally first," they mean verification, not just test-passing. **Checklist before opening:** - [ ] README updated if user-facing behavior changed (new feature, new config key, changed behavior) - [ ] CHANGELOG updated (new entry for unreleased changes) - [ ] Docstrings/type hints on new public APIs - [ ] Existing docs scanned for references to the changed area (outdated examples, stale config snippets) ### Before Opening the PR 1. **Verify everything is committed** — `git status` must show a clean working tree. The most common new-contributor mistake is pushing without committing, which produces "No commits between upstream:main and your:branch" on PR creation. 2. **Rebase on latest upstream** (don't merge unless the project prefers merge commits). If conflicts arise, see `references/rebase-conflict-resolution.md` for the step-by-step resolution workflow. 3. **Run the test suite** locally — don't rely on CI to catch basic failures 4. **Check for merge conflicts** — if there are conflicts, resolve them using `references/rebase-conflict-resolution.md` 5. **Lint your code** per the project's style (run the formatter/linter they use) ```bash # FIRST: verify commits exist git status # Must show: nothing to commit, working tree clean git log --oneline origin/main..HEAD # Must show your commit(s) git fetch origin git rebase origin/main # If conflicts: see references/rebase-conflict-resolution.md # After resolving: git push --force-with-lease # --force-with-lease, not --force ``` ### Documentation Audit Before Opening **Documentation is not optional.** A feature PR that doesn't update the README, CHANGELOG, or relevant docs is incomplete. This applies even (especially) when you're the maintainer shipping your own project — the README is the user's first impression, and if it doesn't describe the new feature, the feature doesn't exist to the user. **Three-document scan for CLI/command changes:** When your PR adds or modifies a CLI command, subcommand, or flag, scan these three locations before opening: 1. **CLI help text** — Run `command --help` and verify the new command appears in the choices list, usage examples, and epilog. If the project has a help string that lists available subcommands (e.g., `choices=['obsidian', 'sessions', ...]`), update it. 2. **README CLI reference tables** — Most projects with CLIs have a table of commands in the README. Add a new row for the command, or update any existing row that your change affects. 3. **Project-internal dev docs** — Some projects maintain a `CLAUDE.md`, `AGENTS.md`, or `DEVELOPMENT.md` with architecture documentation, including tables of modules or interfaces. If your change adds a new module (e.g., a new extractor, new plugin, new tool), that table needs a new row. The test: **before opening the PR, can you point to the exact line in the README that tells a user how to use what you just added?** If not, the PR isn't ready. **CLI-specific checklist:** - [ ] `command --help` output includes the new subcommand - [ ] README CLI reference table has a row for the new command - [ ] Developer docs (CLAUDE.md, AGENTS.md) updated for new modules/interfaces - [ ] Existing examples or usage snippets that reference the changed area are updated --- -
phase-3-after-submitting.md 1 KB
## Phase 3: After Submitting ### CI and Code Review 1. **Monitor CI** — if it fails, fix it promptly. Don't leave a broken PR sitting. 2. **Respond to review feedback** — address each comment, even if just to acknowledge 3. **Don't take feedback personally** — code reviews are about the code, not you 4. **Explain your reasoning** when you disagree with a reviewer's suggestion 5. **Make requested changes promptly** — maintainers reviewing stale PRs is wasted effort ### What to Do If... | Situation | Response | |---|---| | CI fails | Fix and push. Don't force-push over review history unless the project prefers it | | Reviewer asks for changes | Make them, push new commits, re-request review when ready | | PR goes stale (no response) | Bump politely after 1-2 weeks: "Friendly ping — anything else needed here?" | | PR is closed without merge | Don't argue. If it's important, open a discussion to understand why | | You need to abandon the PR | Close it and leave a comment explaining why so others don't duplicate | --- -
phase-35-followup.md 6.3 KB
## Phase 3.5: Follow-up After Maintainer Scope Feedback Sometimes a PR gets merged with a maintainer note that the fix is correct but **incomplete in scope** — it fixes the highest-leverage call site but misses others that need the same treatment. This is not a rejection; it's a direction to expand the fix systematically. The correct response is a systematic follow-up: issue → PR, with a full call-site audit as the bridge between them. > **Pre-flight checklist:** Before beginning a Phase 3.5 investigation, run > the three-state check in `references/tracking-upstream-fix-status.md` to > verify the fix isn't already in your local install, a released tag, or an > existing open PR. This avoids duplicating work. ## Step 1: Understand the Scope Note The maintainer's comment tells you exactly what they noticed was missing. Before doing anything, parse their note: - **What specific call sites or patterns did they name?** (e.g., "`metrics.py` has seven more" or "`db.py::connect()` is the canonical factory") - **What's the principle behind the gap?** (e.g., "every `sqlite3.connect()` call site needs this, not just the session module") - **Is there an existing open PR from the maintainer** that already addresses part of the gap? Check their repo for related PRs. ### Step 2: Do a Full Call-Site Audit Grep the entire codebase for the pattern that needs fixing, excluding tests initially (the maintainer's concern is production code): ```bash # Find ALL direct call sites, excluding tests grep -rn 'sqlite3\.connect' --include='*.py' core/ scripts/ other_dirs/ \ | grep -v __pycache__ | grep -v test | grep -v 'busy_timeout' ``` Then build a **completion table** with four columns: | Module | Call sites | Fixed by (PR) | Status | |--------|-----------|---------------|--------| | `core/session.py` | 1 | Your PR #56 | ✅ Fixed | | `core/metrics.py` | 7 | — | ❌ Open | | ... | ... | ... | ... | **Key technique: cross-reference against existing PRs.** If there's an open PR (e.g., PR #59) that already covers most of the remaining sites, don't duplicate that work. Instead, identify what that PR *still misses*: ```bash # Check which files an existing PR touches git fetch origin pull/59/head:pr59 git diff main..pr59 --name-only # Then check if any grep-identified sites fall outside those files # Those are the remaining gaps ``` This gives you the precise delta: sites covered by your merged PR + sites covered by the existing follow-up PR = coverage. Anything left is your gap. ### Step 3: File a Comprehensive Issue Open an issue that documents the full audit. Don't just say "we need to fix the metrics module too" — show the complete picture: ```markdown **Context:** PR #56 added `X` to `core/session.py` as the highest-leverage call site. The maintainer noted ([link to comment]) that `core/db.py::connect()` and `core/metrics.py` also needed the same treatment. **Completion table:** | Module | Direct call sites | Status | |--------|------------------|--------| | `core/session.py` | 1 | Fixed by #56 | | `core/db.py::connect()` | delegates to session | ✅ transitive | | `core/metrics.py` | 7 | Covered by #59 | | ... | ... | ... | | `scripts/migrate_embeddings.py` | 3 | ❌ Still open | | `extractors/obsidian.py` | 1 | ❌ Still open | **Remaining gaps:** - `extractors/obsidian.py` (1 site at line 191) - `scripts/migrate_embeddings.py` (3 sites at lines 57, 128, 144) **Suggested fix:** Apply the same `busy_timeout=5000` PRAGMA pattern used in #56. ``` The table format is critical. It shows the maintainer you did the full audit, found the gaps, and aren't duplicating existing open work. A table is scannable and leaves no ambiguity about what's been checked. ### Step 4: Create the Follow-up PR The PR should: 1. **Only address remaining gaps** — don't redo files already covered by your merged PR or existing open PRs. Duplicating work creates merge conflicts and makes the maintainer's review harder. 2. **Follow the same fix pattern** as your original PR for consistency 3. **Update the issue reference** — close the scope-gap issue once merged ```bash # Branch from the latest upstream main git fetch upstream main git checkout -b fix/remaining-busy-timeout-sites upstream/main # Apply the same pattern to gap sites, commit, push gh pr create --title "fix: apply busy_timeout=5000 to remaining sqlite3.connect sites" \ --body "## Summary\n\nCompletes the scope outlined in #ISSUE_NUMBER.\n\n### Remaining sites\n- `extractors/obsidian.py` (line 191)\n- `scripts/migrate_embeddings.py` (lines 57, 128, 144)\n\n### Test plan\n- [ ] Syntax check on changed files\n- [ ] Existing test suite passes\n\nCloses #ISSUE_NUMBER" ``` ### When There's Already an Open PR from the Maintainer If the maintainer opened their own PR (#59) to address part of the scope: 1. **Don't open a competing comprehensive PR.** Instead, create a complementary PR that covers only the gaps the maintainer's PR missed. 2. **Reference both** the original issue/PR context and the maintainer's open PR in your issue and PR body. 3. **The maintainer's PR covers the bulk; your complementary PR handles the stragglers.** This is cleaner than one superseding PR — it respects the maintainer's work while filling the gap they overlooked. ### Pitfall: The Installed-Code Ambush When tracking the status of an upstream fix you developed on your local machine, **check the installed code before reporting it as broken.** The local installed copy may already have the fix applied from your development work, even though the upstream release doesn't. ```bash # Before assuming installed code is broken: python -c "import inspect, core.embedding_service; print(inspect.getsource(core.embedding_service.get_default_service))" ``` If the fix is in the installed code, your local machine is fine — the gap is only for fresh installs of the unreleased version. This matters when you're deciding whether to prioritize a workaround vs. waiting for the upstream release. The dual trap: - **Direction 1:** Patching installed code without filing an issue or PR → erodes process (covered above as "The installed code trap") - **Direction 2:** Assuming installed code is still broken when the fix was already applied locally → wasted investigation Both directions have the same root cause: *asserting state without verification.* Fix: check first, speak second. --- -
phase-4-release-process.md 4 KB
## Phase 4: The Release Process (After Merge) Once a PR is merged to the main branch, getting it to users requires a release. This phase is often overlooked by contributors but essential for maintainers. ### Sequence After Merge ```bash # 1. Pull the merged main git checkout main && git pull # 2. Update version and changelog # Edit pyproject.toml (version field) and CHANGELOG.md (new section) git commit -s -m "chore: bump to vX.Y.Z" # 3. VERIFY version matches before tagging # Run this check — if it fails, fix pyproject.toml before proceeding PACKAGE_VERSION="$(grep -Po '^version = \"\K[^\"]+' pyproject.toml)" echo "Package version: $PACKAGE_VERSION Tag: v$PACKAGE_VERSION" # 4. Tag and push (triggers release workflow on many projects) git tag v$PACKAGE_VERSION && git push origin main --tags # 4. Create a GitHub Release with notes gh release create vX.Y.Z --title "vX.Y.Z — Title" --notes "..." ``` **Version numbering conventions:** - `v1.0.0` — MAJOR: breaking changes - `v0.4.0` — MINOR: new features (pre-1.0: significant additions) - `v0.4.1` — PATCH: bug fixes (pre-1.0: small fixes) ### Release workflow anatomy Many projects use a separate release workflow (`.github/workflows/release.yml`) that: - Triggers on `v*` tag pushes (not on branch pushes) - May gate on tests passing before publishing - Publishes to a package registry (PyPI, npm, etc.) via trusted publishing OIDC - Creates a deployment record visible on the repository's Deployments page **Key gotcha:** Merging to main does NOT trigger the release workflow. You must push the tag separately. If you forget, the release won't happen — no error, no notification, just silence. The PyPI publish won't fire until the tag exists. ### GitHub Releases vs Tags A tag is just a pointer to a commit. A GitHub Release is a tag + release notes + optional assets. They are separate concepts: ```bash # Tag only (no release page, no announcement) git tag v1.0.0 && git push origin v1.0.0 # Tag + GitHub Release gh release create v1.0.0 --title "v1.0.0 — Title" --notes "..." ``` Always create a GitHub Release after tagging. The release notes are what users see on the repository's Releases page, and what gets announced via GitHub's notification system. Without a Release, the tag exists but is invisible to most users. ### Handling release metadata **`--body-file` for gh commands:** When your release notes or PR body contains special characters (backticks, `&`, quotes, braces), use a file instead of an inline string: ```bash # WRONG — shell interprets special chars gh release create v1.0.0 --notes '{"query": "test", "exclude_tags": ["private"]}' # RIGHT — use a file cat > /tmp/release.md << 'EOF' Adds `exclude_tags` filtering. EOF gh release create v1.0.0 --title "v1.0.0" --notes-file /tmp/release.md ``` This avoids shell interpretation of backticks, `$` signs, curly braces, and ampersands in structured text like JSON examples or code blocks. ### What to do when a commit accidentally lands on main If you accidentally push a feature commit directly to `main` (bypassing the PR process), the correct fix is: ```bash # 1. Create a branch from the accidental commit git branch feat/description HEAD # 2. Revert the commit on main git revert --no-edit HEAD git push origin main # 3. Push the branch and open a proper PR git push -u origin feat/description gh pr create --base main --head feat/description ``` Do NOT force-push to main to "undo" the commit — rewriting published history causes problems for anyone who has already pulled. A revert is clean, auditable, and doesn't require force push. If the branch already exists (from `git checkout -b` that was never used), delete it first: ```bash git branch -D feat/description git push origin --delete feat/description # Then proceed with the steps above ``` **Cherry-pick note:** After reverting main, create the feature branch from the current main (`git checkout -b feat/description main`) and cherry-pick the original commit (`git cherry-pick <sha>`). This ensures the branch is based on the current tip of main, not on a now-reverted ancestor. -
pitfalls.md 18.3 KB
## Pitfalls ### Backtick expansion in `--title` See `github-issues` skill for the full treatment. In short: don't use backticks in double-quoted shell strings — they undergo command substitution. ```bash # WRONG — backticks get shell-expanded gh issue create --title "`command` crashes with error" # RIGHT — single quotes gh issue create --title '`command` crashes with error' # RIGHT — no backticks in title gh issue create --title "command crashes with error" ``` ### Silent failure on non-existent labels `gh issue create --label "nonexistent"` exits 1 and creates nothing. Verify labels first: ```bash gh label list # See what exists ``` Or create the issue without labels and add them after: ```bash gh issue create --title "..." --body "..." gh issue edit <NUMBER> --add-label "bug" ``` ### The "I'll just fix it quickly" trap The smallest-seeming changes can have the most impact. A one-line "fix" that changes behavior without understanding the architecture can break subtle edge cases. Always: 1. Understand why the code is the way it is 2. Check if tests exist for the area you're changing 3. Run the full test suite, not just your new test ### Premature PRs for discussion-mode ideas When the user is workshopping an idea ("what if we contributed X?", "imagine a feature that Y"), do NOT interpret that as a directive to fork, branch, and create a PR. This is discussion mode. Wait for explicit directive signals like "create the PR", "file the issue", "send the patch" before executing. ### Force-push etiquette Unless CONTRIBUTING.md explicitly asks for rebased/clean history (Linux kernel style), **do not force-push** to an open PR after a reviewer has looked at it. Force-pushing destroys the review history and makes it impossible to see what changed between review rounds. Add fixup commits instead. Maintainers can squash on merge. ### CI debugging workflow When CI fails after a push, the systematic debugging loop is documented in `references/ci-debugging-loop.md`. It covers: reading CI logs, reproducing locally, isolating pre-existing vs new failures, common root cause categories (env var leakage, stale metadata, refactored methods, immutable types), and the fix-verify loop. ### CI as a testing crutch Don't push broken code assuming CI will catch it. Run tests locally first. CI should confirm your work is clean, not discover basic failures. Pushing obviously broken code wastes maintainer CI resources (which the project pays for). ### Not all labels are the same Some projects use labels like `good first issue` to indicate approachability. Others use `help wanted` for anything they'd accept help on. Still others tag issues with `needs reproduction` or `needs discussion`. Understand the project's label taxonomy before picking up issues. ### Pitfall: `gh pr create --base main` fails silently on repos with custom default branches The most common reason for `gh pr create` to fail with an opaque "Base ref must be a branch" error is that the target repo's default branch isn't named `main`. Many older or differently-configured repos use `master`, `develop`, or `trunk`. **The fix is a one-line discovery step before PR creation:** ```bash BASE_BRANCH=$(gh repo view owner/repo --json defaultBranchRef --jq '.defaultBranchRef.name') gh pr create --repo owner/repo --base "$BASE_BRANCH" ... ``` This applies to both same-repo and cross-fork PRs. The cross-fork section above now includes this as step 2 in the troubleshooting checklist. ### Pitfall: Cross-fork PR: `gh pr create` fails with opaque GraphQL error When forking a repo into a personal account, `gh pr create --head you/repo:branch` fails with: ``` pull request create failed: GraphQL: Head sha can't be blank, Base sha can't be blank, Head user can't be blank, Head repository can't be blank, No commits between upstream:main and , Head ref must be a branch, not all refs are readable ``` **Don't waste time debugging the `--head` format.** Even the correct `username:branch` format may fail. Try the **GitHub API directly** first: ```bash gh api repos/owner/upstream/pulls \ -f title="fix(scope): description" \ -f head="your-github-username:branch-name" \ -f base="main" \ -f body="## Summary\n\nPR description here" ``` This usually works, but **both approaches can fail** with 404 or permissions errors when: - The **target organization has third-party access restrictions** or SAML SSO. The token must be authorized for the org via `gh auth refresh --sso`. - The fork is very new and GitHub's systems haven't fully indexed it yet - The token's OAuth scopes don't include `public_repo` — verify with `gh auth status` ### Cross-fork PR: When Both `gh pr create` and the API Fail (Org Restrictions) If both `gh pr create` and the REST API return 404 despite the compare endpoint confirming the branches are connected, the fallback is the **GitHub compare URL**: ```bash # Print the URL — open in browser to create the PR manually echo "https://github.com/upstream-org/upstream-repo/compare/develop...your-github-username:branch-name" # On macOS, open it directly: open "https://github.com/upstream-org/upstream-repo/compare/develop...your-github-username:branch-name" ``` The compare URL pre-fills the diff and lets you write/edit the body. Requires human click-through — it's a hand-off point rather than fully automated, but it works when the API doesn't. **Prerequisites:** - Fork must exist (created via `gh repo fork` or the web UI) - Fork's default branch must be synced with upstream: ```bash git fetch upstream main git push origin upstream/main:main ``` - Branch must have been pushed to the fork **Head format:** The `head` field takes `owner:branch` — the GitHub username (or org name), not the repo name. For a fork at `you/hermes-agent-1` with branch `fix/foo`, use `head="you:fix/foo"`. #### Troubleshooting checklist when both approaches fail: 1. Verify the fork exists and branch is pushed: `gh api repos/you/fork/branches/your-branch --jq '.name'` 2. **Verify the target repo's default branch name** — it may not be `main`: `gh repo view owner/upstream --json defaultBranchRef --jq '.defaultBranchRef.name'` 3. Verify the compare API works: `gh api repos/owner/upstream/compare/develop...you:branch --jq '.status'` — should return `ahead`, `behind`, or `identical` 4. Check token authorization for the org: `gh auth refresh --sso` (if the org requires SAML) 5. Check token has `public_repo` scope: `gh auth status` 6. A **404 on PR creation with a valid compare check** usually means an **org-level access restriction** — use the compare URL as the fallback See `references/cross-fork-pr-workaround.md` for the full debugging session transcript and the org-restriction case study added in v1.4.0. --- ### Post-merge scope creep — extra commits on a merged branch A variant that catches agents off guard: **the PR was merged, but the branch still exists locally with additional commits that were never on main.** Those commits are not in the repo's history — they only exist on your local branch. When the PR is already merged, do NOT push the remaining commits to the old branch or try to re-open the PR. The correct workflow: ```bash # 1. Switch to main, pull the latest (which includes the merged PR) git checkout main && git pull origin main # 2. Create a fresh branch from the current main git checkout -b fix/separate-issue # 3. Apply the change (cherry-pick from the old branch, or re-apply manually) git cherry-pick <commit-sha-from-old-branch> # 4. File a NEW issue, commit, push, open a NEW PR gh issue create --title "Separate issue" --body "..." git commit -s -m "fix: description of separate issue" git push -u origin HEAD gh pr create --title "fix: description" --body "Closes #ISSUE" ``` **Why the old branch is dangerous:** If you push more commits to a branch whose PR was already merged, those commits orphan — they're on the branch but not on main. No CI will run against them, no review will happen, and the only way they reach main is through a new PR from a main-based branch. ### Scope creep on an open PR — pushing a separate issue to the same branch A distinct variant that applies *before* merge: **a PR is already open, you discover a separate issue that needs fixing, and you push it to the same branch.** This is wrong for three reasons: 1. **The PR was opened for a specific issue.** If the original PR merges and the branch is closed, the second fix disappears with it — it was never on main. 2. **If the PR hasn't merged yet**, the reviewer sees unrelated changes mixed into the same diff. They can't approve one and reject the other. 3. **You lose the audit trail.** The second fix has no issue of its own, no separate review, and no independent merge record. **The correct response when you discover a separate issue while a PR is open:** ```bash # 1. Leave the open PR alone. Do NOT push to that branch. # 2. Create a fresh branch from the current main: git checkout main && git pull git checkout -b fix/second-issue-description # 3. Apply the fix (cherry-pick if you already committed on the wrong branch, # or re-apply manually) # 4. File a NEW issue, commit, push, open a NEW PR gh issue create --title "Second issue" --body "..." git commit -s -m "fix: description of second issue" git push -u origin HEAD gh pr create --title "fix: description" --body "Closes #NEW_ISSUE" ``` **The "but it's just a small fix" trap:** This is the same rationalization that produces one-line direct-to-main pushes. A fix's size doesn't determine whether it needs its own issue and PR — its *logical independence* from the open PR does. If it addresses a different problem, it gets its own lifecycle. **Exception:** Fixup commits for *review feedback* on the open PR are fine — those are part of the same logical change. The boundary is: "does this change make sense to a reviewer without knowing about the open PR's issue?" If yes, it's a separate change and needs its own PR. ### The "I have write access" trap — accidental main commit recovery You branched, you worked, you committed — but you were on `main`, not the feature branch. The commit is now on main and pushed. This is not a crisis, but the fix requires care: ```bash # 1. Create a branch at the commit so the work isn't lost git branch feat/my-feature HEAD # 2. Revert the commit on main (creates a new commit, does NOT rewrite history) git revert --no-edit HEAD git push origin main # 3. Cherry-pick onto a fresh branch from current main git checkout -b feat/my-feature main git cherry-pick <original-commit-sha> git push -u origin HEAD # 4. Open PR from the branch gh pr create --title "..." --body "..." ``` **Why not just force-push to reset main?** Because main is shared history. If anyone else has pulled (CI, another agent session, a collaborator), force-pushing creates divergence. Revert is safe — it creates a new commit that undoes the change, which merges cleanly with anyone else's history. **Why not just leave the commit on main and open the PR from a branch?** Because the commit already on main means "no commits between `main` and `branch`" error. The PR system sees the feature branch as behind main, not ahead. The cherry-pick onto a main-based branch creates a clean diff. **One gotcha:** After reverting and cherry-picking, the git history looks like: ``` A — B (feature commit) — C (revert) ← main \ D (cherry-pick of B) ← feat/my-feature ``` The `gh pr create` may fail with "no commits between main and feat/my-feature" if the cherry-pick SHA matches the original commit SHA. In practice, cherry-pick creates a new SHA, so this usually works. If it doesn't, `git commit --amend --no-edit` on the branch creates a new SHA and unblocks the PR. ### The "I have write access" trap — release edition The same trap applies at release time. After a feature PR merges, the natural next steps are: ```bash git checkout main && git pull git tag vX.Y.Z && git push origin vX.Y.Z ``` But that's not the complete release cycle. Two things are easy to forget: 1. **GitHub Releases are not automatic.** The tag triggers PyPI publish via the release workflow, but GitHub Releases are separate — they need `gh release create`. Without this, the Releases page shows tags without release notes, and users browsing GitHub don't see the changelog. 2. **Release backlog.** If a previous release was shipped without a proper GitHub Release (common during agent-assisted build phases), backfill it. The Releases page should show every release that exists on PyPI. Use `gh release create vX.Y.Z --title "..." --notes "..."` to fill in gaps. **Full release checklist for maintainers:** ```bash # 1. After PR merges, update local main git checkout main && git pull # 2. Create the release commit (version bump + CHANGELOG) # This should have been done in the PR — verify before tagging # 3. Verify version matches what you intend to release PACKAGE_VERSION="$(grep -Po '^version = \"\K[^\"]+' pyproject.toml)" echo "Releasing: v$PACKAGE_VERSION" # 4. Tag and push git tag v$PACKAGE_VERSION && git push origin v$PACKAGE_VERSION # Release workflow runs tests, builds, publishes to PyPI # 5. Verify PyPI publish succeeded # Check: https://pypi.org/project/<project>/ # 6. Create GitHub Release with release notes gh release create v$PACKAGE_VERSION --title "v$PACKAGE_VERSION — Title" --notes "Changelog summary..." # 7. Update project metadata files (AGENTS.md, any version references in docs) # that were touched by the release changes ``` **If the release workflow fails after tagging:** 1. Fix the issue in a PR (not directly on main) 2. Merge the fix PR 3. **Do not re-push the same tag** — GitHub won't re-trigger the workflow. Instead: - Delete the tag: `git tag -d vX.Y.Z && git push --delete origin vX.Y.Z` - Recreate it on the new merge commit: `git tag vX.Y.Z && git push origin vX.Y.Z` - Or bump the version and create a new tag (`vX.Y.Z+1`) ### Issue Debt — File First, Even When Direction is Clear When a conversation with the maintainer converges on a feature direction, the natural next step is to file an issue before implementing. This applies even when: - The maintainer said "go" or "let's do it" - The scope seems clear and well-understood - The change is small (under 50 lines) The issue is the durable record. Conversation context is lost across sessions, but an issue persists. Filing one ensures: - A future session can find the decision rationale - The maintainer can review and correct the approach before code is written - The conversation-to-code handoff is explicit rather than implicit The pattern: **discuss → issue → implement → PR**. Never skip from "discuss" to "implement" without the issue in between. ### The "installed code" trap — debug → issue → PR, not patch When debugging installed code you have write access to (your own project or a repo you maintain), there's a strong temptation to skip from "found the root cause" directly to "patching the file." This is a distinct variant of the Issue Debt trap — the fix isn't speculative, you fully understand it — but the process still applies: **Even when you know the exact fix, even when it's five lines, even when you traced root cause yourself — file the issue first, then PR the fix.** This has been corrected multiple times across different projects (direct patching of installed plugin code, modifying upstream library files in site-packages, applying fixes without PR review). The pattern is always the same: 1. Debug the issue, find root cause ✅ 2. Understand the exact fix needed ✅ 3. ~~Apply the patch directly to installed code~~ ❌ 4. File an issue documenting root cause and proposed fix ✅ 5. Branch, implement, submit PR ✅ Step 3 is the trap. It feels efficient because you already know the answer. But it bypasses: - **The durable record** — conversation context about the bug is lost across sessions; the issue persists - **Review** — even obvious fixes can have edge cases or alternative approaches - **CI validation** — the fix lands without tests running - **Process integrity** — every skipped step erodes the convention for future contributions The fix for the installed patch: revert it (git checkout or restore the original), file the issue, then proceed with a proper branch + PR. **Exception:** CI hotfixes where the pipeline is actively broken (same as the "Don't Skip the PR" table). Even then, file a follow-up issue documenting the root cause. ### Pitfall: The "debugging discovery" shortcut When you're deep in an active debugging or dogfooding session and you discover a root cause that needs a fix, there is a strong gravitational pull to apply the fix immediately — patch the file, rebuild the container, re-test, all within the same session without filing an issue. **This is the same trap as the installed-code trap, with a different rationalization.** The rationalization sounds like: "I'm already debugging. I found the problem. Applying the fix now is part of the same flow. Filing an issue would break momentum." This is wrong for three reasons: 1. **The fix is a separate action from the diagnosis.** Finding the root cause is debugging. Applying the fix is a code change. These are different activities governed by different processes. The debugging flow should produce an issue documenting the root cause, not a commit. 2. **The container doesn't make it any less of a code change.** If you patched the file in a Docker service source, rebuilt the image, and restarted the container — you made a production code change without an issue. The fact that it was "just testing" or "part of debugging" doesn't exempt the file from the issue requirement. 3. **The session ends and the context is lost.** The issue is the permanent record. The conversation thread about "I fixed X while debugging Y" vanishes when the session wraps. Without an issue, the next session finds the fix in the code with zero context about why it was done or what alternatives were considered. **The fix if you already did this:** Revert the code, rebuild the container to restore original behavior, file the issue, then proceed with a branch + PR. The momentary cost of reverting is less than the long-term cost of a process exception that erodes conventions. **This was corrected in a real session:** During a GroktoCrawl debugging session, a fix to the `llm-svc` fixture was applied and the container rebuilt — all without an issue. The user asked "are you making code changes without an issue filed?" The container was reverted, the issue was filed, and the fix proceeded through the proper lifecycle. -
rebase-conflict-resolution.md 4.1 KB
# Rebase Conflict Resolution When a PR branch has diverged from `main` (because other PRs merged while yours was open), you need to rebase. If that rebase produces conflicts, here's how to resolve them systematically. ## The Workflow ```bash # 1. Update main git checkout main && git pull origin main # 2. Rebase your branch onto main git checkout feat/your-branch git rebase main ``` **Expected outcomes:** - `Successfully rebased` → no conflicts. Push with `--force-with-lease`. - `CONFLICT (content): Merge conflict in <file>` → conflicts to resolve. ## Resolving Conflicts ### 1. Identify the conflict markers ```bash grep -rn "<<<<<<<\|=======\|>>>>>>>" --include='*' . | grep -v node_modules | grep -v '.git/' ``` Each conflict looks like: ``` <<<<<<< HEAD (current branch's version — what's on main) content from main ======= content from your branch's commit >>>>>>> commit-hash (your commit message) ``` ### 2. Understand what each side represents | Section | Meaning | |---------|---------| | `<<<<<<< HEAD` to `=======` | What's currently on `main` (the base you're rebasing onto) | | `=======` to `>>>>>>> <sha>` | What your commit introduced | During a rebase, you're applying your commits *on top of* main. `HEAD` = what's already on main; the bottom section = what your change tried to do. ### 3. Resolve the conflict - **If both sides' content is needed** — keep both, in the right order. - **If one side supersedes the other** — keep the version from main that already incorporates similar logic. - **If both sides added independent content near the same line** — keep both, stacked. Edit the file to produce the correct result, **removing all conflict markers**. ### 4. Verify no markers remain ```bash grep -c "<<<<<<<\|>>>>>>>" <resolved-file> # Must return 0 ``` ### 5. Stage and continue ```bash git add <resolved-file(s)> git rebase --continue ``` ### 6. Handle editor hangs in non-interactive environments If `git rebase --continue` opens an editor in a non-interactive terminal (agent session, CI), it will time out: ```bash # Option A: Skip the editor entirely GIT_EDITOR=true git rebase --continue # Option B: Set EDITOR to a no-op EDITOR=true git rebase --continue ``` This preserves the original commit message without requiring interactive editing. ### 7. Push the rebased branch ```bash git push --force-with-lease origin feat/your-branch ``` **Use `--force-with-lease`, NOT `--force`.** `--force-with-lease` checks that no one else has pushed to the branch since you last fetched. It's safer. ### 8. Check the PR for resolved status ```bash gh pr view <number> --json mergeable # Should show "MERGEABLE" or clean ``` ## Common Patterns ### Pattern A: Both sides added content to the same area (most common) Both main and your branch added new sections near each other. Keep both, ordered logically. ``` <<<<<<< HEAD - /council hybrid "question" ← from main ======= - /council premortem "question" ← from your branch >>>>>>> feat/premortem-mode ``` Resolution: ``` - /council hybrid "question" - /council premortem "question" ``` ### Pattern B: One side refactored what the other changed Main has a refactored version of the same code your branch touched. Read the version on main to understand the new structure, then re-apply your intent. ### Pattern C: Semantic conflicts (no merge markers but wrong behavior) A rebase can succeed with zero conflict markers but produce incorrect behavior if the code on main changed the assumptions your code depends on. Always smoke-test after rebasing. ## Key Principles 1. **The rebase applies your commits one at a time.** If you have multiple commits, you'll resolve conflicts multiple times — once per commit. This is normal. 2. **Resolve each conflict in the context of the specific commit being applied.** Don't try to produce the final merged result in one shot — make each commit's change consistent with what's already on main at that point. 3. **Never force-push to main.** Force-push is for feature branches after rebase, where you're the sole contributor to that branch. 4. **Force-push requires user approval.** Always flag that a force-push is needed and wait for approval before executing. -
release-workflow-completion.md 5 KB
# Release Workflow Completion Release lifecycle steps that are easy to miss — from a messy agent-assisted release cycle. ## The Full Release Cycle After a feature PR merges, the complete sequence is: 1. **Update local main** ```bash git checkout main && git pull ``` 2. **Create the release commit** (version bump in pyproject.toml + CHANGELOG entry) ```bash git commit -s -m "chore: bump to vX.Y.Z" git tag vX.Y.Z && git push origin main --tags ``` 3. **Verify PyPI publish succeeded** ```bash curl -s https://pypi.org/pypi/<project>/json | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['info']['version'])" ``` 4. **Create GitHub Release** (separate from tag — easy to forget) ```bash gh release create vX.Y.Z --title "vX.Y.Z — Title" --notes "..." ``` 5. **Check the Releases page** — verify it shows at [github.com/owner/repo/releases](https://github.com/owner/repo/releases) ## Missed Release — Backfill If a previous release tag exists but has no GitHub Release (common during build phases), backfill: ```bash # Check what releases exist gh release list --json tagName,name,createdAt # Backfill a missing one gh release create v0.3.0 --title "v0.3.0 — Title" --notes "Release notes..." ``` This is important for anyone browsing the project's release history — gaps make the project look abandoned. ## Release Workflow Failure Recovery **Scenario:** Tag was pushed, release workflow ran, PyPI publish failed. **Do NOT** push the same tag again — GitHub won't re-trigger the workflow. Instead: **Option A — Keep the version, re-push the tag (risky):** ```bash git tag -d vX.Y.Z # delete locally git push --delete origin vX.Y.Z # delete remotely # Then fix, commit, and re-tag on the fix commit git tag vX.Y.Z && git push origin vX.Y.Z ``` **Option B — Bump the version (safer):** ```bash # Increment patch version, update CHANGELOG git commit -s -m "chore: bump to vX.Y.Z+1" git tag vX.Y.Z+1 && git push origin main --tags ``` ### Pitfall: Recreating a tag orphans its GitHub Release If you delete and recreate a tag (Option A), the associated GitHub Release becomes a **draft**. The release page shows the tag but the release is in an unpublished state — users see nothing. This happens because the release is bound to the tag's original commit SHA, and the new tag points to a different commit. After recreating the tag, check the release state: ```bash gh release view vX.Y.Z --json isDraft,isPrerelease ``` If `isDraft` is true, publish: ```bash gh release edit vX.Y.Z --draft=false ``` This also resets the "latest" flag — run `gh release edit vX.Y.Z --latest` if needed. **Better to avoid the whole problem:** Prefer Option B (bump version) over Option A (recreate tag). The extra version number is cheap insurance against release metadata corruption. ### Pitfall: Version mismatch between pyproject.toml and tag The tag name and the version in `pyproject.toml` MUST agree. If you tag `v0.5.0` but `pyproject.toml` still says `version = "0.4.0"`, the build produces `hermes_cashew-0.4.0-*` files and PyPI rejects them with `400 File already exists` (since 0.4.0 was already published). **Check before tagging:** ```bash grep '^version = ' pyproject.toml # Must match the tag you're about to create ``` If you already tagged and pushed with the wrong version: 1. Delete the tag: `git tag -d vX.Y.Z && git push --delete origin vX.Y.Z` 2. Fix the version in `pyproject.toml` (via a PR) 3. Wait for PR to merge 4. Tag on the merge commit **Don't fix the version AND tag on the same direct-to-main commit** — that's trading one process violation for another. ## Channeling Release Notes Without Shell Escaping When your release notes or PR body contain special characters (backticks, `&`, quotes, braces, JSON), use a file instead of an inline string: ```bash # Write notes to a file to avoid shell interpretation cat > /tmp/release.md << 'EOF' ## Summary Adds `exclude_tags` filtering. ### Usage ```json {"query": "test", "exclude_tags": ["vault:private"]} ``` EOF # Then reference the file gh release create vX.Y.Z --title "Title" --notes-file /tmp/release.md ``` The `<< 'EOF'` (quoted delimiter) prevents the shell from expanding variables or interpreting backticks inside the heredoc. ## Post-Release Project Housekeeping After any release, check these project metadata files for staleness: | File | What to check | |------|---------------| | `AGENTS.md` | Version references, dependency specs, config key counts, architecture description, open issue count. **Must be updated** — the next AI agent session reads this to orient itself. Stale AGENTS.md causes agents to operate on wrong assumptions. | | `README.md` | Version badge, feature descriptions, setup instructions, config reference | | Issue tracker | Milestones that should be closed, issues that were resolved | The AGENTS.md is especially important — it's what the next AI agent session reads to orient itself. Stale AGENTS.md causes agents to operate on wrong assumptions (wrong dependency type, wrong config shape, references to removed code). -
remote-code-exploration.md 2.9 KB
# Remote Code Exploration with `gh api` When contributing to an unfamiliar open source project, you need to understand the codebase before writing code. The fastest way is often **not** to clone the repo — instead, use the GitHub API remotely to explore specific directories and files. ## Why Remote First | Approach | Cost | Best For | |----------|------|----------| | `gh repo clone` | Full clone (can be slow for large repos) | When you need to run the project or make many edits | | `gh api repos/.../contents/` | One API call per directory | When you only need to study one area of the codebase | | `gh api repos/.../git/trees` | One API call for the full tree | When you need the complete file listing | | Web browser | Interactive but slow | When you need to read docs formatted for web | For **studying a plugin/extension interface** before implementing, remote exploration is usually faster because you only fetch the relevant files. ## Basic Commands ### List a directory ```bash gh api repos/owner/repo/contents/path/to/dir --jq '.[].name' ``` This returns file/directory names in the target path. No clone needed. ### Read a file ```bash gh api repos/owner/repo/contents/path/to/file.py --jq '.content' | base64 -d ``` The API returns file content base64-encoded. Decode inline. ### Read multiple files quickly ```bash for f in __init__.py example_extractor.py; do echo "=== $f ===" gh api repos/owner/repo/contents/extractors/$f --jq '.content' | base64 -d echo done ``` ### Get the full source tree ```bash gh api repos/owner/repo/git/trees/main?recursive=1 --jq '.tree[].path' ``` ## When to Use This Pattern - **Studying an interface before implementing against it** — read the base class (e.g., `BaseExtractor`), an existing implementation (e.g., `sessions.py`), and the wiring (e.g., CLI parser choices) in one shot - **Checking if a certain type of file exists** — discover project structure without cloning - **Quick validation** — verify that a pattern exists before writing code that depends on it ## When to Clone Instead - You need to run the project (build, test) - You're making changes that need local validation (test suite, linting) - You're reading many files across many directories (API rate limits apply) - The repo is small (clone is faster than N API calls) ## Pitfall: Base64 Decoding The GitHub API returns file contents base64-encoded. Always pipe through `base64 -d`. For large files, the API response may be truncated — in that case, use `?ref=branchname` and check for `"truncated": true` in the response. For truncated files, clone the repo instead. ## Pitfall: Binary Files The `contents/` API endpoint does not work for binary files — it returns a `"download_url"` instead of `"content"`. Use the download URL with `curl` for binary assets. ## Pitfall: API Rate Limits Unauthenticated requests are limited to 60/hour. Authenticated (`gh api`) uses the OAuth token and gets 5000/hour. Always use `gh api` over bare `curl`. -
sequential-pr-conflict-resolution.md 3.3 KB
# Resolving Conflicts Between Sequential PRs ## Context When implementing a chain of dependent PRs that modify the same files, merge conflicts are inevitable when upstream PRs merge before yours. This is a common pattern in multi-PR feature rollouts and has a systematic resolution strategy. ## The Pattern Three times in one session this pattern appeared: both PRs added independent code to the same file. The conflicts were merge artifacts, not competing design decisions. In every case, the correct resolution was to **keep both sets of changes**. ## Resolution Workflow ### Step 1: Check PR status ```bash gh pr view N --json mergeStateStatus,mergeable # MERGEABLE = clean, CONFLICTING = needs attention ``` ### Step 2: Fetch and merge ```bash git fetch upstream main git merge upstream/main ``` **Always use `merge` (not `rebase`)** on shared PR branches. Rebase rewrites history — force-pushing after review hurts the audit trail. ### Step 3: Identify conflict regions ```bash grep -n "<<<<<<<\|=======\|>>>>>>>" path/to/file.py ``` ### Step 4: Resolve each conflict For each conflict region, determine if the changes are: 1. **Independent additions** — both heads add new code that doesn't overlap logically. → Keep both. Remove conflict markers. 2. **Competing changes** — both modify the same logical block. → Evaluate which is correct; upstream usually wins since it's been reviewed. 3. **Dependency chain** — one adds a function the other side calls. → Keep both. **Safe default:** when in doubt about two independent additions, keep both. If they conflict, syntax check catches it: ```bash python3 -c "import ast; ast.parse(open('path/to/file.py').read())" ``` ### Step 5: Stage, commit, push ```bash git add path/to/resolved/file.py git commit -s -m "Merge upstream/main into feat/my-feature Resolved conflict(s) in path/to/file.py: - kept both X and Y changes" git push upstream HEAD ``` ### Step 6: Notify the reviewer Add a PR comment noting which files conflicted and how they were resolved. ## Proactive Avoidance - **Implement foundation PRs first** — get model changes merged before building on them - **No branch stacking** — always branch from main, not from unmerged feature branches - **Merge upstream/main before pushing** on branches older than a day ## When NOT to merge (rebase instead) Before any reviews: rebase for clean linear history. ```bash git fetch upstream main git rebase upstream/main git push --force-with-lease upstream HEAD ``` After first review: **use merge** — force-push destroys the review audit trail. ## Worked Example PR #36 (cookie persistence) conflicted with PR #32 (stealth config) in `browser-svc/app.py`: **Conflict 1 — Constants:** Both PRs added constant definitions to the same area. Resolution: keep both sections. **Conflict 2 — Navigate action:** PR #36 added cookie injection, PR #32 added Cloudflare-aware navigation. Resolution: merged — inject cookies → cloudflare-aware navigate → store cookies. ``` git add browser-svc/browser_svc/app.py git commit -s -m "Merge upstream/main into feat/cookie-persistence Resolved conflicts in browser-svc/browser_svc/app.py: - Kept both cookie persistence constants and stealth config constants - Merged navigate action: cookie injection + Cloudflare-aware nav + cookie storage" ``` -
sleep-cycle-refactor-worked-example.md 4.5 KB
# Worked Example: Cashew Sleep Cycle Refactor A real PR that demonstrates the backward-compat shell-and-pipeline pattern, issued to upstream ([#66](https://github.com/rajkripal/cashew/pull/66)). ## Context **Source:** Upstream thought-graph memory library. **Goal:** Port proven improvements from a fork upstream without breaking existing callers. ## The Pattern Used ### 1. Cross-repo comparison (10 improvements identified) | Improvement | Upstream (old) | Fork (new) | |---|---|---| | Scaling strategy | Full O(N²) every cycle | Work-capped at 2000 nodes | | DB writes | One SELECT+INSERT+commit per pair | Batched executemany, 500/commit | | Architecture | Stateful class with implicit connections | Pure functions with explicit conn | | Vector validation | None — NaN/inf crash sklearn silently | Filter before matrix computation | | Dedup algorithm | Bron-Kerbosch maximal cliques (O(3^(n/3))) | BFS connected components (O(N)) | | Cross-source noise | No filter — links same-source pairs | Skip pairs sharing source_file | | Edge cap | None — unlimited growth | Hard cap at 100K edges/cycle | | Async LLM | Blocks caller for ~60s dream generation | Daemon thread | | Orphan detection | Not checked | Auto-embed missing nodes | | WAL mode | DELETE journal | Explicit WAL enable | ### 2. Shell-and-pipeline architecture ``` SleepProtocol.run_sleep_cycle() # ← old interface (class) │ ├─ conn = self._get_connection() ├─ has_embeddings = check table exists │ ├─ if NOT has_embeddings: │ └─ _run_sleep_cycle_legacy() # ← old code path │ # calls individual class methods │ # (find_cross_link_candidates, cross_link_nodes, ...) │ # text-based Jaccard fallback │ └─ if has_embeddings: └─ run_sleep_cycle() # ← new pipeline (free fn) # Phase 1: _find_pairs() # Phase 2: _batch_cross_links() # Phase 3: _run_dedup() # Phase 4: _compute_metrics() # Phase 5: _garbage_collect() # Phase 6: _evaluate_permanence() # Phase 7: _promote_core_memories() # Phase 8: _generate_dream() # Phase 9: _embed_orphans() ``` ### 3. Dispatch check code pattern ```python # Inside SleepProtocol.run_sleep_cycle(): def run_sleep_cycle(self, model_fn=None, **kwargs): conn = self._get_connection() has_embeddings = conn.execute( "SELECT name FROM sqlite_master " "WHERE type='table' AND name='embeddings'" ).fetchone() is not None conn.close() if not has_embeddings: # Old path: per-method calls, text-based fallback return self._run_sleep_cycle_legacy(model_fn=model_fn) # New path: vectorized pipeline return run_sleep_cycle( db_path=self.db_path, limit=kwargs.get("limit", 2000), model_fn=model_fn, background_dream=kwargs.get("background_dream", False), ... ) ``` ### 4. Preserved all existing tests + added new ones | Test file | Tests | Status | |---|---|---| | `tests/test_sleep.py` | 38 (original) | All pass unchanged | | `tests/test_sleep_refactor.py` | 23 (new) | All pass | | **Total** | **61** | **Green** | ### 5. Documentation delta | Doc | What changed | |---|---| | `CLAUDE.md` | Sleep Cycle section replaced with 9-phase pipeline description | | `DESIGN.md` | §4.5 updated from 5-step to 9-step pipeline | ## Key Implementation Decisions ### Why the legacy fallback exists The tests created temporary databases without an `embeddings` table — and the old code path handled this by catching sklearn exceptions and falling back to text Jaccard. Rather than modifying every test fixture, the legacy fallback preserves the exact old behavior. The new pipeline only activates when the embeddings table is present. ### Why each phase is a free function Each phase is independently testable, can be run in isolation, and has explicit dependency injection (the `conn` parameter). No hidden state. This makes it trivial to: - Test a single phase with a synthetic database - Reorder phases without touching class state - Skip phases when their preconditions aren't met ### Why Bron-Kerbosch was preserved The existing `find_merge_clusters()` method uses Bron-Kerbosch maximal clique enumeration because one test explicitly verifies that a chain A-B-C-D does NOT produce a single 4-node cluster (connected components would merge all of them). The new pipeline uses connected-components BFS for dedup, which is correct for the dedup case (you DO want transitive merging). Both algorithms coexist. -
tracking-upstream-fix-status.md 4.4 KB
# Tracking Upstream Fix Status — Three-State Checklist When tracking a fix or feature you developed, that was merged upstream, but you're unsure whether it has landed in your local installation or a released version yet. Check the three states **in this order** to avoid wasted investigation. ## State 1: Local Installed Code **Check the actual disk copy**, not just `pip show`. ```bash # Quick — is the version number newer than when you submitted the fix? pip show package-name # More precise — inspect the specific function for your fix python3 -c " import inspect from core.embedding_service import get_default_service print(inspect.getsource(get_default_service)) " ``` The version number can be misleading if: - The fix was applied locally during development but not released yet - The package was installed from a git branch or source checkout - You patched the site-packages copy during debugging **Always inspect the code before assuming it's broken.** The fix may already be on your machine even though upstream hasn't tagged a release. ## State 2: Upstream Release Tags The upstream repo's tags tell you which fixes are in which release: ```bash # Clone fresh (or fetch existing) git clone git@github.com:owner/repo.git /tmp/check-repo cd /tmp/check-repo # Find the fix commit git log --oneline --all | grep -i "keyword" # Check if it's in any release tag git tag --contains <commit-sha> ``` If no tag contains the commit, the fix hasn't been released. You need either: - A new upstream release - Pinning to `main` in your dependencies - Applying the patch locally (not recommended for production) ## State 3: Open PRs on Upstream Before doing any original work, check whether someone else (including the maintainer) already opened a PR addressing the same gap: ```bash # Search open PRs by topic gh pr list --repo owner/repo --state open --limit 20 # Search for related terms gh pr list --repo owner/repo --state open --search "busy_timeout" --json title,number,author # If your fix had a scope-gap follow-up (Phase 3.5), the maintainer may # have opened their own PR addressing the remaining sites ``` If an open PR already covers the scope, don't duplicate — instead: - Do a full call-site audit to find what it misses - Comment on the existing PR with findings - Create a complementary PR for the gaps ## Combined Flow ``` ┌──────────────────────┐ │ Start tracking a fix │ └──────────┬───────────┘ │ ┌──────────▼───────────┐ │ Check installed code │──── The fix is here? → ✅ Done │ (inspect source) │ └──────────┬───────────┘ │ Not present ┌──────────▼───────────┐ │ Check upstream tags │──── Tagged? → Upgrade pip / npm / etc. │ (git tag --contains) │ └──────────┬───────────┘ │ Not tagged ┌──────────▼───────────┐ │ Check open PRs │──── PR exists? → Audit + complement │ (gh pr list --search) │ └──────────┬───────────┘ │ No PR ┌──────────▼───────────┐ │ File issue + │ │ create PR ourselves │ └──────────────────────┘ ``` ## Why This Order Matters - **Local first** — the fastest resolution is "it's already here." Checking the disk avoids an upstream investigation that may be irrelevant. - **Tags before PRs** — if the fix is already released, you don't need to open or search for PRs at all. - **PRs before issue-filing** — if someone else already did the work, contributing to their PR is better than creating a competing one.
-
-
scripts
-
check-pr-template-compliance.py 3.1 KB
#!/usr/bin/env python3 """ PR Template Compliance Checker Validates that a PR body matches the structure of the project's PR template. Exits 0 (compliant) or 1 (non-compliant) with a reason. Usage: python3 check-pr-template-compliance.py /tmp/pr-body.md /tmp/pr-template.md The checker verifies: - Every section header from the template appears in the body - Required sections are not empty - Checkboxes are present when the template includes them """ import re import sys def extract_section_headers(text: str) -> list[str]: """Extract markdown section headers (## or ###) from text.""" headers = re.findall(r'^(#{2,3})\s+(.+)$', text, re.MULTILINE) return [h[1].strip() for h in headers] def extract_checkboxes(text: str) -> list[str]: """Extract checkbox items from text.""" return re.findall(r'-\s*\[\s*[ x]?\s*\]\s*(.+)', text) def has_content(text: str, header: str) -> bool: """Check if a section has substantive content (not just N/A or empty).""" pattern = re.compile(rf'^#{{2,3}}\s+{re.escape(header)}\s*$', re.MULTILINE) match = pattern.search(text) if not match: return False # Get content after this header until the next header or end start = match.end() remainder = text[start:].lstrip('\n') # Find next header next_header = re.search(r'^#{2,3}\s+', remainder, re.MULTILINE) section_text = remainder[:next_header.start()] if next_header else remainder section_text = section_text.strip() # Empty or only N/A/not applicable if not section_text: return False if section_text.lower() in ('n/a', 'na', 'not applicable', 'none'): return False return True def main(): if len(sys.argv) < 3: print("Usage: check-pr-template-compliance.py <pr-body.md> <pr-template.md>") sys.exit(1) body_path = sys.argv[1] template_path = sys.argv[2] try: with open(body_path) as f: body = f.read() except FileNotFoundError: print(f"FAIL: PR body file not found: {body_path}") sys.exit(1) try: with open(template_path) as f: template = f.read() except FileNotFoundError: print(f"SKIP: No template found at {template_path} — skipping compliance check") sys.exit(0) template_headers = extract_section_headers(template) body_headers = extract_section_headers(body) missing = [h for h in template_headers if h not in body_headers] empty_sections = [h for h in template_headers if h in body_headers and not has_content(body, h)] if missing: print(f"FAIL: Missing required template sections: {', '.join(missing)}") sys.exit(1) if empty_sections: print(f"WARN: Empty or N/A sections: {', '.join(empty_sections)}") # Check checkboxes template_checkboxes = extract_checkboxes(template) body_checkboxes = extract_checkboxes(body) missing_boxes = [c for c in template_checkboxes if c not in body_checkboxes] if missing_boxes: print(f"WARN: Missing checkboxes: {', '.join(missing_boxes)}") print("PASS: PR body complies with template") sys.exit(0) if __name__ == '__main__': main()
-
-
README.md 1.5 KB
# Open Source Contributions — Be a Good Citizen Make good open source contributions — check CONTRIBUTING.md first, follow project norms, be a good citizen. Covers bug reports, feature requests, and pull requests. ## Why Install This Skill When your agent loads this skill, it becomes a **model open source contributor** who knows how to: - **Read the room** — check CONTRIBUTING.md, check existing issues/PRs before acting - **File great bug reports** — minimal reproduction cases, clear steps, expected vs actual behavior - **Make good PRs** — right-sized scope, clean commits, proper messaging - **Handle review gracefully** — respond to feedback, update PRs, know when to follow up - **Work without guidelines** — defensible defaults when the project hasn't documented expectations - **Disclose AI assistance** — proper agent disclosure on AI-assisted contributions ## What You Get | Directory | Purpose | |-----------|---------| | `SKILL.md` | Golden rule, phase overview, default posture, agent checklist | | `references/` | 8 phase references + agent checklist + pitfalls + default posture (comprehensive guidance for every stage of contribution) | ## Triggers Load this when filing an issue, opening a PR, or contributing to any open source project. ## Requirements None. Works with any agent supporting the Agent Skills format. ## Quick Start Start with the setup and first workflow in SKILL.md, then use the linked resources for the specific task you need to complete. -
SKILL.md 5.6 KB
--- name: opensource-contributions description: >- Make good open source contributions — check CONTRIBUTING.md first, follow project norms, be a good citizen. Covers bug reports, feature requests, and pull requests with a defensible default posture when the project hasn't documented expectations. Do not use this skill for unrelated requests; route to the nearest named specialist. license: MIT compatibility: Compatible with any agent supporting the Agent Skills format (Hermes Agent, Claude Code, GitHub Copilot, OpenCode, Cursor, etc.) metadata: tags: opensource, contributing, github, etiquette, PRs, issues related_skills: github-issues, github-pr-workflow, github-code-review version: 1.6.0 author: agent-skills source_repo: https://agentskills.io --- # Open Source Contributions ## The Golden Rule **Make it easy for maintainers to absorb your contribution.** Maintainers are often under-resourced volunteers or small teams. Every friction point you remove — unclear reproduction steps, missing tests, bad commit messages, force-pushed history — is time they don't have to spend figuring out what you did and why. The whole point is to *help* them, not create more work. ## When to Use This Skill - Filing a bug report or feature request on a public repository - Preparing a pull request for any open source project - Working on a project without documented contributing guidelines (default posture) - Reviewing your own PR before submission - Setting up open source conventions for your own project **Load the relevant phase reference for detailed instructions.** --- ## Quick Phase Overview | Phase | What It Covers | Reference | |-------|----------------|-----------| | **0a — Maintainer Conventions** | README discipline, LICENSE, CONTRIBUTING.md, issue/PR templates, commit conventions, DCO, release workflow hygiene | `references/phase-0a-maintainer-conventions.md` | | **0 — Before You Start** | Reading CONTRIBUTING.md, checking existing issues/PRs, triaging bugfix candidates, large-change discussion etiquette | `references/phase-0-before-you-start.md` | | **1 — Filing Issues** | Bug report and feature request templates, agent disclosure, maintainer workflow ("issue first"), coordinated multi-issue roadmaps, multi-PR plan execution | `references/phase-1-filing-issues.md` | | **2 — Pull Requests** | Branching conventions, scope assessment, studying existing implementations, cross-repo comparison, commit messages, PR templates, documentation audits, CI setup | `references/phase-2-pull-requests.md` | | **3 — After Submitting** | CI monitoring, responding to review feedback, what to do if your PR goes stale or gets closed | `references/phase-3-after-submitting.md` | | **3.5 — Follow-up After Scope Feedback** | Systematic call-site audits, filing comprehensive issues, complementary PRs when maintainer scope notes identify gaps | `references/phase-35-followup.md` | | **4 — Release Process** | Version bumping, tagging, GitHub Releases vs tags, release workflow anatomy, handling failed releases | `references/phase-4-release-process.md` | ### Default Posture (No CONTRIBUTING.md) When a project has no contributing guide, load `references/default-posture.md` for defensible defaults on issue filing, PRs, communication norms, and code of conduct. ### Agent-Specific Checklist If you are an AI agent filing or contributing on behalf of a human, load `references/agent-checklist.md` before submitting anything. ### Pitfalls Load `references/pitfalls.md` when you're about to submit an issue or PR, or when something goes wrong. Covers: backtick expansion, silent label failures, force-push etiquette, the "I'll just fix it quickly" trap, cross-fork PR issues, post-merge scope creep, the installed-code trap, CI debugging, and more. --- ## The Agent-Specific Rule When filing an issue on behalf of a human, **always disclose the agentic nature.** Add this line at the bottom of the issue body: ``` Filed by {{AGENT_NAME}} (AI agent on behalf of {{HUMAN_NAME}}) ``` This is a transparency requirement, not a courtesy. Maintainers deserve to know who they're talking to. --- ## Quick Reference Card | Step | Action | Command / Check | |------|--------|-----------------| | 0 | Read contributing guide | `cat CONTRIBUTING.md` or `.github/CONTRIBUTING.md` | | 0 | Check existing issues | `gh issue list --search "topic" --state all` | | 1 | File a bug/feature | Use template if provided; include reproduction for bugs | | 2 | Issue first before coding | File issue, wait for maintainer feedback, then branch | | 3 | Branch | `git checkout -b fix/description` | | 4 | Commit | `git commit -s -m "fix: description"` | | 5 | Run tests locally | `make test` or `npm test` or `pytest` | | 6 | Push | `git push -u origin HEAD` | | 7 | Open PR | Fill out PR template completely. Use `--body-file` for complex bodies | | 8 | Monitor CI | `gh pr checks --watch` | | 9 | Address review | Respond to comments, push fixup commits. No force-push after review | | 10 | After merge | `git checkout main && git pull && git tag vX.Y.Z && git push origin main --tags` | | 11 | Create Release | `gh release create vX.Y.Z --title "vX.Y.Z — Title" --notes-file /tmp/notes.md` | --- ## When NOT to Use This Skill - You're the sole maintainer of a project with no external contributors and no public collaborators - You're making a trivial single-line fix to your own code - The contribution is internal (same organization, same team) with established workflow norms - You already know the project's contributing guidelines by heart and this is routine - The issue is a security vulnerability — follow the project's security disclosure policy instead
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.