Skip to content
20 changes: 20 additions & 0 deletions memories/gha-reusable-workflow-permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@
- **Do:** see `github-actions.md`'s "GitHub Actions workflow authoring
gotchas" for the permission-grant mechanism and remedy; this only adds
where to find the error text once you've hit it.
- **Confirmed again, different repo, different permission**:
`Morrison-Lab/psw`'s `claude-code-review.yml` caller granted `contents`,
`pull-requests`, `issues`, `id-token` but omitted `actions`, while the
callee's `claude-review` job requests `actions: read` (to let
`claude-code-action` install its `github_ci` MCP server). Same

Check warning on line 50 in memories/gha-reusable-workflow-permissions.md

View workflow job for this annotation

GitHub Actions / new-line-breaks / check-new-line-breaks

Line packs more than one sentence: `claude-code-action` install its `github_ci` MCP server). Same
`startup_failure`, zero jobs, nothing in `pull_request_read`
`get_check_runs` or `get_job_logs`. `WebFetch` on the run's `html_url`

Check warning on line 52 in memories/gha-reusable-workflow-permissions.md

View workflow job for this annotation

GitHub Actions / new-line-breaks / check-new-line-breaks

Line packs more than one sentence: `get_check_runs` or `get_job_logs`. `WebFetch` on the run's `html_url`
reliably surfaced the Annotations text verbatim -- "The nested job
'claude-review' is requesting 'actions: read', but is only allowed
'actions: none'." -- confirming this isn't `d-morrison/qwt`-specific and
that a plain `WebFetch` (not just a dedicated `get_page_text` browser
tool) is sufficient to read it. (Morrison-Lab/psw#43/#44, 2026-08-10.)
This exact `actions: read` gap -- same four granted permissions, same
`startup_failure`/zero-jobs shape -- had already happened once before,
in `ai-config` itself rather than a downstream consumer
([`github-actions.md`](github-actions.md), ai-config#224).
So this is the third occurrence, not the second, and "not
`d-morrison/qwt`-specific" above undersells it -- the gap recurs
independently of which repo is calling `Morrison-Lab/gha`, ai-config's
own repo included.

## A caller-level `concurrency:` group with the same name as a nested job's own group deadlocks the run

Expand Down
48 changes: 47 additions & 1 deletion memories/github-mcp-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,53 @@
leave a broken file on the branch waiting for the next review round to catch
it. (Hit on lab-manual#376: an editing slip sent a truncated placeholder
instead of the real fragment text; caught by checking the returned `size`.)
**Confirmed again with a different downstream symptom, and it defeats a
naive round-trip check.** Passing an already-base64-encoded string produced

Check warning on line 385 in memories/github-mcp-tools.md

View workflow job for this annotation

GitHub Actions / new-line-breaks / check-new-line-breaks

Line packs more than one sentence: naive round-trip check.** Passing an already-base64-encoded string produced
a `size` of 2310 bytes for content that should have been 1710 -- a ~4/3
inflation, the base64 expansion ratio, rather than a suspiciously *small*
number this time. The GitHub Actions symptom was different too: since the

Check warning on line 388 in memories/github-mcp-tools.md

View workflow job for this annotation

GitHub Actions / new-line-breaks / check-new-line-breaks

Line packs more than one sentence: number this time. The GitHub Actions symptom was different too: since the
stored blob was a bare base64 scalar rather than a YAML mapping, the
workflow read as having no triggers at all -- a dispatch-time `422
Workflow does not have 'workflow_dispatch' trigger` on that ref (dispatch
to the unmodified default branch worked fine), and the push itself
produced a generic `failure` conclusion with zero jobs (not
`startup_failure`, which is the permissions-cascade shape covered in
[`gha-reusable-workflow-permissions.md`](gha-reusable-workflow-permissions.md)).
A naive "does it decode without erroring"
round-trip check does not catch this: base64-decoding what
`get_file_contents` reads back just undoes your own accidental encoding
and returns the intended text, which looks like confirmation. The `size`

Check warning on line 399 in memories/github-mcp-tools.md

View workflow job for this annotation

GitHub Actions / new-line-breaks / check-new-line-breaks

Line packs more than one sentence: and returns the intended text, which looks like confirmation. The `size`
comparison against the source's real byte length is the check that
actually discriminates. (Morrison-Lab/psw#44, 2026-08-10.)
**A third instance is not an encoding mistake at all --- the `content`
parameter can simply be constructed wrong.**
A follow-up call meant to correct the two case records above instead sent
a literal placeholder string as the whole file body, caught immediately
by `content.size` reading 21 bytes for a ~50KB file.
**A local clone plus a real `git push` avoids this class of mistake
entirely, when push is available.**
`git clone --depth 1 --filter=blob:none --sparse` plus `git push` from
that clone worked in this same session, for a branch that was neither
harness-assigned nor the working directory's own repo --- consistent
with [`github.md`](github.md)'s "the proxy allows branch creation/push
but BLOCKS branch deletion."
`git config -l` showed no local credential (only
`http.proxyauthmethod=basic` and `credential.interactive=false`, no
`~/.git-credentials` or `~/.netrc`), so authentication happens somewhere
in the outbound proxy layer rather than the checkout --- consistent with
this environment's outbound HTTPS being proxied, though the exact
mechanism wasn't traced further.
Once a branch exists to push to, prefer editing the file locally and
pushing over `create_or_update_file`/`push_files` for anything beyond a
trivial edit: the committed content is exactly what `git diff` shows, and
`git hash-object` verifies it byte-for-byte before AND after the push,
with no encoding step or parameter-construction step for a mistake to
hide in.
Not every session gets this --- some are restricted to the
harness-assigned branch only, or fully read-only, per
[`github-actions.md`](github-actions.md)'s "403 caveat" and "fully
READ-ONLY" entries --- so test with a throwaway push before relying on
it.
- **Issue *writes* 404 while *reads* succeed → the issue was transferred to
another repo, not a permissions gap.** If `mcp__github__add_issue_comment` /
`issue_write` to `owner/repo#<N>` fail (`404 Not Found`, or `Could not resolve
Expand Down Expand Up @@ -708,4 +755,3 @@
where the table actually lives. (Caught in ai-config#137 review: the gip
skill referenced a table ai-config didn't have at the time; ai-config#327
later added `tool-mappings.md` to close that gap.)

Loading