Skip to content
Merged
3 changes: 2 additions & 1 deletion TELEMETRY.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ conversation, use `app.dispatch.id` or `agent-dispatch:<dispatch_id>` as
A turn parked for auth, resumed late, or failed after callback.

Events: `sandbox.egress.credential.needed`,
`sandbox.egress.credential.unavailable`, `plugin.credential.rejected`,
`sandbox.egress.credential.unavailable`, `sandbox.egress.upstream_auth.rejected`,
`sandbox.egress.upstream_auth.retrying`, `plugin.credential.rejected`,
`subscribed_message.authorization.required`, `agent.continue.schedule.failed`,
`agent.continue.lock.busy`, `agent.continue.lock.retrying`,
`oauth.callback.resume.completed`, `oauth.callback.resume.busy`,
Expand Down
14 changes: 7 additions & 7 deletions packages/junior-github/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ githubPlugin({

Installation-read token requests remain read-only by requesting read-capable configured permissions at `read` level and omitting GitHub permission fields that have no `read` value. Installation-write token requests intentionally omit the `permissions` field, so GitHub applies the complete permission envelope approved on the App installation. GitHub remains the source of truth for whether a permission name or level exists.

GitHub App user-to-server tokens do not use OAuth scopes as their permission model. Their effective access is limited by the GitHub App's installed permissions, the app installation's repository access, and the requesting user's own GitHub access. Repository-scoped installation tokens instead use the App permission envelope and installation repository access without borrowing the requesting user's authority. GitHub returns an empty `scope` value for user-to-server tokens, so Junior cannot verify granted scopes from the token response.
GitHub App user-to-server tokens do not use OAuth scopes as their permission model. Their effective access is limited by the GitHub App's installed permissions, the app installation's repository access, and the requesting user's own GitHub access. Installation tokens use the App permissions and installation repository access without borrowing the requesting user's authority. GitHub returns an empty `scope` value for user-to-server tokens, so Junior cannot verify granted scopes from the token response.

If you pass `additionalUserScopes`, Junior includes those values in the authorization URL and records the requested scope string as a local reauthorization contract. This does not expand or prove GitHub API permissions. Configure provider-enforced access in the GitHub App settings; `appPermissions` only controls read-token downscoping:

Expand All @@ -148,16 +148,16 @@ githubPlugin({
});
```

Use `additionalUserScopes` only when a human-identity integration flow requires specific GitHub OAuth scope parameters in the authorization URL. Do not rely on it to authorize Junior-owned repository or workflow writes — those use repository-scoped installation tokens and the permissions approved on the GitHub App installation.
Use `additionalUserScopes` only when a human-identity integration flow requires specific GitHub OAuth scope parameters in the authorization URL. Do not rely on it to authorize Junior-owned repository or workflow writes — those use installation tokens and the permissions approved on the GitHub App installation.

## 3) Runtime behavior

- When either GitHub skill is active, authenticated `gh` and `git` commands cause the runtime to inject GitHub credentials automatically for the current turn.
- The plugin classifies GitHub traffic from the forwarded HTTP request. Reads use `installation-read`, while `GET /user` uses `user-read`. Allowlisted App-owned mutations and Git smart-HTTP pushes use repository-scoped `installation-write`. User-attachment uploads to `uploads.github.com/user-attachments/assets` use `user-write`. Unknown REST writes and GraphQL mutations are denied.
- The plugin classifies GitHub traffic from the forwarded HTTP request. Reads use `installation-read`, while `GET /user` uses `user-read`. Allowlisted App-owned mutations and Git smart-HTTP pushes use `installation-write`. User-attachment uploads to `uploads.github.com/user-attachments/assets` use `user-write`. Unknown REST writes and GraphQL mutations are denied.
- `user-read` and explicitly human `user-write` operations require the actor, or an explicitly delegated user subject, to authorize the GitHub App through the private OAuth flow. Junior-owned issue, pull request, review, inline review comment, and branch operations do not fall back to user OAuth.
- Headless resource-event turns use the `resource-event` system actor and may receive the same repository-scoped installation grants. This lets Junior respond to subscribed pull request events by committing and pushing fixes without inheriting a subscriber's OAuth credential.
- Headless resource-event turns use the `resource-event` system actor and may receive the same installation grants. This lets Junior respond to subscribed pull request events by committing and pushing fixes without inheriting a subscriber's OAuth credential.
- Git commits use Junior as author and committer. Resolvable human run actors are credited once with `Co-Authored-By` trailers.
- Issued credentials are reused only within the current turn, credential leases are cached by plugin grant and repository lease scope, and upstream 403 permission denials clear the cached lease before the next retry.
- Installation credential leases are cached on the host by grant name and reused across sandboxes until near expiry. User grants stay actor-scoped. Upstream 403 after injection clears the cached lease, issues a new token, and retries the hop once before recording permission denied.
- Sandbox does not receive raw tokens via env; host applies Authorization header transforms for GitHub API and upload calls.

## 4) CLI usage
Expand Down Expand Up @@ -188,14 +188,14 @@ The plugin uses installation credentials for read-only GitHub traffic, workflow
Committing and pushing code uses more than one GitHub surface:

- Creating the local Git commit does not call GitHub. Junior sets the GitHub App bot as author and committer and credits resolvable human actors with `Co-Authored-By` trailers.
- Pushing a branch with Git smart HTTP (`git push`) uses the repository-scoped `installation-write` grant and requires the App installation to have `Contents: write`. Workflow-file changes also require the installation to have `Workflows: write`.
- Pushing a branch with Git smart HTTP (`git push`) uses the `installation-write` grant and requires the App installation to have `Contents: write`. Workflow-file changes also require the installation to have `Workflows: write`.
- The smart-HTTP classifier does not distinguish Junior-managed branches or independently detect force updates or ref deletion. Use GitHub branch protection and limit the App installation to repositories where Junior may push.
- REST Git database and ref writes are denied by the current write allowlist. Use Git smart HTTP (`git push`) for branch updates instead.
- Opening the PR after the branch exists is separate: `github_createPullRequest` needs pull-request write permission, but it should not create or push commits itself.

Fork creation is not part of the default PR path and is denied by the current write allowlist. Do not grant `Administration: write` for routine PR creation; push a branch explicitly and create the PR with `github_createPullRequest` instead.

Repository scoping and the egress allowlist are the write boundaries. Credential injection is provider-domain scoped for sandbox traffic to `api.github.com` and `github.com` during turns with a signed credential context. Keep repo context explicit, and let the plugin choose the grant for the outbound request.
The egress allowlist and App installation repositories are the write boundaries. Credential injection is provider-domain scoped for sandbox traffic to `api.github.com` and `github.com` during turns with a signed credential context. Keep repo context explicit, and let the plugin choose the grant for the outbound request.

Be careful with mixed-surface PR commands. Use the allowlisted REST endpoints
rather than GraphQL-backed `gh pr` mutation commands. PR-native title, body,
Expand Down
94 changes: 24 additions & 70 deletions packages/junior-github/skills/github-code/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,34 @@ description: Work with GitHub repositories, source code, branches, commits, pull

# GitHub Code Operations

Use `git` and `gh` for repository work. Use `github_createPullRequest`, not `gh pr create`, for new PRs. Use `github_updatePullRequest`, not raw `gh api`/`gh pr edit`, when changing PR title, body, base, or open/closed state. Use `github_resolvePullRequestReviewThread`, not raw `gh api graphql` `resolveReviewThread`, when resolving review threads on Junior-authored PRs.
Use `git` and `gh` for repository work.

## References
| Action | Tool / command |
| --- | --- |
| Create PR | `github_createPullRequest` (not `gh pr create`) |
| Update PR title/body/base/state | `github_updatePullRequest` (not raw PATCH / `gh pr edit`) |
| Resolve review thread | `github_resolvePullRequestReviewThread` (not raw GraphQL) |
| Clone missing repo | `github_cloneRepository`; on Workspace match error use `switchWorkspace` |

| Open when you need | Read |
| -------------------------------------- | -------------------------------------------------------------------------------------- |
| Command syntax, permissions, config | [references/api-surface.md](references/api-surface.md) |
| Failed commands or permission recovery | [references/troubleshooting-workarounds.md](references/troubleshooting-workarounds.md) |
## Open when needed

## Non-negotiable rules
| Need | Read |
| --- | --- |
| Commands, permissions, allowlist | [references/api-surface.md](references/api-surface.md) |
| Edit → verify → PR packaging | [references/workflow.md](references/workflow.md) |
| Failed command or permission recovery | [references/troubleshooting-workarounds.md](references/troubleshooting-workarounds.md) |

- Resolve the repo from the explicit request, then `github.repo`. Run `jr-rpc config get github.repo` standalone.
## Always

- Resolve repo from the request, then `github.repo`. Run `jr-rpc config get github.repo` standalone.
- Keep `--repo owner/repo` explicit on `gh`; use `git -C PATH` for local repos.
- Read applicable `AGENTS.md` files before editing. Narrower repo/task instructions win.
- Preserve unrelated work. Never force-push, delete refs, or perform destructive merges.
- Read applicable `AGENTS.md` before editing. Narrower repo/task instructions win.
- Preserve unrelated work. Never force-push, delete refs, or do destructive merges.
- Base conclusions on repository evidence. Do not claim a check ran unless it did.
- For Junior-owned pull requests, push the branch before creating the PR. The runtime supplies repository-scoped GitHub App credentials for both; try the operations before requesting remediation and never ask for a user token.
- Use `github_cloneRepository` instead of shelling out to `git clone` when a repository is not already available in the sandbox.
- If `github_cloneRepository` returns a tool input error about matching Workspaces, call `switchWorkspace`. The checkout is already present after a successful switch. Pass `allowAdHoc=true` only for an intentional ad-hoc checkout.
- A tool-routing denial requires the named tool; only an upstream denial justifies permission remediation.
- Stop for ambiguous targets, missing access, destructive operations, or unresolved upstream permission failures.

## Workflow

### 1. Resolve and inspect

Identify the repo, checkout, default/current branches, worktree state, repo instructions, package manager, and relevant checks. Prefer an existing checkout or matching Workspace; otherwise clone shallowly. If clone returns a Workspace tool input error, switch Workspace instead of cloning again, or pass `allowAdHoc=true`.

A shallow clone is for fast inspection, not history rewriting. Before rebasing, merge-base analysis, blame/history work, or comparing against a base absent locally, fetch the needed refs and deepen incrementally. Use `--unshallow` only when bounded deepening is insufficient. Never use a force push to compensate for incomplete history.

For edits, choose the smallest credible validation path before changing files. Capture a baseline when a failure may be pre-existing.

### 2. Investigate

Establish where the behavior lives, current versus requested behavior, root cause or gap, and the smallest proof of correctness. Read linked issues, PRs, specs, and failing output when provided. For pull requests, inspect conversation comments, inline review comments, reviews, the diff, and checks. If the request is investigation-only, report evidence without editing.

### 3. Edit

Make the smallest coherent change. Follow local patterns and avoid speculative cleanup. After a failed attempt, re-check the root cause before patching again.

Before running repo checks, ensure project dependencies are available:

1. Detect the package manager and lockfile from repo evidence.
2. If dependencies are missing or the check reports missing packages, run the repo-native frozen/immutable install (`pnpm install --frozen-lockfile`, `npm ci`, `yarn install --immutable`, `bun install --frozen-lockfile`, or the documented equivalent).
3. Do not regenerate or modify a lockfile merely to make verification run. If the locked install fails, report the exact failure unless dependency changes are part of the task.

Do not install or repair the GitHub plugin runtime itself; that is manifest-owned setup.

### 4. Verify and review

Run targeted changed-file/package checks before broad suites. Separate regressions from baseline failures. For instruction-only changes, run available structural checks and perform a content-consistency review.

### 5. Package every completed edit

Unless the user explicitly says not to create a PR, every completed repository edit must end in a pushed branch and PR. Default to draft; honor an explicit user or repo instruction to open it ready for review. Do not stop at local changes or a commit.

1. Reuse the current non-default branch or create a focused branch.
2. Commit using repo conventions; otherwise use `<type>(<scope>): <Subject>` in imperative present tense, with no agent branding.
3. Push explicitly with `git push -u origin BRANCH`.
4. Resolve the actual default branch.
5. Reuse and update an existing PR for the branch with `github_updatePullRequest`; otherwise call `github_createPullRequest` with explicit repo, head, base, title, body, and `draft: true` unless the user or repo explicitly requires ready-for-review.

PR titles use the same conventional form as commits: `<type>(<scope>): <Subject>` or `<type>: <Subject>`. Match the current dominant change, not the latest commit or a stale title.

Write the PR body for a reviewer who knows the product but not this change. Use ASD-STE100 English: short sentences, common words, active voice, and one idea per sentence. Avoid dense academic prose and unnecessary jargon.

Explain what this PR changes and why it matters. Add only context the diff cannot show. Keep the body short by default; add structure only when it helps. Omit empty or `N/A` sections, file-by-file narration, copied commit logs, and redundant diff summaries. Do not put `Checks`, `Verification`, `Test plan`, or similar validation sections in the PR body; put local check results only in the final user report.

Treat the current title, body, and commit messages as fallible context. After material follow-up commits, re-check the title and rewrite the body against the current diff with `github_updatePullRequest`. Never include customer data, PII, secrets, or sensitive thread context, especially in public repositories. Resolve requested assignee/reviewer handles from evidence; skip unconfirmed identities.

If PR creation or update is blocked, report the exact failed command/tool call and leave the committed branch intact.

### 6. Follow and report

When PR creation returns a subscribable resource hint, subscribe to suggested review/CI events. Report only actionable feedback addressed, build failures fixed, fully green/ready state, or merge.
- Push the branch before creating a bot-owned PR. Runtime injects installation credentials; never ask for a user token for bot pushes.
- Tool-routing denials need the named tool. Only upstream denials justify permission remediation.
- Stop for ambiguous targets, missing access, destructive ops, or unresolved upstream permission failures.
- Unless the user opts out, finish completed edits with a pushed branch and PR (draft by default).
- Report to the user: repo, branch, PR URL/number, local check results, and anything not run.

Return to the user (not the PR body): repo, branch, PR URL/number, local check results, pre-existing failures, and anything not run with the reason.
Do not install or repair the GitHub plugin runtime from this skill. The plugin manifest owns that.
Loading
Loading