From 76f399ff14fef7aaff0a6813ba5f8f0f17b437ce Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:46:11 +0900 Subject: [PATCH] fix(ci): remove stale hourly-pr-steward test left behind by #140 #140 removed .github/workflows/hourly-pr-steward.yml (superseded by the org-wide pr-review-merge-scheduler.yml, which already dispatches in real time on every PR event) but did not remove its own static contract test, which asserts on that workflow file's now-nonexistent content. The test fails closed with FileNotFoundError, breaking the required account-unification-tests check on main and on every PR -- including ones with no relation to the removal -- since GitHub's pull_request checkout tests against the current base branch, which already lacks the file even when a PR's own branch still has it. Also updates docs/operations/hourly-product-development.md, which still described the removed hourly steward alongside the surviving hourly-product-development.yml as if both ran on offset schedules. Verified: full account-unification suite passes (coverage 100%, ruff, interrogate, compileall, and the repository documentation contract test all clean). Co-Authored-By: Claude Sonnet 5 --- docs/operations/hourly-product-development.md | 9 ++- .../tests/test_hourly_pr_steward.py | 78 ------------------- 2 files changed, 6 insertions(+), 81 deletions(-) delete mode 100644 services/account_unification/tests/test_hourly_pr_steward.py diff --git a/docs/operations/hourly-product-development.md b/docs/operations/hourly-product-development.md index abb366a..8c25b07 100644 --- a/docs/operations/hourly-product-development.md +++ b/docs/operations/hourly-product-development.md @@ -1,12 +1,15 @@ # Hourly product-development loop Keyverse separates protected pull-request maintenance from autonomous product -development. The schedules are offset so the merge loop has time to settle the -repository before a new product slice is considered. +development. Protected PR maintenance (updating trusted PR branches, requiring +approval and required Checks, then arming exact-head auto-merge) is owned by +the organization's central `pr-review-merge-scheduler.yml`, which dispatches +in real time on every PR event rather than on an hourly schedule — Keyverse's +own former hourly steward workflow provided no security boundary beyond that +already-required central scheduler and was removed (#140). | Minute (UTC) | Workflow | Responsibility | | --- | --- | --- | -| `17 * * * *` | `hourly-pr-steward.yml` | Update trusted PR branches, require approval and required Checks, then arm exact-head auto-merge. | | `41 * * * *` | `hourly-product-development.yml` | When the PR queue is empty and exact `main` is healthy, use OpenCode with NVIDIA NIM to produce one bounded buyer-visible draft PR. | The development scheduler never approves or merges its own work and never diff --git a/services/account_unification/tests/test_hourly_pr_steward.py b/services/account_unification/tests/test_hourly_pr_steward.py deleted file mode 100644 index 910133e..0000000 --- a/services/account_unification/tests/test_hourly_pr_steward.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Static contract tests for the hourly protected PR steward.""" -from __future__ import annotations - -from pathlib import Path - - -def _workflow_source() -> str: - """Return the repository's hourly PR stewardship workflow source.""" - repository_root = Path(__file__).resolve().parents[3] - return ( - repository_root / ".github" / "workflows" / "hourly-pr-steward.yml" - ).read_text(encoding="utf-8") - - -def _permissions_block(source: str, marker: str, terminator: str) -> str: - """Return one indentation-sensitive workflow permissions block.""" - block_start = source.index(marker) - block_end = source.index(terminator, block_start) - return source[block_start:block_end] - - -def test_hourly_steward_runs_once_per_hour_with_bounded_concurrency() -> None: - """The schedule is hourly and overlapping steward runs are serialized.""" - workflow = _workflow_source() - assert 'cron: "17 * * * *"' in workflow - assert "group: hourly-pr-steward" in workflow - assert "cancel-in-progress: false" in workflow - assert "timeout-minutes: 10" in workflow - - -def test_hourly_steward_uses_read_only_workflow_token_defaults() -> None: - """Only the steward job receives its narrowly required write scopes.""" - workflow = _workflow_source() - top_level_permissions = _permissions_block( - workflow, - "permissions:\n", - "\nconcurrency:", - ) - job_permissions = _permissions_block( - workflow, - " permissions:\n", - " steps:", - ) - - assert "contents: read" in top_level_permissions - assert "write" not in top_level_permissions - assert "contents: write" in job_permissions - assert "pull-requests: write" in job_permissions - assert "checks: read" in job_permissions - assert "security-events: write" not in workflow - assert "actions: write" not in workflow - - -def test_hourly_steward_is_fail_closed_on_trust_review_and_checks() -> None: - """Untrusted, unapproved, pending, or failed pull requests remain untouched.""" - workflow = _workflow_source() - assert 'head_owner" != "ContextualWisdomLab"' in workflow - assert 'trusted_author" != "true"' in workflow - assert 'review_decision" != "APPROVED"' in workflow - assert 'gh pr checks "$number" --repo "$REPOSITORY" --required' in workflow - assert "--admin" not in workflow - - -def test_hourly_steward_invalidates_old_evidence_after_branch_update() -> None: - """A branch update exits the current iteration before merging stale evidence.""" - workflow = _workflow_source() - update_position = workflow.index("gh pr update-branch") - continue_position = workflow.index("continue", update_position) - approval_position = workflow.index('review_decision" != "APPROVED"') - assert update_position < continue_position < approval_position - - -def test_hourly_steward_binds_auto_merge_to_the_checked_head() -> None: - """GitHub auto-merge is armed only for the enumerated exact head SHA.""" - workflow = _workflow_source() - assert '--auto \\' in workflow - assert '--squash \\' in workflow - assert '--match-head-commit "$head_sha"' in workflow