src/osprey/templates/skills/osprey-contribute/SKILL.md
Guides a contributor through the OSPREY GitHub Flow contribution journey from a working-tree change to a merged PR on main. Use when someone says "I want to contribute X to osprey", "help me commit/push this", "prep this branch for a PR", "open a PR", "my CI is failing on this branch", "rebase onto main", or wants help following the contributing workflow. Auto-detects whether they have push access to als-apg/osprey or are contributing from a fork. Composes with the osprey-pre-commit, commit-organize, and osprey-release skills — invoke this whenever someone is contributing code to OSPREY, even if they haven't named the workflow explicitly.
npx skillsauth add als-apg/osprey osprey-contributeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill walks a contributor from "I have a change" to "merged on main." It
is the connective tissue of OSPREY's GitHub Flow — branch off main, work,
validate, PR, merge — wrapping the existing scripts and skills you already
have so the contributor doesn't have to remember every gate.
The skill enters at whichever phase fits the contributor's current state. Run Phase 0: Orient first; the orient step decides where to pick up.
| For | Use |
| --- | --- |
| Splitting a messy working tree into clean, atomic commits | commit-organize |
| Just running pre-commit checks without the full journey | osprey-pre-commit |
| Cutting a release / tagging a version | osprey-release |
| Designing a new feature or capability | feature-dev / brainstorming |
OSPREY uses GitHub Flow with a single long-lived main branch. There are two
modes; detect which one applies on first run by inspecting git remote -v:
origin points to als-apg/osprey. Contributor has push
access. Flow: branch off main → push branch to origin → PR → self-merge after green CI.origin is a personal fork; another remote (typically
upstream) points to als-apg/osprey. Flow: branch off latest upstream/main → push branch to origin (the fork) → PR upstream → maintainer merges.If neither remote points to als-apg/osprey, ask the contributor. If origin
appears to be a fork but no upstream is configured, offer:
git remote add upstream https://github.com/als-apg/osprey.git
Mode detection is cheap; just re-detect each session rather than persisting.
Before changing anything, gather the lay of the land. Run these in parallel:
git status --short
git rev-parse --abbrev-ref HEAD
git remote -v
git fetch origin main --quiet && \
git rev-list --left-right --count HEAD...origin/main
Decide where to enter:
| Observed state | Enter at |
| --- | --- |
| Uncommitted changes on main | Phase 1 (branch first, then commit) |
| Uncommitted changes on a topic branch | Phase 3 (commit) |
| Clean tree, topic branch, commits not pushed | Phase 4 (push) |
| Branch pushed, no PR yet | Phase 5 (open PR) |
| PR open with failing or pending CI | Phase 6 (watch and iterate) |
| PR green, internal mode | Phase 7 (merge) |
| Detached HEAD, mid-rebase, or conflicts | Resolve before proceeding |
Tell the contributor what you observed and which phase you're entering.
Hard block: refuse to commit while on
main. Branch protection rejects direct pushes anyway, but failing locally saves the round-trip and the "rejected" error after the contributor has already typed the commit message.
Sync local main against the canonical remote:
# internal mode
git checkout main && git pull --ff-only origin main
# fork mode
git fetch upstream && git checkout main && git merge --ff-only upstream/main
Suggest a branch name from the change description and validate against the
prefix convention from CONTRIBUTING.md:
feature/<short-kebab> — new functionalityfix/<short-kebab> — bug fixesdocs/<short-kebab> — documentation onlyrefactor/<short-kebab> — internal restructuringtest/<short-kebab> — tests onlyIf the proposed name doesn't match a prefix, warn and suggest an alternative. Accept the contributor's choice on insist — these are conventions, not invariants.
Create the branch:
git checkout -b <name>
If pre-commit hooks aren't installed in this clone, offer to install them once. Idempotent and safe on every fresh clone:
pre-commit install
The contributor edits code; the skill is mostly absent. Two reminders:
CHANGELOG.md as you go, not at the end. Add a bullet under the
relevant heading (### Added, ### Changed, ### Fixed) in the
## [Unreleased] section. Doing this concurrently keeps it accurate and
turns the per-commit CHANGELOG check into a non-event.commit-organize to split it before committing.Hard block: run
./scripts/quick_check.sh(~30 s) before the commit. Catches formatting drift and broken imports before they enter history. Fixing a polluted commit later is much more expensive than failing fast.
Show git status and git diff --stat so the contributor sees exactly
what's about to land.
Stage surgically — list specific paths rather than git add -A. Blanket
adds occasionally sweep up .env, scratch files, or large binaries.
Run ./scripts/quick_check.sh. On failure, surface the output, suggest the
minimal fix, re-run. Don't commit until clean.
Compose a conventional commit message:
type(scope): summary
Optional body. Explain *why*, not *what* — the diff already shows what.
Where type ∈ {feat, fix, docs, refactor, test, chore, ci, build, perf}.
Subject ≤ 70 chars, imperative mood ("add", not "added").
Soft prompt: if the contributor's preferred message doesn't match the conventional form, propose a rewrite once. Accept their version on insist.
Soft prompt — CHANGELOG: if CHANGELOG.md isn't in the staged set, ask
whether this commit needs an entry. Some genuinely don't (pure ruff
renames, internal refactors invisible to users). Don't block.
Commit, then git log -1 --stat so the contributor sees what was recorded.
Hard block: run
./scripts/ci_check.sh(~2-3 min) before the push. CI minutes are shared and PR dashboards get noisy when obviously-broken branches sit waiting for triage. Pushing only what would have passed CI respects everyone's time.
Confirm the push target. Topic branches always go to the contributor's
personal remote, never to upstream:
origin (which is als-apg/osprey).origin (which is the contributor's fork).Run ./scripts/ci_check.sh. On failure, surface output, suggest the
minimal fix, ask the contributor to amend or add a follow-up commit.
Push with upstream tracking the first time. Confirm before — this is the first visible-to-others state-change in the journey:
git push -u origin <branch>
Hard block: run
./scripts/premerge_check.sh mainbeforegh pr create. Catches drift frommainthat surfaces only at merge time. Cheaper to find now than after CI burns minutes on the PR.
Compose the PR title and body:
## Summary — 1-3 bullets, why this change.## Changes — what landed.## Test plan — bulleted checklist of how this was validated.## Related issues — if any.Show the contributor the draft. They can edit before it goes out.
Open against als-apg/osprey:main:
# internal mode
gh pr create --base main --head <branch>
# fork mode (gh handles the cross-repo head spec)
gh pr create --repo als-apg/osprey
Print the PR URL and remind the contributor that CI will start running.
gh pr checks --watch
When checks complete:
All green → Phase 7.
Failures — fetch the failed run's logs, summarize the root cause, suggest
a minimal fix. The contributor edits, re-stage, re-commit (or git commit --amend if the broken commit is the tip and not yet pulled by anyone else).
Push again. Loop.
Stale checks because main moved — rebase:
git fetch origin main && git rebase origin/main
git push --force-with-lease
Always --force-with-lease, never plain --force — the lease prevents
clobbering work someone else might have pushed to your branch in the
meantime. Walk through any conflicts manually; do not force-push past a
conflict the contributor hasn't reviewed.
pre-commit.ci runs as a required check and may auto-fix style by pushing a
commit to your branch. If you see "pre-commit.ci - autofix" land, just
git pull --rebase before the next push.
Internal mode — when CI is green:
gh pr merge --rebase --delete-branch
--rebase because branch protection requires linear history; --merge would
be rejected. --delete-branch because long-lived branches aren't the model.
Fork mode — comment on the PR and ping a maintainer; merging is theirs to
do. The maintainer will use the same --rebase --delete-branch from their
side.
After merge, sync local:
git checkout main && git pull --ff-only origin main
The journey ends here. The next change starts again at Phase 0.
Before any state-changing operation — git commit, git push, gh pr create,
gh pr merge — show the contributor exactly what's about to happen and ask
them to proceed. Reads (status, diff, fetch, log) don't need
confirmation.
The point is: every visible-to-others action has a deliberate moment where the contributor sees what's about to ship and can stop it.
A few facts about OSPREY's main branch protection that shape behaviour here:
enforce_admins is on. If a required check is genuinely wrong (a
flaky runner, a broken matrix), fix it forward — don't try to bypass.--rebase
and rebases use --force-with-lease.main is rejected. This skill never tries; the hard
block in Phase 1 mirrors the server-side rule so the contributor fails
locally instead of after typing a commit message.required_approving_review_count: 0),
so internal-mode contributors can self-merge once CI is green. Fork-mode
contributors still need a maintainer to merge.If the contributor hits an unexpected branch-protection error, surface the GitHub message verbatim; the skill should not pretend the rules are looser than they are.
development
Interactive interview to create a custom OSPREY build profile for a new accelerator, detector, or beamline application. Use when someone says "interview me", "create a build profile", "set up my agent", "configure my detector", "onboard me", or needs to create an OSPREY project tailored to their specific control system. Also handles migration from existing OSPREY projects (including LangGraph-era projects) — trigger on "migrate my project", "I have an existing project", "upgrade from old OSPREY", "upgrade from langgraph", "legacy migration", "bring my project forward", "convert my project", "extract profile from existing project", "reverse-engineer build profile". Also use for /osprey-build-interview feedback to collect post-use feedback. Also trigger when onboarding a new colleague or when anyone needs help figuring out what their OSPREY agent should look like.
testing
Guides a maintainer through cutting an OSPREY release on the GitHub Flow workflow: open a version-bump PR, merge it to main, tag the merge commit, push the tag, verify the automated PyPI publish. Use when someone says "create a release", "bump the version", "cut v2026.X.Y", "publish to PyPI", "tag a release", or asks about the release process. Composes with `osprey-contribute` for the bump PR. Versions follow CalVer (vYYYY.M.P) and the source of truth is `src/osprey/__init__.py` — Hatch derives the pyproject.toml version dynamically.
development
Validates code before committing using OSPREY's three-tier check scripts. Runs linting, formatting, and tests to catch issues early. Use when ready to commit, before pushing, before opening a PR, or when the user asks to run checks, validate, or verify their changes. For the full contribution journey (branching, commits, push, PR, merge), use `osprey-contribute` instead — this skill is the focused validation step.
tools
Deployment control plane for an OSPREY-based facility profile repository. Owns the deploy lifecycle end-to-end: scaffolds the deploy infrastructure (docker-compose, .gitlab-ci.yml, scripts/deploy.sh, .env.template) from facility-config.yml, drives the GitLab CI/CD container pipeline (push → CI → manual release tag → server deploy), brings containers up on the deploy server with `scripts/deploy.sh`, and runs post-deploy health checks. Modular opt-in support for OLOG/logbook integration, multi-user web terminals, Ollama embeddings, shared-disk mounts, EPICS-driven event dispatcher, ARIEL database, wiki search, custom MCP servers, e2e benchmarks, and EPICS test IOC management. Use this skill whenever the user is asking about deploying, releasing, pushing to GitLab, building containers, modifying docker-compose files, scripts/deploy.sh, .gitlab-ci.yml, the CI pipeline, the container registry, the deploy server, the .env, the facility-config.yml, webhook triggers, the event dispatcher, web terminals, OLOG, Ollama, ARIEL, integration tests, test IOC, OSPREY:TEST PVs, agent benchmarks, or anything CI/CD or on-server-deploy related — even if they don't explicitly say "deploy". Mandatory entry point for any work touching the project's deploy pipeline; if the user says "let's set this up", "ship this", "release", "promote to prod", "rebuild on the server", or anything similar, invoke this skill before doing anything else. On first invocation, runs an interactive deploy interview to capture facility-specific values (GitLab host, deploy server, container runtime, proxy, ports, optional modules) and writes them to `facility-config.yml` at the repo root; subsequent invocations read that config. Does NOT author build profile YAMLs — that is the job of the separate `osprey-build-interview` skill (`/osprey-build-interview`). If the user wants to create or edit a profile, hand off to `/osprey-build-interview` and stop.