From f1795238d4ea000fb94dfb267f2ee2d321aaf334 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:44:41 +0000 Subject: [PATCH 01/10] feat: add read-only standalone squabbler gate census --- scripts/estate-gate-census.sh | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 scripts/estate-gate-census.sh diff --git a/scripts/estate-gate-census.sh b/scripts/estate-gate-census.sh new file mode 100644 index 0000000..1643288 --- /dev/null +++ b/scripts/estate-gate-census.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Read-only squabbler deployment census. Unknown is NEVER a clean verdict. +set -euo pipefail +command -v gh >/dev/null && command -v jq >/dev/null || exit 2 +tmp="$(mktemp)" +trap 'rm -f "$tmp"' EXIT +out="" +while (($#)); do + case "$1" in + --owner) + gh repo list "$2" --limit 10000 --json nameWithOwner,isArchived,isFork \ + | jq -r '.[] | select(.isArchived != true and .isFork != true) | .nameWithOwner' >> "$tmp" || exit 3 + shift 2 ;; + --repo) printf '%s\n' "$2" >> "$tmp"; shift 2 ;; + --output) out="$2"; shift 2 ;; + -h|--help) echo "Usage: $0 [--owner OWNER]... [--repo OWNER/REPO]... [--output JSONL]"; exit 0 ;; + *) echo "Bad argument: $1" >&2; exit 2 ;; + esac +done +api() { + local data + data="$(gh api "$1" 2>/dev/null)" || return 1 + jq -e . >/dev/null 2>&1 <<< "$data" || return 1 + printf '%s\n' "$data" +} +if [[ -n "$out" ]]; then mkdir -p "$(dirname "$out")"; : > "$out"; fi +emit() { + if [[ -n "$out" ]]; then printf '%s\n' "$1" >> "$out" + else printf '%s\n' "$1"; fi +} +sort -u "$tmp" | while IFS= read -r repo; do + [[ -n "$repo" ]] || continue + [[ "$repo" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || exit 2 + meta="$(api "repos/$repo")" || { + emit "$(jq -nc --arg r "$repo" '{repo:$r,state:"unknown",reason:"metadata_unreadable"}')"; continue + } + branch="$(jq -r '.default_branch // empty' <<<"$meta")" + if [[ -z "$branch" ]]; then + emit "$(jq -nc --arg r "$repo" '{repo:$r,state:"unknown",reason:"default_branch_missing"}')"; continue + fi + ref="$(jq -rn --arg x "$branch" '$x|@uri')" + workflows="$(api "repos/$repo/contents/.github/workflows?ref=$ref")" || { + emit "$(jq -nc --arg r "$repo" '{repo:$r,state:"unknown",reason:"workflow_listing_unavailable"}')"; continue + } + if ! jq -e 'type == "array"' <<<"$workflows" >/dev/null; then + emit "$(jq -nc --arg r "$repo" '{repo:$r,state:"unknown",reason:"bad_listing"}')"; continue + fi + names="$(jq -r '.[] | select(.type=="file" and (.name|test("squabbl.*[.]ya?ml$";"i"))) | .name' <<<"$workflows")" + if [[ -z "$names" ]]; then + emit "$(jq -nc --arg r "$repo" '{repo:$r,state:"filename_absent",note:"embedded_callers_not_examined"}')"; continue + fi + rules="$(api "repos/$repo/rulesets?includes_parents=true")" || rules="" + while IFS= read -r name; do + [[ -n "$name" ]] || continue + rid=""; status=unknown; job=""; job_status=unknown + runs="$(api "repos/$repo/actions/workflows/$name/runs?branch=$ref&per_page=10")" || runs="" + if [[ -n "$runs" ]] && jq -e '.workflow_runs|type=="array"' <<<"$runs" >/dev/null; then + rid="$(jq -r '.workflow_runs[0].id // empty' <<<"$runs")" + status="$(jq -r '.workflow_runs[0].conclusion // .workflow_runs[0].status // "no_run"' <<<"$runs")" + if [[ -n "$rid" ]]; then + jobs="$(api "repos/$repo/actions/runs/$rid/jobs?per_page=100")" || jobs="" + if [[ -n "$jobs" ]] && jq -e '.jobs|type=="array"' <<<"$jobs" >/dev/null; then + job="$(jq -r '[.jobs[]|select((.name//"")|test("squabbl";"i"))|.name]|first//empty' <<<"$jobs")" + if [[ -n "$job" ]]; then + job_status="$(jq -r --arg j "$job" '[.jobs[]|select(.name==$j)|.conclusion//.status]|first//"unknown"' <<<"$jobs")" + elif [[ "$status" == "startup_failure" ]]; then job_status=zero_jobs; fi + fi + fi + fi + required=unknown; bypass=unknown + if [[ -n "$rules" ]] && jq -e 'type=="array"' <<<"$rules" >/dev/null; then + required=not_in_readable_rulesets; bypass='[]' + ids="$(jq -r '.[]|select(.enforcement=="active")|.id' <<<"$rules")" + while IFS= read -r id; do + [[ -n "$id" ]] || continue + detail="$(api "repos/$repo/rulesets/$id")" || { required=unknown; bypass=unknown; break; } + if [[ -n "$job" ]] && jq -e --arg j "$job" \ + '[.rules[]?|select(.type=="required_status_checks")|.parameters.required_status_checks[]?|.context]|index($j)!=null' \ + <<<"$detail" >/dev/null; then + required=required_by_ruleset + bypass="$(jq -c '[.bypass_actors[]?|{actor_type,actor_id,bypass_mode}]' <<<"$detail")" + fi + done <<<"$ids" + fi + emit "$(jq -nc --arg r "$repo" --arg wf "$name" --arg id "$rid" \ + --arg state "$status" --arg job "$job" --arg js "$job_status" \ + --arg req "$required" --arg by "$bypass" \ + '{repo:$r,state:"named_workflow_found",workflow:$wf,run:{id:$id,status:$state,job:$job,job_conclusion:$js},policy:{rulesets:$req,bypass:$by,classic_protection:"not_inspected"}}')" + done <<<"$names" +done From 3f2f9956e523dd40e5fda59d493a32c6c2fd92fb Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:45:11 +0000 Subject: [PATCH 02/10] test: exercise success, zero-job startup failure and absent named workflow --- tests/estate_gate_census_test.sh | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 tests/estate_gate_census_test.sh diff --git a/tests/estate_gate_census_test.sh b/tests/estate_gate_census_test.sh new file mode 100644 index 0000000..9556d91 --- /dev/null +++ b/tests/estate_gate_census_test.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +set -euo pipefail +root="$(cd "$(dirname "$0")/.." && pwd)" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +mkdir -p "$tmp/bin" +cat > "$tmp/bin/gh" <<'MOCK' +#!/usr/bin/env bash +set -euo pipefail +[[ "$1" == api ]] || exit 2 +case "$2" in + repos/example/pass|repos/example/startup|repos/example/absent) + printf '%s\n' '{"default_branch":"main","archived":false}';; + 'repos/example/pass/contents/.github/workflows?ref=main') + printf '%s\n' '[{"name":"cicd-squabbler.yml","type":"file"}]';; + 'repos/example/startup/contents/.github/workflows?ref=main') + printf '%s\n' '[{"name":"squabble.yml","type":"file"}]';; + 'repos/example/absent/contents/.github/workflows?ref=main') + printf '%s\n' '[{"name":"ci.yml","type":"file"}]';; + 'repos/example/pass/actions/workflows/cicd-squabbler.yml/runs?branch=main&per_page=10') + printf '%s\n' '{"workflow_runs":[{"id":7,"conclusion":"success"}]}';; + 'repos/example/startup/actions/workflows/squabble.yml/runs?branch=main&per_page=10') + printf '%s\n' '{"workflow_runs":[{"id":8,"conclusion":"startup_failure"}]}';; + 'repos/example/pass/actions/runs/7/jobs?per_page=100') + printf '%s\n' '{"jobs":[{"name":"Squabble chains (hard gate)","conclusion":"success"}]}';; + 'repos/example/startup/actions/runs/8/jobs?per_page=100') + printf '%s\n' '{"jobs":[]}';; + 'repos/example/pass/rulesets?includes_parents=true') + printf '%s\n' '[{"id":3,"enforcement":"active"}]';; + 'repos/example/startup/rulesets?includes_parents=true') + printf '%s\n' '[]';; + repos/example/pass/rulesets/3) + printf '%s\n' '{"rules":[{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"Squabble chains (hard gate)"}]}}],"bypass_actors":[{"actor_type":"RepositoryRole","actor_id":5,"bypass_mode":"always"}]}';; + *) echo "unexpected mocked API call: $2" >&2; exit 1;; +esac +MOCK +chmod +x "$tmp/bin/gh" +bash -n "$root/scripts/estate-gate-census.sh" +PATH="$tmp/bin:$PATH" bash "$root/scripts/estate-gate-census.sh" \ + --repo example/pass --repo example/startup --repo example/absent --output "$tmp/rows.jsonl" +[[ "$(wc -l < "$tmp/rows.jsonl")" -eq 3 ]] +jq -e -s ' + (map(select(.repo=="example/pass"))[0] | .run.job_conclusion=="success" and + .policy.rulesets=="required_by_ruleset" and (.policy.bypass | contains("always"))) and + (map(select(.repo=="example/startup"))[0] | .run.status=="startup_failure" and + .run.job_conclusion=="zero_jobs") and + (map(select(.repo=="example/absent"))[0] | .state=="filename_absent") +' "$tmp/rows.jsonl" >/dev/null +echo "estate gate census: hermetic cases passed" From 93ff14737e4ced822e1343b946a279a4191dcee7 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:45:38 +0000 Subject: [PATCH 03/10] ci: run estate gate census hermetic regression with squabble action tests --- .github/workflows/squabble-action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/squabble-action.yml b/.github/workflows/squabble-action.yml index 5393d82..00c27f6 100644 --- a/.github/workflows/squabble-action.yml +++ b/.github/workflows/squabble-action.yml @@ -67,6 +67,9 @@ jobs: GH_TOKEN: ${{ github.token }} run: bash tests/squabble_action_test.sh + - name: Estate gate census (read-only hermetic fixtures) + run: bash tests/estate_gate_census_test.sh + - name: Install, as the action's install step does id: install env: From c3dd740492681205d897043828a5438ef1239432 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:45:51 +0000 Subject: [PATCH 04/10] docs: document scope and unknown states of read-only estate census --- docs/ESTATE-GATE-CENSUS.adoc | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 docs/ESTATE-GATE-CENSUS.adoc diff --git a/docs/ESTATE-GATE-CENSUS.adoc b/docs/ESTATE-GATE-CENSUS.adoc new file mode 100644 index 0000000..dfb2192 --- /dev/null +++ b/docs/ESTATE-GATE-CENSUS.adoc @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: CC-BY-SA-4.0 += Read-only estate gate census + +The gate census inventories named Squabbler workflow files and probes the +corresponding GitHub Actions run and readable repository rulesets. It is +standalone: it does not call Hypatia and does not mutate GitHub repositories. + +== Running + +[source,shell] +---- +bash scripts/estate-gate-census.sh --repo example/project +bash scripts/estate-gate-census.sh --owner example --output /tmp/squabble-census.jsonl +bash tests/estate_gate_census_test.sh +---- + +The owner form uses paginated `gh repo list --limit 10000`, excluding +archived repositories and forks. Provide a `gh` identity with read access +to the repositories, workflow runs and rulesets, plus `jq`. + +== Reading results + +Each output line is JSON. `filename_absent` means no workflow *filename* +matches Squabble; it **does not** establish that no embedded integration +exists in an unrelated workflow file. `named_workflow_found` establishes +only that a candidate workflow exists. `run.status` is the latest +default-branch run status; `startup_failure` with `zero_jobs` is explicitly +not green. A successful run is not evidence that its job is required. + +`policy.rulesets` distinguishes `required_by_ruleset`, +`not_in_readable_rulesets` and `unknown`. Required-check comparison uses +the **actual emitted job name**, not the guessed name of a workflow file. +`policy.bypass` contains the effective-looking bypass actors from the +matching repository ruleset. Classic branch protection is not inspected +and is always marked `not_inspected`; an apparent absence from readable +rulesets must not be promoted into an assertion of absent enforcement. +GitHub access errors always produce an unknown state rather than a pass. + +== Known limits + +This is a bounded inventory instrument, not an authorisation to amend +branch protections, grant workflow access, merge pull requests or enable +autonomous repairs. It checks named workflow files rather than searching +every workflow body, uses the first page of each Actions job/ruleset query, +and inspects default-branch runs rather than all pull-request events. +For an estate-wide enforcement decision, follow up with full pagination, +classic-branch-protection reads using Administration:read, PR-event +producibility checks, check-source identity and a separate human-reviewed +ruleset diff. No filename or status count substitutes for those checks. + +The command exits nonzero if it cannot enumerate a requested owner; +per-repository access failure is retained as an `unknown` row to allow +the inventory to proceed and show gaps explicitly. From 267f6ed59454399d8b26be58dda899cab997b888 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:48:21 +0000 Subject: [PATCH 05/10] fix: do not assert ruleset absence on unreadable or paginated evidence --- scripts/estate-gate-census.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/estate-gate-census.sh b/scripts/estate-gate-census.sh index 1643288..d7d6342 100644 --- a/scripts/estate-gate-census.sh +++ b/scripts/estate-gate-census.sh @@ -69,8 +69,12 @@ sort -u "$tmp" | while IFS= read -r repo; do fi fi required=unknown; bypass=unknown - if [[ -n "$rules" ]] && jq -e 'type=="array"' <<<"$rules" >/dev/null; then - required=not_in_readable_rulesets; bypass='[]' + if [[ -n "$job" && -n "$rules" ]] && jq -e 'type=="array"' <<<"$rules" >/dev/null; then + # A single page is not a complete negative. Keep incomplete data explicit. + required=not_matched_in_readable_first_page; bypass='[]' + if [[ "$(jq -r 'length' <<<"$rules")" -ge 30 ]]; then + required=unknown; bypass=unknown + fi ids="$(jq -r '.[]|select(.enforcement=="active")|.id' <<<"$rules")" while IFS= read -r id; do [[ -n "$id" ]] || continue From 22d2be24972f44c81468f5806dae3f0132de4694 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:48:30 +0000 Subject: [PATCH 06/10] docs: distinguish partial ruleset inventory from confirmed absence --- docs/ESTATE-GATE-CENSUS.adoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/ESTATE-GATE-CENSUS.adoc b/docs/ESTATE-GATE-CENSUS.adoc index dfb2192..da742b7 100644 --- a/docs/ESTATE-GATE-CENSUS.adoc +++ b/docs/ESTATE-GATE-CENSUS.adoc @@ -28,7 +28,7 @@ default-branch run status; `startup_failure` with `zero_jobs` is explicitly not green. A successful run is not evidence that its job is required. `policy.rulesets` distinguishes `required_by_ruleset`, -`not_in_readable_rulesets` and `unknown`. Required-check comparison uses +`not_matched_in_readable_first_page` and `unknown`. Required-check comparison uses the **actual emitted job name**, not the guessed name of a workflow file. `policy.bypass` contains the effective-looking bypass actors from the matching repository ruleset. Classic branch protection is not inspected @@ -42,7 +42,9 @@ This is a bounded inventory instrument, not an authorisation to amend branch protections, grant workflow access, merge pull requests or enable autonomous repairs. It checks named workflow files rather than searching every workflow body, uses the first page of each Actions job/ruleset query, -and inspects default-branch runs rather than all pull-request events. +and inspects default-branch runs rather than all pull-request events. A +full first page of rulesets or a missing emitted job name is not a +verified absence of required checks. For an estate-wide enforcement decision, follow up with full pagination, classic-branch-protection reads using Administration:read, PR-event producibility checks, check-source identity and a separate human-reviewed From 6c92b40938bab080533e6e5ffb8ae5aebab0e8c6 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:49:40 +0000 Subject: [PATCH 07/10] fix: classify squabble action self-tests separately from gate candidates --- scripts/estate-gate-census.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/estate-gate-census.sh b/scripts/estate-gate-census.sh index d7d6342..8d26fce 100644 --- a/scripts/estate-gate-census.sh +++ b/scripts/estate-gate-census.sh @@ -53,6 +53,8 @@ sort -u "$tmp" | while IFS= read -r repo; do rules="$(api "repos/$repo/rulesets?includes_parents=true")" || rules="" while IFS= read -r name; do [[ -n "$name" ]] || continue + role=gate_candidate + if [[ "$name" == *squabble-action* ]]; then role=action_selftest; fi rid=""; status=unknown; job=""; job_status=unknown runs="$(api "repos/$repo/actions/workflows/$name/runs?branch=$ref&per_page=10")" || runs="" if [[ -n "$runs" ]] && jq -e '.workflow_runs|type=="array"' <<<"$runs" >/dev/null; then @@ -89,7 +91,7 @@ sort -u "$tmp" | while IFS= read -r repo; do fi emit "$(jq -nc --arg r "$repo" --arg wf "$name" --arg id "$rid" \ --arg state "$status" --arg job "$job" --arg js "$job_status" \ - --arg req "$required" --arg by "$bypass" \ - '{repo:$r,state:"named_workflow_found",workflow:$wf,run:{id:$id,status:$state,job:$job,job_conclusion:$js},policy:{rulesets:$req,bypass:$by,classic_protection:"not_inspected"}}')" + --arg req "$required" --arg by "$bypass" --arg role "$role" \ + '{repo:$r,state:"named_workflow_found",role:$role,workflow:$wf,run:{id:$id,status:$state,job:$job,job_conclusion:$js},policy:{rulesets:$req,bypass:$by,classic_protection:"not_inspected"}}')" done <<<"$names" done From 14c0f26a3d09b95074f86c5b192de342c5a9f828 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:49:48 +0000 Subject: [PATCH 08/10] docs: distinguish consumer-action self-tests from real gate candidates --- docs/ESTATE-GATE-CENSUS.adoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/ESTATE-GATE-CENSUS.adoc b/docs/ESTATE-GATE-CENSUS.adoc index da742b7..5f429f9 100644 --- a/docs/ESTATE-GATE-CENSUS.adoc +++ b/docs/ESTATE-GATE-CENSUS.adoc @@ -23,7 +23,9 @@ to the repositories, workflow runs and rulesets, plus `jq`. Each output line is JSON. `filename_absent` means no workflow *filename* matches Squabble; it **does not** establish that no embedded integration exists in an unrelated workflow file. `named_workflow_found` establishes -only that a candidate workflow exists. `run.status` is the latest +only that a candidate workflow exists. `role=action_selftest` distinguishes +Squabble installer/consumer tests from `role=gate_candidate`; neither +role alone establishes enforcement. `run.status` is the latest default-branch run status; `startup_failure` with `zero_jobs` is explicitly not green. A successful run is not evidence that its job is required. From d579ed4e41da641f2bb0d8017e66987d455aa5b9 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:50:00 +0000 Subject: [PATCH 09/10] test: self-test workflow must not masquerade as a required gate --- tests/estate_gate_census_test.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/estate_gate_census_test.sh b/tests/estate_gate_census_test.sh index 9556d91..60cbc87 100644 --- a/tests/estate_gate_census_test.sh +++ b/tests/estate_gate_census_test.sh @@ -10,12 +10,14 @@ cat > "$tmp/bin/gh" <<'MOCK' set -euo pipefail [[ "$1" == api ]] || exit 2 case "$2" in - repos/example/pass|repos/example/startup|repos/example/absent) + repos/example/pass|repos/example/startup|repos/example/absent|repos/example/selftest) printf '%s\n' '{"default_branch":"main","archived":false}';; 'repos/example/pass/contents/.github/workflows?ref=main') printf '%s\n' '[{"name":"cicd-squabbler.yml","type":"file"}]';; 'repos/example/startup/contents/.github/workflows?ref=main') printf '%s\n' '[{"name":"squabble.yml","type":"file"}]';; + 'repos/example/selftest/contents/.github/workflows?ref=main') + printf '%s\n' '[{"name":"squabble-action.yml","type":"file"}]';; 'repos/example/absent/contents/.github/workflows?ref=main') printf '%s\n' '[{"name":"ci.yml","type":"file"}]';; 'repos/example/pass/actions/workflows/cicd-squabbler.yml/runs?branch=main&per_page=10') @@ -24,6 +26,12 @@ case "$2" in printf '%s\n' '{"workflow_runs":[{"id":8,"conclusion":"startup_failure"}]}';; 'repos/example/pass/actions/runs/7/jobs?per_page=100') printf '%s\n' '{"jobs":[{"name":"Squabble chains (hard gate)","conclusion":"success"}]}';; + 'repos/example/selftest/actions/workflows/squabble-action.yml/runs?branch=main&per_page=10') + printf '%s\n' '{"workflow_runs":[{"id":9,"conclusion":"success"}]}';; + 'repos/example/selftest/actions/runs/9/jobs?per_page=100') + printf '%s\n' '{"jobs":[{"name":"squabble action test","conclusion":"success"}]}';; + 'repos/example/selftest/rulesets?includes_parents=true') + printf '%s\n' '[]';; 'repos/example/startup/actions/runs/8/jobs?per_page=100') printf '%s\n' '{"jobs":[]}';; 'repos/example/pass/rulesets?includes_parents=true') @@ -38,13 +46,14 @@ MOCK chmod +x "$tmp/bin/gh" bash -n "$root/scripts/estate-gate-census.sh" PATH="$tmp/bin:$PATH" bash "$root/scripts/estate-gate-census.sh" \ - --repo example/pass --repo example/startup --repo example/absent --output "$tmp/rows.jsonl" -[[ "$(wc -l < "$tmp/rows.jsonl")" -eq 3 ]] + --repo example/pass --repo example/startup --repo example/absent --repo example/selftest --output "$tmp/rows.jsonl" +[[ "$(wc -l < "$tmp/rows.jsonl")" -eq 4 ]] jq -e -s ' (map(select(.repo=="example/pass"))[0] | .run.job_conclusion=="success" and .policy.rulesets=="required_by_ruleset" and (.policy.bypass | contains("always"))) and (map(select(.repo=="example/startup"))[0] | .run.status=="startup_failure" and .run.job_conclusion=="zero_jobs") and - (map(select(.repo=="example/absent"))[0] | .state=="filename_absent") + (map(select(.repo=="example/absent"))[0] | .state=="filename_absent") and + (map(select(.repo=="example/selftest"))[0] | .role=="action_selftest") ' "$tmp/rows.jsonl" >/dev/null echo "estate gate census: hermetic cases passed" From 20b4baf54398d46149a2667d940cf941c1bb0c27 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Sat, 31 Oct 2026 19:01:16 +0000 Subject: [PATCH 10/10] Improve docstrings for functions changed by the pull request --- scripts/estate-gate-census.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/estate-gate-census.sh b/scripts/estate-gate-census.sh index 8d26fce..7bda001 100644 --- a/scripts/estate-gate-census.sh +++ b/scripts/estate-gate-census.sh @@ -18,6 +18,9 @@ while (($#)); do *) echo "Bad argument: $1" >&2; exit 2 ;; esac done +# api — print the gh API response when jq -e accepts it. +# Returns 1 on request or validation failure, including a final false or null +# value; callers convert these failures to unknown evidence. Write errors propagate. api() { local data data="$(gh api "$1" 2>/dev/null)" || return 1 @@ -25,6 +28,8 @@ api() { printf '%s\n' "$data" } if [[ -n "$out" ]]; then mkdir -p "$(dirname "$out")"; : > "$out"; fi +# emit — write a newline-terminated row to stdout or append it to $out +# when non-empty. Write errors propagate. emit() { if [[ -n "$out" ]]; then printf '%s\n' "$1" >> "$out" else printf '%s\n' "$1"; fi