Repository navigation
feat(digest): survey default-branch protection in the daily digest - #214
Merged
Merged
Conversation
dev-env#211 asked whether a periodic check should flag fleet repos that
lack protection or a required standards-check. jordan-paddock-pitch went a
week without either, and nothing noticed. The digest already enumerates
every repo the App can read, so the check rides that loop instead of
adding a second enumeration.
standards.sh now reads GET repos/{nwo}/branches/{branch} for each
non-archived repo and adds a "protection" object to its record. The probe
runs before the standards survey, so a repo whose standards read fails
still gets a protection result. States:
- required: protected, enabled, enforcement not "off", and a required
context matching the existing standards-check name rule.
- protected-no-standards: protected, but standards-check is not
required or nothing is enforced. .protected == true alone never counts
as required: STATUS.md records (2026-09-16) that .protected read true
for repos with no effective rules.
- unprotected: .protected == false and no exempt class applies.
- exempt: unprotected, and in a class exempt by decision.
- unreadable: the read failed, .protected is not a boolean, the
protection settings are missing or malformed, or the listing gave no
fork/private flag for an unprotected repo. Whether the App token
(contents/checks/statuses/pull-requests read, no administration) gets
the protection object is unverified, so a withheld field must read as
unreadable, never as a finding or as clean.
Exempt classes are predicates, not repo names: forks, private
smartwatermelon repos (dev-env#85), private twistedmelonman repos (Free
user accounts cannot protect them). Private nightowlstudiollc repos are
not exempt: NOS keeps Team and protection there is a requirement. Three
predicates on fork, owner and visibility live in a commented block in
standards.sh; a parsed config file would need its own mini-language.
render.sh adds a "Branch protection" section: required and exempt repos
are counted (exempt by class, with the reason); unprotected,
protected-no-standards and unreadable repos are listed with links. If no
repo could be read, the section says protection could not be verified
and prints no counts. A record with no protection object is unreadable.
Fixtures for required, exempt and unprotected states are real branch
responses captured 2026-10-21, trimmed to name/protected/protection. The
edge cases are those captures with one field changed. Each new assertion
was checked against a deliberate break of the logic it covers.
Closes #211
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Answers the last open question on #211: the daily digest now flags fleet repos whose default branch is unprotected or does not require
standards-check / run-standards-check.standards.shreadsGET repos/{nwo}/branches/{branch}in the existing per-repo loop (no second enumeration) and adds aprotectionobject to each record:required,protected-no-standards,unprotected,exempt(with class), orunreadable.render.shadds a "Branch protection" section. Required and exempt repos are counted (exempt by class, with reason and decision date). Unprotected, protected-no-standards and unreadable repos are listed with links. If no repo could be read, the section says protection could not be verified and prints no counts.Design choices
requiredneeds more than.protected == true: it also needsprotection.enabled == true, anenforcement_levelother thanoff, and a context matching the existing standards-check name rule. STATUS.md (2026-09-16) records.protectedreadingtruefor repos with no effective rules; today's capture ofclaude-config-backupreadsfalse, so that note may be stale, but the stricter rule costs nothing..protected, missing or malformed protection settings, or an unprotected repo whose listing has nofork/privateflag all render as UNREADABLE.standards.sh, not a parsed config file.protected:falsewithprotectionpresent (enabled:false,enforcement_level:"off",contexts:[]), not an absentrequired_status_checks. The fixtures use that real shape.Unverified
protectionsub-object. Session tokens did. If it does not, every repo reads UNREADABLE and the section says so. No permission was added to the workflow.required_status_checks: null(protection by reviews only) reads UNREADABLE, notprotected-no-standards. No fleet repo is in that state today, so the real shape was not captured.Tests
name/protected/protection), and edge cases built from those captures with one field changed: protection withheld,protected: null, enforcement off, other checks only, NOS private, twistedmelonman private, a public unprotected repo, missing listing flags, and a 403.bash scripts/dependabot-digest/tests/run-tests.sh: classify, render, run-digest and standards pass. Locally,test-upsert-issue.sh(untouched) fails because the session exports aghshell function (BASH_FUNC_gh%%) whose approval gate overrides the PATH stub. CI has no such function.shellcheck -S infois clean on every touched script.Closes #211