process: #311 H11 rebase-hook + PR-scoped format CI + #312 H12 qualification audit #313

Merged
timothy merged 3 commits from ci/311-rebase-hook-format-ci into main 2026-07-12 20:32:19 +02:00
Owner

fixes #311
fixes #312

Extends the #303 hook program with H11, H12, and a PR-scoped format CI gate — all prompted by the #309 session, where a stale branch merged main in, dragged legacy-BOM .cs it never touched into the commit, and the pre-commit format hook then blocked on code that wasn't the author's.

H11 — rebase-before-push hook (#311)

.claude/hooks/prepush-rebase-check.sh, wired into .husky/pre-push: blocks a push whose branch is behind origin/main with git rebase origin/main guidance. Rebasing keeps your diff to what you touched; merging main in drags in others' files. Fail-open (offline / no origin/main / not-a-repo → allow, since a git hook has no "ask"); escape ETV_SKIP_REBASE_CHECK=1. Dogfooded live: this very branch was behind main after #309 merged, H11 blocked, I rebased, then the push passed.

PR-scoped format CI job (#311, the "CI never checks charset" fix)

New blocking job runs dotnet format --verify-no-changes scoped to the PR's changed .cs (vs merge-base) — enforcing fix-formatting-as-you-touch (style + charset=utf-8/no-BOM) without a big-bang reformat of the ~2500 legacy BOM files. A .cs-free PR (like this one) skips the expensive steps and passes trivially → always reports a status (safe as a required check). Closes the gap that let #269 land 17 BOM files (#310), since the pre-commit hook alone is --no-verify-bypassable.

H12 — session-end issue-qualification audit (#312)

scripts/issue-qualification-audit.sh: lists OPEN issues missing a priority: label (the #237 ranking keys off priority/gate labels, so an unlabeled issue is invisible to it). Milestone deliberately NOT required (backlog is unmilestoned). Advisory (exit 1 if any unqualified); fail-open without creds. Wired into the kickoff session-end protocol. Tested live (flagged #237, #22) + no-creds no-op.

Docs (same PR)

  • docs/contributing.md §7 — the fix-as-you-touch convention.
  • docs/handoffs/chicorytv-issue-queue.md — lore flipped from "merge main in" → "rebase on main" (H11); H12 added to the session-end step + a lore bullet.
  • docs/decisions.md — new entry (+ TOC) for the formatting-as-you-touch + rebase-not-merge decisions.

Verification

bash -n + shellcheck clean on both scripts; H11 pipe-tested (behind→block, current→allow, escape hatch, fail-open); H12 run against live Gitea + no-creds path; workflow YAML parses. Not docs-only → the H6/H10 merge gate applies (Review-verdict comment to follow after review).

🤖 Generated with Claude Code

fixes #311 fixes #312 Extends the #303 hook program with **H11**, **H12**, and a **PR-scoped format CI gate** — all prompted by the #309 session, where a stale branch *merged main in*, dragged legacy-BOM `.cs` it never touched into the commit, and the pre-commit format hook then blocked on code that wasn't the author's. ## H11 — rebase-before-push hook (#311) `.claude/hooks/prepush-rebase-check.sh`, wired into `.husky/pre-push`: blocks a push whose branch is **behind `origin/main`** with `git rebase origin/main` guidance. Rebasing keeps your diff to what you touched; merging main in drags in others' files. Fail-open (offline / no origin/main / not-a-repo → allow, since a git hook has no "ask"); escape `ETV_SKIP_REBASE_CHECK=1`. **Dogfooded live**: this very branch was behind main after #309 merged, H11 blocked, I rebased, then the push passed. ## PR-scoped `format` CI job (#311, the "CI never checks charset" fix) New blocking job runs `dotnet format --verify-no-changes` **scoped to the PR's changed `.cs`** (vs merge-base) — enforcing *fix-formatting-as-you-touch* (style + `charset=utf-8`/no-BOM) **without** a big-bang reformat of the ~2500 legacy BOM files. A `.cs`-free PR (like this one) skips the expensive steps and passes trivially → always reports a status (safe as a required check). Closes the gap that let #269 land 17 BOM files (#310), since the pre-commit hook alone is `--no-verify`-bypassable. ## H12 — session-end issue-qualification audit (#312) `scripts/issue-qualification-audit.sh`: lists OPEN issues missing a `priority:` label (the #237 ranking keys off priority/gate labels, so an unlabeled issue is invisible to it). Milestone deliberately NOT required (backlog is unmilestoned). Advisory (exit 1 if any unqualified); fail-open without creds. Wired into the kickoff session-end protocol. **Tested live** (flagged #237, #22) + no-creds no-op. ## Docs (same PR) - `docs/contributing.md` §7 — the fix-as-you-touch convention. - `docs/handoffs/chicorytv-issue-queue.md` — lore flipped from "merge main in" → "rebase on main" (H11); H12 added to the session-end step + a lore bullet. - `docs/decisions.md` — new entry (+ TOC) for the formatting-as-you-touch + rebase-not-merge decisions. ## Verification `bash -n` + shellcheck clean on both scripts; H11 pipe-tested (behind→block, current→allow, escape hatch, fail-open); H12 run against live Gitea + no-creds path; workflow YAML parses. Not docs-only → the H6/H10 merge gate applies (Review-verdict comment to follow after review). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
timothy added 3 commits 2026-07-12 20:22:12 +02:00
- H11: .husky/pre-push calls .claude/hooks/prepush-rebase-check.sh, which blocks a
  push whose branch is behind origin/main (rebase first; do not merge main in — a
  merge drags in files you didn't touch, e.g. legacy-BOM .cs, tripping the format
  hook on code that isn't yours). Fail-open; escape ETV_SKIP_REBASE_CHECK=1.
- New blocking `format` CI job: dotnet format --verify-no-changes scoped to the
  PR's changed .cs only (style + charset=utf-8/no-BOM), enforcing fix-as-you-touch
  without a big-bang reformat of the ~2500 legacy BOM files. .cs-free PRs skip and
  pass (always reports a status). Closes the "CI never checks charset" gap that let
  #269 land 17 BOM files (#310).

Docs (contributing.md §7 / decisions.md / lore) follow in the next commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- contributing.md §7: document fix-formatting-as-you-touch (normalize a legacy file you
  edit, incl. BOM strip; no big-bang), enforced by the pre-commit hook + the new format CI job.
- kickoff lore: flip "merge main into PR branch" -> "rebase on origin/main" (H11 enforces it);
  keep the regenerate-generated-artifacts-on-conflict nugget.
- decisions.md: new entry (+ TOC) for both decisions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(process): #312 H12 — session-end issue-qualification audit
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 7s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 7s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 6s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m31s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m9s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
f1b2521228
scripts/issue-qualification-audit.sh: lists OPEN issues missing a `priority:` label
(the #237 ranking keys off priority:/gate labels, so an unlabeled issue is invisible
to it). "Fully qualified" = has a priority: label; gate-vs-backlog derives from the
review label / milestone, and a milestone is NOT required (backlog is unmilestoned).
Advisory (exit 1 if any unqualified); fail-open without Gitea creds. Wired into the
kickoff session-end protocol + a lore bullet. Tested live (flagged 2) + no-creds no-op.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Owner

Review-verdict: MERGEABLE @ f1b25212

Adversarial review (fork, scoped) verified all three artifacts empirically, not just by reading:

  • H11: ancestor logic correct across 9 cases (equal/ahead/rebased → allow; behind/diverged → block; offline/no-origin/not-a-repo → fail-open; escape hatch). No false-block of a genuinely-current push.
  • format CI job: changed-file detection mirrors the proven api-docs/docs-reminder pattern; skip-path passes trivially (safe as required check); --no-restore valid; migration-file false-fail neutralized (dotnet format skips <auto-generated> files, so BOM'd EF migrations don't trip it).
  • H12: jq label logic confirmed with crafted inputs (null/[]/PR/literal-prefix); pagination terminates; fail-open verified; live run flags #237/#22.

LOW by-design notes, none blocking: H11 keys off HEAD (not the pushed ref) — harmless, escape hatch covers the rare --delete; the format job verifies whole touched files (intended §7 "normalize when you touch it" friction).

Accepted deferral: the format job's active fail path (a touched .cs with a BOM → red) is logic-verified but not live-exercised by this PR (it touches no .cs, so only the skip→green path runs in this PR's CI). The next .cs-touching PR exercises it for real; #310 (de-BOM the 17 files) is a natural first exercise.

Review-verdict: MERGEABLE @ f1b25212 Adversarial review (fork, scoped) verified all three artifacts empirically, not just by reading: - **H11**: ancestor logic correct across 9 cases (equal/ahead/rebased → allow; behind/diverged → block; offline/no-origin/not-a-repo → fail-open; escape hatch). No false-block of a genuinely-current push. - **format CI job**: changed-file detection mirrors the proven `api-docs`/`docs-reminder` pattern; skip-path passes trivially (safe as required check); `--no-restore` valid; **migration-file false-fail neutralized** (`dotnet format` skips `<auto-generated>` files, so BOM'd EF migrations don't trip it). - **H12**: jq label logic confirmed with crafted inputs (null/[]/PR/literal-prefix); pagination terminates; fail-open verified; live run flags #237/#22. LOW by-design notes, none blocking: H11 keys off HEAD (not the pushed ref) — harmless, escape hatch covers the rare `--delete`; the format job verifies whole touched files (intended §7 "normalize when you touch it" friction). **Accepted deferral**: the format job's active *fail* path (a touched `.cs` with a BOM → red) is logic-verified but not live-exercised by this PR (it touches no `.cs`, so only the skip→green path runs in this PR's CI). The next `.cs`-touching PR exercises it for real; #310 (de-BOM the 17 files) is a natural first exercise.
timothy merged commit 9b3b3963fe into main 2026-07-12 20:32:19 +02:00
timothy deleted branch ci/311-rebase-hook-format-ci 2026-07-12 20:32:20 +02:00
Sign in to join this conversation.