From 620676d6ee0e16d9554a65b9159a54796c20853c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 12:37:04 +0000 Subject: [PATCH] fix(ci): inbox-steward-intake publishes through PRs and fails loudly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four defects in .github/workflows/inbox-steward-intake.yml: - update-ruleset appended " :: TODO: Apply rule update: …" text lines to .hypatia-baseline.json for every critical/high suggestion, corrupting the JSON the Governance baseline gate reads. It now only surfaces them as ::notice annotations; the suggestions file (applied: false) is unchanged. - Every commit and push to main was masked (`2>/dev/null || true`, `|| echo "::warning::…"`), so a refused write stayed green. Writes now go through scripts/open-data-pr.sh: a branch, a PR and a squash auto-merge (the estate-rescan.yml pattern), with every failure exiting non-zero and an empty change skipped by `git diff --cached --quiet`. - record-report committed only when inputs.dry_run == "false", but the input is empty on repository_dispatch, so every real dispatch ran as a dry run. Only an explicit "true" is dry now, matching update-ruleset. - The .git-private-farm dispatch discarded stderr and turned a refusal into a warning. It now fails the job. FARM_PAT needs Contents and Pull requests write on this repository, and auto-merge must be enabled. Previously the pushes went straight to main. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CYR5bf5FBLS7cXQK72aVNM --- .github/workflows/inbox-steward-intake.yml | 78 ++++++++-------------- scripts/open-data-pr.sh | 45 +++++++++++++ 2 files changed, 73 insertions(+), 50 deletions(-) create mode 100644 scripts/open-data-pr.sh diff --git a/.github/workflows/inbox-steward-intake.yml b/.github/workflows/inbox-steward-intake.yml index 1a893795..938119c0 100644 --- a/.github/workflows/inbox-steward-intake.yml +++ b/.github/workflows/inbox-steward-intake.yml @@ -63,6 +63,7 @@ jobs: env: EVENT_JSON: ${{ toJson(github.event) }} DRY_RUN: ${{ inputs.dry_run }} + GH_TOKEN: ${{ secrets.FARM_PAT }} run: | set -euo pipefail @@ -101,13 +102,14 @@ jobs: echo "validated_count=$VALIDATED" >> "$GITHUB_OUTPUT" echo "merged_count=$MERGED" >> "$GITHUB_OUTPUT" - # Commit the report - if [ "$DRY_RUN" = "false" ]; then + # Publish the report through a PR. `inputs.dry_run` is empty on + # repository_dispatch, so only an explicit 'true' is a dry run. + if [ "$DRY_RUN" != "true" ]; then git config user.name "Inbox Steward Intake" git config user.email "inbox-steward-intake@reposystem.dev" - git add "$REPORT_PATH" - git commit -m "inbox-steward: record from $SOURCE_REPO ($PR_COUNT PRs, $MERGED merged)" 2>/dev/null || true - git push origin HEAD 2>/dev/null || echo "::warning::Could not push report" + bash scripts/open-data-pr.sh report \ + "inbox-steward: record from $SOURCE_REPO ($PR_COUNT PRs, $MERGED merged)" \ + "$REPORT_PATH" else echo "::notice::Dry run - not committing report" fi @@ -234,12 +236,10 @@ jobs: id: update env: RULE_UPDATES: ${{ needs.analyze-patterns.outputs.rule_updates }} + GH_TOKEN: ${{ secrets.FARM_PAT }} run: | set -euo pipefail - - # Load existing baseline - BASELINE_PATH=".hypatia-baseline.json" - + # Create rule update file UPDATES_DIR="data/ruleset-updates" mkdir -p "$UPDATES_DIR" @@ -256,39 +256,19 @@ jobs: echo "Created rule updates: $UPDATE_FILE" - # Auto-apply critical/high priority updates - CRITICAL_UPDATES=$(echo "$RULE_UPDATES" | jq '[.[] | select(.priority == "critical" or .priority == "high")]') - - if [ "$(echo "$CRITICAL_UPDATES" | jq length)" -gt 0 ]; then - echo "Applying critical/high priority updates..." - - for UPDATE in $(echo "$CRITICAL_UPDATES" | jq -c '.[]'); do - UPDATE_TYPE=$(echo "$UPDATE" | jq -r '.type') - ACTION=$(echo "$UPDATE" | jq -r '.action') - REASON=$(echo "$UPDATE" | jq -r '.reason') - SUGGESTION=$(echo "$UPDATE" | jq -r '.suggestion') - - echo " - $UPDATE_TYPE: $ACTION ($REASON)" - - # Apply the update to baseline - # For now, append as a comment/todo - full automation needs more logic - echo " :: TODO: Apply rule update: $SUGGESTION" >> "$BASELINE_PATH" - done - - # Commit updates - git config user.name "Inbox Steward Intake" - git config user.email "inbox-steward-intake@reposystem.dev" - git add "$UPDATE_FILE" "$BASELINE_PATH" - git commit -m "inbox-steward: apply critical rule updates from steward reports" 2>/dev/null || true - git push origin HEAD 2>/dev/null || echo "::warning::Could not push updates" - else - echo "No critical/high priority updates to apply" - git config user.name "Inbox Steward Intake" - git config user.email "inbox-steward-intake@reposystem.dev" - git add "$UPDATE_FILE" - git commit -m "inbox-steward: record rule update suggestions" 2>/dev/null || true - git push origin HEAD 2>/dev/null || echo "::warning::Could not push updates" - fi + # Critical/high suggestions are surfaced, not applied. This step used + # to append " :: TODO: …" text lines to .hypatia-baseline.json, + # which corrupts the JSON the Governance baseline gate reads. The + # suggestions live only in $UPDATE_FILE (`applied: false`). + echo "$RULE_UPDATES" \ + | jq -r '.[] | select(.priority == "critical" or .priority == "high") + | "::notice title=Rule update suggested (\(.priority))::\(.type): \(.action) (\(.reason)) — \(.suggestion)"' + + git config user.name "Inbox Steward Intake" + git config user.email "inbox-steward-intake@reposystem.dev" + bash scripts/open-data-pr.sh rule-updates \ + "inbox-steward: record rule update suggestions" \ + "$UPDATE_FILE" # Job 4: Dispatch to .git-private-farm for fleet-wide application dispatch-to-farm: @@ -333,14 +313,12 @@ jobs: echo "Dispatching to .git-private-farm: $(echo "$PAYLOAD" | jq -c '.')" - # Send to .git-private-farm - if gh api -X POST \ - "/repos/hyperpolymath/.git-private-farm/dispatches" \ - --input - <<<"$PAYLOAD" >/dev/null 2>&1; then - echo "✅ Successfully dispatched to .git-private-farm" - else - echo "::warning::Failed to dispatch to .git-private-farm" - fi + # Send to .git-private-farm. A refused dispatch fails the job; it + # used to be swallowed into a warning with stderr discarded. + gh api -X POST \ + "/repos/hyperpolymath/.git-private-farm/dispatches" \ + --input - <<<"$PAYLOAD" >/dev/null + echo "✅ Successfully dispatched to .git-private-farm" # Job 5: Summary summary: diff --git a/scripts/open-data-pr.sh b/scripts/open-data-pr.sh new file mode 100644 index 00000000..80b3aeb5 --- /dev/null +++ b/scripts/open-data-pr.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# open-data-pr.sh — land machine-written data files on main through a PR +# +# Usage: open-data-pr.sh <path>... +# +# Commits <path>... on a new branch `inbox-steward/<run_id>-<suffix>`, pushes +# it, opens a PR against main and queues a squash auto-merge. Used by +# inbox-steward-intake.yml instead of `git push origin HEAD` to main, which +# bypassed review and signing and was masked so a refusal stayed green. +# +# Needs: GH_TOKEN (a credential with Contents and Pull requests write on this +# repository), GITHUB_RUN_ID, GITHUB_REPOSITORY, and a checkout whose `origin` +# authenticates with the same credential. Every failure exits non-zero. +set -euo pipefail + +if [ "$#" -lt 3 ]; then + echo "usage: $0 <branch-suffix> <title> <path>..." >&2 + exit 2 +fi + +suffix="$1" +title="$2" +shift 2 + +git add -- "$@" +if git diff --cached --quiet; then + echo "No changes under $* — nothing to publish." + exit 0 +fi + +branch="inbox-steward/${GITHUB_RUN_ID}-${suffix}" +git switch -c "$branch" +git commit -m "$title" +git push origin "HEAD:refs/heads/${branch}" + +pr_url="$(gh pr create --repo "$GITHUB_REPOSITORY" --base main --head "$branch" \ + --title "$title" \ + --body "Machine-written by inbox-steward-intake.yml, run ${GITHUB_RUN_ID}. Data files only.")" +echo "opened ${pr_url}" + +# --auto queues the merge until the base-branch policy allows it; a bare +# merge right after creation is refused before the policy settles. +gh pr merge "${pr_url##*/}" --repo "$GITHUB_REPOSITORY" --squash --auto --delete-branch +echo "auto-merge queued for ${pr_url}"