Skip to content

MIP-0076: notify-umbrella sends submodule-updated on every push - #32

Open
brunogbv wants to merge 2 commits into
mainfrom
mip-0076/3-notify-every-push
Open

brunogbv wants to merge 2 commits into
mainfrom
mip-0076/3-notify-every-push

Conversation

@brunogbv

@brunogbv brunogbv commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

Every caller sets permissions: {}, so this keeps permissions: {} and reads the compare API with github.token instead of checking the repo out. event-type stays declared (deprecated, ignored), so a caller still passing it doesn't fail input validation.

The scratch-repo live test (human, not done here) — updated for the fix

The brief's test case — "a code-only push sends only submodule-updated, a docs push sends both,
a first push sends both" — needs a real GitHub push/Actions run, which this task may not do.
Steps for whoever runs it (the PAT scope needed changed: no checkout happens now, so the caller's
own repo needs no Contents grant beyond its default — only the scratch umbrella needs a PAT):

  1. Create two throwaway public repos under your own account (or an org you control), e.g.
    scratch-notify-caller and scratch-notify-umbrella.
  2. On scratch-notify-umbrella: Settings → Developer settings → fine-grained PAT scoped to just
    that repo, Contents: read & write. Add it as a repo secret on scratch-notify-caller, e.g.
    SCRATCH_PAT. (A scratch umbrella, not marola-dev/marola, so the test can't dispatch into the
    real umbrella.)
  3. On scratch-notify-caller, add .github/workflows/notify-umbrella.yml:
    name: notify umbrella
    on:
      push:
        branches: [main]
    jobs:
      notify:
        uses: marola-dev/marola-devkit/.github/workflows/notify-umbrella.yml@mip-0076/3-notify-every-push
        with:
          umbrella: <your-account>/scratch-notify-umbrella
        secrets:
          token: ${{ secrets.SCRATCH_PAT }}
    (A reusable-workflow uses: ref accepts a branch name, not only a tag, so this can point at
    the unreleased branch directly — no devkit release needed to test it. permissions: {} at the
    top, matching today's five real callers, is fine to add too — it's exactly the case this fix
    round makes work.)
  4. First push (repo creation, pushing the file above as the initial commit on main):
    github.event.before is the all-zero SHA. Expect the run's log to show both
    dispatched submodule-updated for … and dispatched submodule-docs-updated for …, and the
    docs step's output to be changed=true.
  5. Code-only push: commit a change to an unrelated file (e.g. touch a src/placeholder.txt).
    Expect only dispatched submodule-updated …, no submodule-docs-updated line, docs step
    output changed=false.
  6. Docs push: commit a change to README.md (or add a file under docs/). Expect both
    dispatch lines again, docs step output changed=true.
  7. Paste the three run URLs into the PR, as the plan row asks.
  8. Clean up: delete both scratch repos and the PAT afterward.

I did not create any repo or push anything — this is left as the human-run step the brief calls
out.

brunogbv and others added 2 commits October 5, 2026 09:00
…ated on a docs diff

MIP-0076 §5.5 step 1's half for notify-umbrella.yml. Until now every repo's
notify-umbrella sent only submodule-docs-updated, and only because the caller's
paths: filter made that the only trigger — pointer-sync.yml listens for
submodule-updated too, but nothing has ever sent it (MIP-0076 §2, confirmed
2026-10-03).

The workflow now checks out the caller's own repo and diffs
github.event.before..github.sha itself to decide: submodule-updated goes on
every call, submodule-docs-updated goes too when that range touches README.md
or docs/** — so a caller drops its paths: filter and runs on every push. A
zero or unreachable before (a branch's first push, or history rewritten out
from under it) counts as a docs change, so both events go rather than
silently dropping one.

event-type stays declared, now unused, instead of being dropped: GitHub
fails a reusable-workflow call outright if the caller still passes an input
the called workflow no longer declares, and today's five callers are
updated to drop paths: in later rows of this MIP, not this one.

Closes #27

Tested: actionlint ok (repo-wide); shellcheck --severity=error ok; ruff check/format ok (uvx ruff 0.16.9, no nix); bash tests/self-tests.sh all ok; scripts/agents-check.sh ok; python3 scripts/docs_lint.py clean; claude plugin validate . passed
Cost: ~$5.14 est. · ~2.6M tokens est. (diff-size model, 88 lines, no session log, IQR 0.6M-13.5M tokens for this diff from 75 calibrated commits) · scripts/cost-split.py --estimate 2026-10-05
Co-Authored-By: Claude <noreply@anthropic.com>
…a checkout

A reusable workflow's effective permissions can only keep or lower what the
caller granted, never raise it, and all five current callers set
permissions: {} with no override on the notify: job — so the checkout
added in the previous commit would have failed every one of them once
rows 6-10 bump to this tag.

Drop the checkout and git diff; call GET
/repos/$REPO/compare/$BEFORE...$SHA with github.token instead (a
no-permission token can still read a public repo over the API) and read
.files[].filename from the response with jq. changed=true on a README.md
or docs/ filename, same as before, plus now also on a non-200 response
(before unknown to the API, e.g. a force-push) and on a truncated compare
(the API caps its file list at 300). Restores permissions: {},
timeout-minutes: 2 and the header's "no checkout needed on either side".

Closes #27

Tested: actionlint ok (repo-wide); shellcheck --severity=error ok; ruff check/format ok (uvx ruff 0.16.9, no nix); bash tests/self-tests.sh all ok; scripts/agents-check.sh ok; python3 scripts/docs_lint.py clean; claude plugin validate . passed; jq filter checked by hand against four fixture compare responses (match on docs/, match on README.md, no match, empty files)
Cost: ~$3.40 est. · ~1.7M tokens est. (diff-size model, 48 lines, no session log, IQR 0.4M-8.9M tokens for this diff from 75 calibrated commits) · scripts/cost-split.py --estimate 2026-10-05
Co-Authored-By: Claude <noreply@anthropic.com>
@brunogbv

brunogbv commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Live test in a throwaway caller, marola-dev/scratch-notify. It has permissions: {} at the top, still passes the deprecated event-type, and uses MAROLA_CROSS_REPO_PAT, dispatching into marola-dev/marola:

Push Expected Run Dispatched
code only (the workflow file) submodule-updated 37276214158 (re-run after the secret was added) submodule-updated
README.md change both 37284350548 submodule-updated, submodule-docs-updated
first push of a new branch (before all zeros) both 37284393624 submodule-updated, submodule-docs-updated

This also confirms that a no-permission github.token can read the public compare API, and that a caller still passing event-type passes input validation.

h0ffmann pushed a commit that referenced this pull request Oct 8, 2026
One command, run twice around a human merge. On a main whose versions are not X.Y.Z it writes
the version into plugin.json, flake.nix and every documented pin, adds a CHANGELOG heading with
the commits since the last tag, and pushes chore/release-vX.Y.Z for a PR. On a main that already
says X.Y.Z everywhere it tags vX.Y.Z and pushes the tag; release.yml checks the versions against
the tag and publishes the GitHub release. `just quality` now fails when the versions disagree.

The consumer pin-bump PRs from #47 wait on the token decision there.

Part of #47.

Tested: release.py --self-test; --check on main (29 locations at 0.6.0) and against 0.5.1 (fails); --dry-run of both paths in a fresh clone; ruff, actionlint, tests/self-tests.sh, docs-lint, agents-check; trial merge with #32 and #33 passes the same gates
Cost: n/a (no measured figure in this cloud session)
Co-Authored-By: Claude <noreply@anthropic.com>
h0ffmann added a commit that referenced this pull request Oct 8, 2026
One command, run twice around a human merge. On a main whose versions are not X.Y.Z it writes
the version into plugin.json, flake.nix and every documented pin, adds a CHANGELOG heading with
the commits since the last tag, and pushes chore/release-vX.Y.Z for a PR. On a main that already
says X.Y.Z everywhere it tags vX.Y.Z and pushes the tag; release.yml checks the versions against
the tag and publishes the GitHub release. `just quality` now fails when the versions disagree.

The consumer pin-bump PRs from #47 wait on the token decision there.

Part of #47.

Tested: release.py --self-test; --check on main (29 locations at 0.6.0) and against 0.5.1 (fails); --dry-run of both paths in a fresh clone; ruff, actionlint, tests/self-tests.sh, docs-lint, agents-check; trial merge with #32 and #33 passes the same gates
Cost: n/a (no measured figure in this cloud session)

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant