@sentry/junior-github 0.102.2 → 0.103.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SETUP.md CHANGED
@@ -64,7 +64,7 @@ vercel env update GITHUB_APP_PRIVATE_KEY production --sensitive < ./github-app-p
64
64
 
65
65
  Repeat for `preview` and `development` as needed. After env changes, redeploy so the new deployment picks up updated values.
66
66
 
67
- ### Optional permission overrides
67
+ ### Optional permission declaration
68
68
 
69
69
  By default, `installation-read` grants read the app installation's current permission envelope once per process and scopes read-capable permissions down to `read` when requesting a token. To declare the GitHub App permission envelope in the plugin and avoid that installation lookup, pass `appPermissions` when registering the plugin:
70
70
 
@@ -81,11 +81,11 @@ githubPlugin({
81
81
  });
82
82
  ```
83
83
 
84
- Junior records these permissions as plugin capabilities. The configured values are the maximum GitHub App envelope Junior may need for writes. 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. GitHub remains the source of truth for whether a permission name or level exists.
84
+ Junior records these permissions as plugin capabilities. 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.
85
85
 
86
86
  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.
87
87
 
88
- 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 GitHub App permissions with `appPermissions` and in the GitHub App settings for provider-enforced access:
88
+ 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 declares capabilities and controls read-token downscoping:
89
89
 
90
90
  ```ts
91
91
  githubPlugin({
@@ -93,7 +93,7 @@ githubPlugin({
93
93
  });
94
94
  ```
95
95
 
96
- 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 configured GitHub App permissions.
96
+ 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.
97
97
 
98
98
  ## 3) Runtime behavior
99
99
 
@@ -128,12 +128,12 @@ new resources through the typed tools, for example
128
128
  Use `gh` for the allowlisted lifecycle operations described by the skill.
129
129
 
130
130
  `gh` supports either direct `GITHUB_TOKEN` (for local debugging) or sandbox-level header injection.
131
- The plugin uses installation credentials for read-only GitHub traffic, workflow dispatches, allowlisted issue and pull request mutations, and Git smart-HTTP pushes. GitHub App permissions still need to cover the operation: actions for workflow dispatches, issues for issue edits/comments/labels, contents for pushes, pull requests for PR mutations, and workflows for workflow-file pushes.
131
+ The plugin uses installation credentials for read-only GitHub traffic, workflow dispatches, allowlisted issue and pull request mutations, and Git smart-HTTP pushes. The installed GitHub App permissions still need to cover the operation: actions for workflow dispatches, issues for issue edits/comments/labels, contents for pushes, pull requests for PR mutations, and workflows for workflow-file pushes.
132
132
 
133
133
  Committing and pushing code uses more than one GitHub surface:
134
134
 
135
135
  - 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.
136
- - Pushing a branch with Git smart HTTP (`git push`) uses the repository-scoped `installation-write` grant and requires `Contents: write`. If `Workflows: write` is configured, it is included so workflow-file changes can be pushed.
136
+ - 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`.
137
137
  - 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.
138
138
  - REST Git database and ref writes are denied by the current write allowlist. Use Git smart HTTP (`git push`) for branch updates instead.
139
139
  - 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.
@@ -144,10 +144,10 @@ Repository scoping and the egress allowlist are the write boundaries. Credential
144
144
 
145
145
  Be careful with mixed-surface PR commands. Use the allowlisted REST endpoints
146
146
  rather than GraphQL-backed `gh pr` mutation commands. PR-native title, body,
147
- base, state, ready-for-review, and requested-reviewer changes use the pull
148
- request grant. Comments, labels, and assignees use GitHub's issue endpoints and
149
- the issue grant. Merge, branch deletion, REST ref mutation, and administration
150
- remain unsupported API operations.
147
+ base, state, ready-for-review, and requested-reviewer changes use pull request
148
+ endpoints. Comments, labels, and assignees use GitHub's issue endpoints. All
149
+ allowlisted bot writes share `installation-write`; merge, branch deletion, REST
150
+ ref mutation, and administration remain unsupported API operations.
151
151
 
152
152
  For PR creation in automation, push explicitly and pass that branch as `head`:
153
153
 
package/dist/index.d.ts CHANGED
@@ -12,11 +12,12 @@ export interface GitHubPluginOptions {
12
12
  */
13
13
  additionalUserScopes?: string[];
14
14
  /**
15
- * GitHub App installation permissions Junior should request for app tokens.
15
+ * GitHub App permissions Junior should expose as capabilities and downscope
16
+ * to read for installation-read tokens.
16
17
  *
17
18
  * Keys may use GitHub permission names with underscores or hyphens. Junior
18
- * records these as plugin capabilities and requests read-only installation
19
- * tokens by scoping read-capable permissions down to `read`.
19
+ * records these as plugin capabilities. Installation-write tokens inherit
20
+ * the App installation's complete permission envelope.
20
21
  * GitHub remains the source of truth for whether a permission exists.
21
22
  */
22
23
  appPermissions?: GitHubAppPermissions;
package/dist/index.js CHANGED
@@ -119,22 +119,24 @@ function sentryConversationUrl(conversationId) {
119
119
  const port = parsed.port ? `:${parsed.port}` : "";
120
120
  return `${parsed.protocol}//${parsed.hostname}${port}/organizations/${orgSlug}/${path}`;
121
121
  }
122
- function githubConversationFooter(conversationId) {
122
+ function githubConversationFooter(conversationId, dashboardUrl) {
123
123
  const id = nonEmptyString(conversationId, "conversationId");
124
- const sessionUrl = sentryConversationUrl(id);
124
+ const normalizedDashboardUrl = dashboardUrl?.trim();
125
+ const sessionUrl = normalizedDashboardUrl || sentryConversationUrl(id);
125
126
  if (!sessionUrl) {
126
127
  return void 0;
127
128
  }
129
+ const label = normalizedDashboardUrl ? "View Junior Session" : "View Junior Session in Sentry";
128
130
  return `${GITHUB_SESSION_FOOTER_START}
129
131
 
130
132
  --
131
133
 
132
- [View Junior Session in Sentry](${sessionUrl})
134
+ [${label}](${sessionUrl})
133
135
 
134
136
  ${GITHUB_SESSION_FOOTER_END}`;
135
137
  }
136
- function appendGitHubFooter(body, conversationId) {
137
- const footer = githubConversationFooter(conversationId);
138
+ function appendGitHubFooter(body, conversationId, dashboardUrl) {
139
+ const footer = githubConversationFooter(conversationId, dashboardUrl);
138
140
  const normalizedBody = body.trimEnd();
139
141
  const existingFooter = new RegExp(
140
142
  `${escapeRegExp(GITHUB_SESSION_FOOTER_START)}[\\s\\S]*?${escapeRegExp(GITHUB_SESSION_FOOTER_END)}`
@@ -645,7 +647,7 @@ function isDefinitiveGitHubPullRequestCreateRejection(error) {
645
647
  }
646
648
  return [400, 401, 404, 410, 422].includes(error.status);
647
649
  }
648
- function createGitHubPullRequestRequest(conversationId, input, actor) {
650
+ function createGitHubPullRequestRequest(conversationId, input, actor, dashboardUrl) {
649
651
  const repo = parseRepo2(input.repo);
650
652
  const payload = {
651
653
  title: nonEmptyString3(input.title, "title"),
@@ -653,7 +655,8 @@ function createGitHubPullRequestRequest(conversationId, input, actor) {
653
655
  base: nonEmptyString3(input.base, "base"),
654
656
  body: appendGitHubFooter(
655
657
  appendGitHubRequesterAttribution(input.body ?? "", actor),
656
- conversationId
658
+ conversationId,
659
+ dashboardUrl
657
660
  ),
658
661
  ...input.draft !== void 0 ? { draft: input.draft } : {}
659
662
  };
@@ -788,7 +791,8 @@ function createGitHubPullRequestTool(ctx) {
788
791
  const request = createGitHubPullRequestRequest(
789
792
  conversationId,
790
793
  parsedInput,
791
- ctx.actor
794
+ ctx.actor,
795
+ ctx.slack?.conversationLink?.url
792
796
  );
793
797
  const pendingState = {
794
798
  status: "pending",
@@ -848,6 +852,7 @@ var USER_REFRESH_TIMEOUT_MS = 2e4;
848
852
  var GITHUB_GRAPHQL_RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
849
853
  var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
850
854
  var USER_TOKEN_GRANTS = /* @__PURE__ */ new Set(["user-read", "user-write"]);
855
+ var CREATE_TOOL_ROUTING_GUIDANCE = "This is a Junior tool-routing denial, not a GitHub permission failure. Do not ask the user for GitHub permissions; retry with the required Junior tool.";
851
856
  var USER_WRITE_REQUIREMENTS = [
852
857
  "requesting GitHub user permission to perform this operation"
853
858
  ];
@@ -1572,17 +1577,16 @@ async function issueInstallationCredential(options) {
1572
1577
  throw new GitHubPluginSetupError(`Invalid ${options.installationIdEnv}`);
1573
1578
  }
1574
1579
  const appJwt = createAppJwt(appId, options.privateKeyEnv);
1575
- const permissions = options.permissions ?? await options.loadPermissions?.({ appJwt, installationId });
1580
+ const body = "repositories" in options ? { repositories: options.repositories } : {
1581
+ permissions: "permissions" in options ? options.permissions : await options.loadPermissions({ appJwt, installationId })
1582
+ };
1576
1583
  const accessTokenResponse = await githubRequest(
1577
1584
  "https://api.github.com",
1578
1585
  `/app/installations/${installationId}/access_tokens`,
1579
1586
  {
1580
1587
  method: "POST",
1581
1588
  token: appJwt,
1582
- body: {
1583
- ...permissions ? { permissions } : {},
1584
- ...options.repositories ? { repositories: options.repositories } : {}
1585
- }
1589
+ body
1586
1590
  }
1587
1591
  );
1588
1592
  const parsedToken = parseInstallationTokenResponse(accessTokenResponse);
@@ -1865,7 +1869,7 @@ function assertGitHubWriteAllowed(input) {
1865
1869
  input.bodyText
1866
1870
  )) {
1867
1871
  throw new EgressPolicyDenied(
1868
- "GitHub issue creation must use the github_createIssue tool so Junior can own idempotency and the conversation footer."
1872
+ `GitHub issue creation must use the github_createIssue tool so Junior can own idempotency and the conversation footer. ${CREATE_TOOL_ROUTING_GUIDANCE}`
1869
1873
  );
1870
1874
  }
1871
1875
  if (isGitHubPullCreateRestRequest(input.method, input.upstreamUrl) || isGitHubPullCreateGraphqlMutation(
@@ -1874,21 +1878,17 @@ function assertGitHubWriteAllowed(input) {
1874
1878
  input.bodyText
1875
1879
  )) {
1876
1880
  throw new EgressPolicyDenied(
1877
- "GitHub pull request creation must use the github_createPullRequest tool so Junior can own idempotency and the conversation footer."
1881
+ `GitHub pull request creation must use the github_createPullRequest tool so Junior can own idempotency and the conversation footer. ${CREATE_TOOL_ROUTING_GUIDANCE}`
1878
1882
  );
1879
1883
  }
1880
1884
  }
1881
- function grantRequirements(reason) {
1882
- return reason === "github.user-write" ? USER_WRITE_REQUIREMENTS : void 0;
1883
- }
1884
1885
  function grantForAccess(access, reason, name, leaseScope) {
1885
- const requirements = grantRequirements(reason);
1886
1886
  return {
1887
1887
  name,
1888
1888
  access,
1889
1889
  ...leaseScope ? { leaseScope } : {},
1890
1890
  reason,
1891
- ...requirements ? { requirements } : {}
1891
+ ...reason === "github.user-write" ? { requirements: USER_WRITE_REQUIREMENTS } : {}
1892
1892
  };
1893
1893
  }
1894
1894
  function repositoryLeaseScope(upstreamUrl) {
@@ -1971,10 +1971,10 @@ function githubPlugin(options = {}) {
1971
1971
  const appIdEnv = options.appIdEnv ?? GITHUB_APP_ID_ENV;
1972
1972
  const privateKeyEnv = options.privateKeyEnv ?? GITHUB_APP_PRIVATE_KEY_ENV;
1973
1973
  const installationIdEnv = options.installationIdEnv ?? GITHUB_INSTALLATION_ID_ENV;
1974
- const appPermissions = normalizePermissions(options.appPermissions);
1975
- const appReadPermissions = appPermissions ? readGrantPermissions(appPermissions) : void 0;
1974
+ const declaredAppPermissions = normalizePermissions(options.appPermissions);
1975
+ const declaredReadPermissions = declaredAppPermissions ? readGrantPermissions(declaredAppPermissions) : void 0;
1976
1976
  const loadReadPermissions = createPermissionCache();
1977
- const appCapabilities = permissionCapabilities(appPermissions);
1977
+ const appCapabilities = permissionCapabilities(declaredAppPermissions);
1978
1978
  const userScopes = normalizeScopeList(options.additionalUserScopes);
1979
1979
  const userScope = userScopes.length ? userScopes.join(" ") : void 0;
1980
1980
  return defineJuniorPlugin({
@@ -2090,7 +2090,7 @@ function githubPlugin(options = {}) {
2090
2090
  appIdEnv,
2091
2091
  privateKeyEnv,
2092
2092
  installationIdEnv,
2093
- ...appReadPermissions ? { permissions: appReadPermissions } : { loadPermissions: loadReadPermissions }
2093
+ ...declaredReadPermissions ? { permissions: declaredReadPermissions } : { loadPermissions: loadReadPermissions }
2094
2094
  });
2095
2095
  }
2096
2096
  if (ctx.grant.name === "installation-write") {
@@ -2101,12 +2101,8 @@ function githubPlugin(options = {}) {
2101
2101
  appIdEnv,
2102
2102
  privateKeyEnv,
2103
2103
  installationIdEnv,
2104
- ...appPermissions ? {
2105
- permissions: {
2106
- ...appPermissions,
2107
- metadata: "read"
2108
- }
2109
- } : {},
2104
+ // This repository-only variant cannot downscope the installed
2105
+ // App envelope with an operation-specific permission body.
2110
2106
  repositories: [repository.name]
2111
2107
  });
2112
2108
  }
@@ -1,6 +1,6 @@
1
1
  export type GitHubAppPermissionLevel = "admin" | "read" | "write";
2
2
  export type GitHubAppPermissions = Record<string, GitHubAppPermissionLevel>;
3
- /** Validate configured GitHub App permissions before using them in grants. */
3
+ /** Validate the configured GitHub App capability and read-token declaration. */
4
4
  export declare function normalizePermissions(permissions: GitHubAppPermissions | undefined): GitHubAppPermissions | undefined;
5
5
  /** Build the read-only installation-token permission body. */
6
6
  export declare function readGrantPermissions(permissions: Record<string, unknown> | undefined): Record<string, "read">;
@@ -1,13 +1,10 @@
1
1
  export declare const GITHUB_SESSION_FOOTER_START = "<!-- junior-session-footer:start -->";
2
2
  export declare const GITHUB_SESSION_FOOTER_END = "<!-- junior-session-footer:end -->";
3
3
  export declare function sentryConversationUrl(conversationId: string): string | undefined;
4
- /**
5
- * Build the Junior session footer block for GitHub issues and pull requests.
6
- * Returns undefined when Sentry DSN/org are not configured.
7
- */
8
- export declare function githubConversationFooter(conversationId: string): string | undefined;
4
+ /** Build the Junior session footer, preferring a host-provided dashboard link. */
5
+ export declare function githubConversationFooter(conversationId: string, dashboardUrl?: string): string | undefined;
9
6
  /**
10
7
  * Append (or replace an existing) Junior session footer to a GitHub body string.
11
- * When Sentry is not configured, returns the body unchanged (existing footer stripped).
8
+ * Without a dashboard or Sentry link, returns the body unchanged (existing footer stripped).
12
9
  */
13
- export declare function appendGitHubFooter(body: string, conversationId: string): string;
10
+ export declare function appendGitHubFooter(body: string, conversationId: string, dashboardUrl?: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-github",
3
- "version": "0.102.2",
3
+ "version": "0.103.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "@sinclair/typebox": "^0.34.49",
27
27
  "zod": "^4.4.3",
28
- "@sentry/junior-plugin-api": "0.102.2"
28
+ "@sentry/junior-plugin-api": "0.103.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^25.9.1",
@@ -1,11 +1,18 @@
1
1
  ---
2
2
  name: github-code
3
- description: Work with GitHub repositories, source code, branches, commits, pushes, pull requests, reviews, diffs, and CI runs. Use for repo implementation tasks, source-code investigation, cloning/fetching, branch and push workflows, PR creation, PR updates, PR review feedback, and GitHub Actions checks. Prefer this skill for repository and code tasks even when the repo concerns Sentry products.
3
+ description: Work with GitHub repositories, source code, branches, commits, pushes, pull requests, reviews, diffs, CI runs, and GitHub App credentials for branch and PR workflows. Use for repo implementation tasks, source-code investigation, cloning/fetching, branch and push workflows, PR creation or updates, PR review feedback, GitHub credential or permission failures involving repositories, pushes, or PRs, and GitHub Actions checks. Prefer this skill for repository and code tasks even when the repo concerns Sentry products.
4
4
  ---
5
5
 
6
6
  # GitHub Code Operations
7
7
 
8
- Use `gh` and `git` for repository checkout, source investigation, code changes, commits, and pull request inspection or mutation. Use Junior's `github_createPullRequest` tool for new pull requests.
8
+ ## Credential contract
9
+
10
+ - Supported App-owned GitHub operations require no user-managed credential. Junior automatically mints and injects a repository-scoped GitHub App `installation-write` credential.
11
+ - For a pull request from an existing branch, push the branch first, then call `github_createPullRequest`; both operations receive the automatic installation credential.
12
+ - Do not ask the user to authorize GitHub, provide a token, or grant Contents/Pull requests permissions before trying a supported operation.
13
+ - Request remediation only after a supported operation returns evidence of an upstream provider denial. A Junior tool-routing denial means retry with the required tool, not request permissions.
14
+
15
+ Use `gh` and `git` for repository checkout, source investigation, code changes, commits, and pull request inspection or mutation. Use Junior's `github_createPullRequest` tool instead of `gh pr create` for new pull requests.
9
16
 
10
17
  ## References
11
18
 
@@ -65,9 +65,9 @@ jr-rpc config set github.repo owner/repo
65
65
  - Prefer `--json` output for machine-readable parsing where available.
66
66
  - Pass extra `git clone` flags after `--` (e.g. `gh repo clone owner/repo -- --depth=1`).
67
67
  - A local `git commit` does not call GitHub. Pushing that commit uses Junior's repository-scoped installation credential and requires `github.contents.write` on the target repo.
68
- - If the commit changes workflow files under `.github/workflows`, expect `github.workflows.write` in addition to contents write.
68
+ - If the commit changes workflow files under `.github/workflows`, the App installation needs Workflows write in addition to Contents write.
69
69
  - Before `github_createPullRequest`, push the head branch explicitly and resolve the target repo's default branch for `base`. That push requires GitHub write access to the remote.
70
70
  - Merge, fork creation, workflow reruns or cancellations, REST contents/Git database writes, and repository administration are outside the current write allowlist.
71
71
  - If the explicit `git push` fails with 401/403 or another access/permission error, verify the repo context and retry once. If it still fails, load troubleshooting guidance and report the exact command failure.
72
- - PR comments, labels, and assignees use GitHub's issue endpoints and therefore the issue grant; use the `github-issues` REST guidance for those operations.
72
+ - PR comments, labels, and assignees use GitHub's issue endpoints; use the `github-issues` REST guidance for those operations. All allowlisted bot writes share the same repository-scoped `installation-write` credential.
73
73
  - Return actionable errors for access, permission, not-found, and validation failures.
@@ -13,9 +13,10 @@ Use this table to recover quickly while keeping operations deterministic.
13
13
  | `junior-auth-required provider=github grant=user-write` | User-to-server OAuth is missing or stale for a human-identity operation. | Follow the private OAuth prompt; do not ask the user to paste or manage tokens manually. |
14
14
  | `git push` fails with 401/403 or auth/permission output | Write permission is missing, app installation is too narrow, or remote is wrong. | Verify the remote and repo context, retry once, then confirm app permissions and installation scope if it still fails. |
15
15
  | Bash result includes `permission_denied` with `source: "upstream"` | GitHub returned 403 after Junior injected the named grant. | Do not call this a Junior runtime block. Use the message, connected account, upstream target, grant requirements, accepted-permissions, and SSO fields to explain the GitHub denial. |
16
- | 403 Forbidden | App lacks required permission on repo or install scope is too narrow. | Verify the repo context, then confirm GitHub App permissions and installation scope. |
16
+ | 403 without `permission_denied` where `source: "upstream"` | Junior may have rejected an unsupported route before contacting GitHub. | Read the response body. Follow any required-tool instruction; do not ask for GitHub permissions unless the failure is confirmed upstream. |
17
17
  | `gh auth status` shows `Token scopes: none` | Expected for GitHub App user-to-server tokens. | Do not treat this as read-only proof. Use the failed command, `permission_denied.acceptedPermissions`, and GitHub App permissions instead. |
18
- | `github_createPullRequest` fails with auth/permission errors | The branch was not pushed first, or repo context is wrong. | Push the branch explicitly first, then retry `github_createPullRequest` with explicit `repo`, `head`, and `base` values. |
18
+ | `github_createPullRequest` returns upstream 401/403 | The App installation or target repository does not permit the operation. | Use the structured upstream denial to verify installation scope and accepted permissions; do not request user OAuth for this bot-owned operation. |
19
+ | `github_createPullRequest` returns 422 for `head` | The head branch was not pushed or the explicit head ref is wrong. | Push the branch, then retry with explicit `repo`, `head`, and `base` values. |
19
20
  | `github_createPullRequest` fails with 422 validation on `base` | The `base` branch does not exist in the target repo. | Resolve the default branch with `gh repo view owner/repo --json defaultBranchRef --jq .defaultBranchRef.name`, then retry with that value as `base`. |
20
21
  | `git blame`, long log history, or old commits are missing after clone | Repo was cloned shallow by design. | Deepen incrementally with `git -C DIRECTORY fetch --depth=N origin`, or `git -C DIRECTORY fetch --unshallow` when full history is required. |
21
22
  | `sandbox setup failed (dnf install gh failed ...)` | `gh` package not available from the plugin runtime dependency bootstrap. | Report the plugin runtime bootstrap failure; do not try to repair package installation from the skill workflow. |
@@ -2,18 +2,19 @@
2
2
 
3
3
  Use this table to recover quickly while keeping operations deterministic.
4
4
 
5
- | Symptom | Likely cause | Fix |
6
- | ------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
7
- | `unknown command "issue"` from `gh` | CLI version too old or wrong binary in the plugin runtime. | Verify `gh --version`; if it is unavailable or too old, report that the GitHub plugin runtime dependency is not available. |
8
- | `Missing required option --repo` | Repo not passed and no default was resolved. | Resolve with `jr-rpc config get github.repo`; pass `--repo owner/repo` explicitly when missing. |
9
- | Command affects or authenticates against the wrong repo | Stale `github.repo` default or authenticated command missing explicit repo. | Pass `--repo owner/repo` for the target repository, or update `github.repo` before retrying. |
10
- | `GraphQL: Could not resolve to a Repository` | Repo slug is wrong or inaccessible. | Validate `owner/repo` and confirm app installation on target repository. |
11
- | 401 Unauthorized | Issued GitHub credentials were rejected upstream. | Verify the target repo, then use the grant/auth signal to distinguish stale user OAuth from app installation or host env setup. |
12
- | `junior-auth-required provider=github grant=user-write` | User-to-server OAuth is missing or stale for a human-identity operation. | Follow the private OAuth prompt; do not ask the user to paste or manage tokens manually. |
13
- | 403 Forbidden | App lacks required permission on repo or install scope is too narrow. | Verify the repo context, then confirm GitHub App permissions and installation scope. |
14
- | 404 Not Found | Issue number or repo is wrong. | Validate repo + issue ID with `gh issue view NUMBER --repo owner/repo`. |
15
- | Issue label mutation fails | Wrong REST payload or wrong repo context. | Use the allowlisted issue labels endpoint with an explicit `owner/repo` path and valid JSON input. |
16
- | Comment command fails with empty body | JSON input is missing a non-empty `body`. | Ensure the REST comment payload contains a non-empty `body` before retrying. |
5
+ | Symptom | Likely cause | Fix |
6
+ | ---------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | `unknown command "issue"` from `gh` | CLI version too old or wrong binary in the plugin runtime. | Verify `gh --version`; if it is unavailable or too old, report that the GitHub plugin runtime dependency is not available. |
8
+ | `Missing required option --repo` | Repo not passed and no default was resolved. | Resolve with `jr-rpc config get github.repo`; pass `--repo owner/repo` explicitly when missing. |
9
+ | Command affects or authenticates against the wrong repo | Stale `github.repo` default or authenticated command missing explicit repo. | Pass `--repo owner/repo` for the target repository, or update `github.repo` before retrying. |
10
+ | `GraphQL: Could not resolve to a Repository` | Repo slug is wrong or inaccessible. | Validate `owner/repo` and confirm app installation on target repository. |
11
+ | 401 Unauthorized | Issued GitHub credentials were rejected upstream. | Verify the target repo, then use the grant/auth signal to distinguish stale user OAuth from app installation or host env setup. |
12
+ | `junior-auth-required provider=github grant=user-write` | User-to-server OAuth is missing or stale for a human-identity operation. | Follow the private OAuth prompt; do not ask the user to paste or manage tokens manually. |
13
+ | 403 without `permission_denied` where `source: "upstream"` | Junior may have rejected an unsupported route before contacting GitHub. | Read the response body. Follow any required-tool instruction; do not ask for GitHub permissions unless the failure is confirmed upstream. |
14
+ | `permission_denied` with `source: "upstream"` | GitHub rejected the injected installation credential. | Verify the target, accepted permissions, and App installation scope; do not request user OAuth for a bot-owned issue operation. |
15
+ | 404 Not Found | Issue number or repo is wrong. | Validate repo + issue ID with `gh issue view NUMBER --repo owner/repo`. |
16
+ | Issue label mutation fails | Wrong REST payload or wrong repo context. | Use the allowlisted issue labels endpoint with an explicit `owner/repo` path and valid JSON input. |
17
+ | Comment command fails with empty body | JSON input is missing a non-empty `body`. | Ensure the REST comment payload contains a non-empty `body` before retrying. |
17
18
 
18
19
  ## Retry guidance
19
20