Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
e1c1efc
fix(automation): harden hourly maintenance credentials
seonghobae Aug 7, 2026
45ec9ff
ci: verify exact pull-request source head
seonghobae Aug 7, 2026
34c237c
test(ci): bind exact-head contract to every checkout
seonghobae Aug 7, 2026
a36e511
test(automation): preserve scheduler credential boundary
seonghobae Aug 7, 2026
a01f259
docs(adr): record exact-head maintenance boundary
seonghobae Aug 7, 2026
c6c5acb
docs(plan): record exact-head CI reconciliation
seonghobae Aug 7, 2026
6ab4e81
docs(changelog): record maintenance and exact-head CI hardening
seonghobae Aug 7, 2026
62fb2f2
test(automation): require current central scheduler head
seonghobae Aug 7, 2026
2836ead
fix(automation): pin current central scheduler prerequisite
seonghobae Aug 7, 2026
b393132
test(automation): align hourly scheduler governance pin
seonghobae Aug 7, 2026
b3a8e01
docs(automation): record current scheduler prerequisite
seonghobae Aug 7, 2026
07bb189
test(automation): require current central scheduler head
seonghobae Aug 7, 2026
8478fbf
test(automation): align scheduler governance identity
seonghobae Aug 7, 2026
8289254
fix(automation): pin current central scheduler head
seonghobae Aug 7, 2026
c25b852
docs(automation): record latest scheduler drift TDD
seonghobae Aug 7, 2026
76c9e86
test(automation): require current scheduler prerequisite
seonghobae Aug 7, 2026
f142a29
fix(automation): pin current scheduler prerequisite
seonghobae Aug 7, 2026
6738aa6
test(automation): align scheduler governance contract
seonghobae Aug 7, 2026
ef427be
docs(automation): record current scheduler drift TDD
seonghobae Aug 7, 2026
ec3c78c
test(automation): require current central scheduler head
seonghobae Aug 8, 2026
0f9dd41
fix(automation): track current central scheduler prerequisite
seonghobae Aug 8, 2026
6eb9a14
refactor(automation): align scheduler pin contract
seonghobae Aug 8, 2026
5729064
docs(automation): record latest scheduler drift TDD
seonghobae Aug 8, 2026
0e39bcf
test(automation): require current central scheduler head
seonghobae Aug 8, 2026
78b2df5
fix(automation): pin current central scheduler head
seonghobae Aug 8, 2026
eea7f9a
refactor(automation): align duplicate scheduler pin contract
seonghobae Aug 8, 2026
6ef1dd6
test(automation): require queued hourly recovery runs
seonghobae Aug 8, 2026
d689750
fix(automation): queue hourly recovery runs
seonghobae Aug 8, 2026
9074855
test(automation): require RCA feasibility contract
seonghobae Aug 8, 2026
07b6535
docs(automation): require RCA and scoped lease recovery
seonghobae Aug 8, 2026
467532c
docs(automation): record scheduler recovery TDD
seonghobae Aug 8, 2026
12db948
docs(changelog): record resilient hourly scheduler
seonghobae Aug 8, 2026
63c0def
test(automation): require OIDC fallback permission
seonghobae Aug 8, 2026
46e181b
fix(automation): enable short-lived OIDC fallback
seonghobae Aug 8, 2026
0fcf441
test(automation): parse OIDC permissions structurally
seonghobae Aug 8, 2026
94b4ee3
test(automation): require realistic OIDC fallback contract
seonghobae Aug 8, 2026
9b172f8
docs(automation): record realistic OIDC repair fallback
seonghobae Aug 8, 2026
1b52ebd
docs(changelog): record OIDC repair feasibility
seonghobae Aug 8, 2026
96aaaae
docs(plan): record OIDC feasibility cycle
seonghobae Aug 8, 2026
97e8e8e
test(automation): require latest green central scheduler pin
seonghobae Aug 8, 2026
a3e6f77
fix(automation): repin review repair to current green scheduler
seonghobae Aug 8, 2026
91eb58e
docs(automation): record current green central prerequisite
seonghobae Aug 8, 2026
322d60d
test(automation): align workflow contract with current scheduler pin
seonghobae Aug 8, 2026
85438c6
refactor(automation): isolate exact-source CI from scheduler hardening
seonghobae Aug 9, 2026
1ae60f3
refactor(automation): remove duplicated exact-source CI contract
seonghobae Aug 9, 2026
821caea
docs(automation): remove duplicated exact-source changelog scope
seonghobae Aug 9, 2026
8a44cd9
docs(automation): scope scheduler ADR away from exact-source CI
seonghobae Aug 9, 2026
2ffcebf
docs(automation): separate exact-source CI from scheduler plan
seonghobae Aug 9, 2026
ce35185
fix(automation): preserve OIDC documentation contract after scope split
seonghobae Aug 9, 2026
9553f88
test(automation): require current central scheduler candidate
seonghobae Aug 11, 2026
e535bf9
fix(automation): track current central scheduler candidate
seonghobae Aug 11, 2026
b27c0c4
fix(tests): track current scheduler candidate
seonghobae Aug 11, 2026
fec17e2
chore(automation): reconcile hourly lane with current main
seonghobae Aug 17, 2026
63c8bf3
test(automation): require current OIDC scheduler boundary
seonghobae Aug 17, 2026
55e8a36
fix(automation): adopt current OIDC scheduler contracts
seonghobae Aug 17, 2026
e3e6cc5
test(automation): track current central scheduler identity
seonghobae Aug 17, 2026
efe766e
merge: stack hourly maintenance repair on #233
seonghobae Aug 19, 2026
ea5b0b9
chore(stack): reconcile hourly maintenance with current #233
seonghobae Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions .github/workflows/hourly-maintenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

concurrency:
group: hourly-commercial-maintenance-${{ github.repository }}
cancel-in-progress: true
cancel-in-progress: false

permissions:
contents: read
Expand All @@ -16,20 +16,15 @@ jobs:
review-fix:
name: Repair actionable pull-request feedback
permissions:
actions: write
contents: read
issues: write
pull-requests: read
statuses: read
uses: ContextualWisdomLab/.github/.github/workflows/pr-review-fix-scheduler.yml@5983b41ace75040c1d81818171ca7d0f3653254e # main
id-token: write
uses: ContextualWisdomLab/.github/.github/workflows/pr-review-fix-scheduler.yml@c47afc2dc68488292c1db7c9d6f82dcd5360f181 # protected central main
with:
target_repository: ContextualWisdomLab/pg-llm-batch
base_branch: main
max_prs: "100"
max_dispatches: "1"
retry_hours: "1"
canonical_ref: 5983b41ace75040c1d81818171ca7d0f3653254e
secrets: inherit

review-merge:
name: Revalidate and merge eligible pull requests
Expand All @@ -41,7 +36,7 @@ jobs:
contents: write
id-token: write
pull-requests: write
uses: ContextualWisdomLab/.github/.github/workflows/pr-review-merge-scheduler.yml@5983b41ace75040c1d81818171ca7d0f3653254e # main
uses: ContextualWisdomLab/.github/.github/workflows/pr-review-merge-scheduler.yml@c47afc2dc68488292c1db7c9d6f82dcd5360f181 # protected central main
with:
base_branch: main
max_prs: "100"
Expand All @@ -53,4 +48,3 @@ jobs:
update_branches: true
stale_opencode_minutes: "90"
project_flow: github-flow
secrets: inherit
91 changes: 91 additions & 0 deletions tests/test_hourly_maintenance_credential_boundary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# SPDX-License-Identifier: Apache-2.0
"""Contracts for the hourly maintenance scheduler trust boundary."""

from __future__ import annotations

from pathlib import Path


WORKFLOW_PATH = Path(".github/workflows/hourly-maintenance.yml")
CENTRAL_SCHEDULER_SHA = "c47afc2dc68488292c1db7c9d6f82dcd5360f181"


def _job_block(workflow: str, job_name: str, next_job_name: str | None) -> str:
"""Return one exact top-level job block from the hourly workflow."""
start = workflow.index(f" {job_name}:\n")
if next_job_name is None:
return workflow[start:]
end = workflow.index(f" {next_job_name}:\n", start)
return workflow[start:end]


def test_hourly_scheduler_preserves_cadence_without_cancelling_active_work() -> None:
"""A later hourly trigger must not cancel the maintenance run already executing."""
workflow = WORKFLOW_PATH.read_text(encoding="utf-8")

assert 'cron: "17 * * * *"' in workflow
assert "group: hourly-commercial-maintenance-${{ github.repository }}" in workflow
assert "cancel-in-progress: false" in workflow
assert "cancel-in-progress: true" not in workflow
assert "queue:" not in workflow


def test_both_scheduler_calls_use_one_current_immutable_central_identity() -> None:
"""Repair and merge planes must consume the same protected central source."""
workflow = WORKFLOW_PATH.read_text(encoding="utf-8")

assert (
"uses: ContextualWisdomLab/.github/.github/workflows/"
f"pr-review-fix-scheduler.yml@{CENTRAL_SCHEDULER_SHA}"
in workflow
)
assert (
"uses: ContextualWisdomLab/.github/.github/workflows/"
f"pr-review-merge-scheduler.yml@{CENTRAL_SCHEDULER_SHA}"
in workflow
)
assert workflow.count(f"@{CENTRAL_SCHEDULER_SHA}") == 2
assert "canonical_ref:" not in workflow


def test_review_fix_uses_oidc_without_forwarding_repository_secrets() -> None:
"""The repair caller grants OIDC but no stored repository mutation secret."""
workflow = WORKFLOW_PATH.read_text(encoding="utf-8")
review_fix = _job_block(workflow, "review-fix", "review-merge")

assert "\n permissions:\n" in review_fix
permissions = review_fix.split("\n permissions:\n", 1)[1].split("\n uses:", 1)[0]
permission_lines = {
line.strip() for line in permissions.splitlines() if line.strip()
}
assert permission_lines == {"contents: read", "id-token: write"}
assert "secrets:" not in review_fix
assert "PR_REVIEW_MERGE_TOKEN" not in review_fix
assert "OPENCODE_APPROVE_TOKEN" not in review_fix
assert "COPILOT_GITHUB_TOKEN" not in review_fix
assert "NVIDIA_NIM_API_KEY" not in review_fix


def test_merge_scheduler_keeps_reviewed_mutation_permissions_without_secrets() -> None:
"""The merge caller keeps bounded token permissions and relies on OIDC/App authority."""
workflow = WORKFLOW_PATH.read_text(encoding="utf-8")
review_merge = _job_block(workflow, "review-merge", None)

required_permissions = {
"actions: write",
"checks: read",
"contents: write",
"id-token: write",
"pull-requests: write",
}
permissions = review_merge.split("\n permissions:\n", 1)[1].split(
"\n uses:", 1
)[0]
permission_lines = {
line.strip() for line in permissions.splitlines() if line.strip()
}
assert permission_lines == required_permissions
assert "secrets:" not in review_merge
assert "merge_mode: direct_or_auto" in review_merge
assert "enable_auto_merge: true" in review_merge
assert "update_branches: true" in review_merge
9 changes: 5 additions & 4 deletions tests/test_workflow_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,25 +197,26 @@ def test_ci_checks_out_and_verifies_the_exact_source_head_in_every_job() -> None

def test_hourly_workflow_repairs_revalidates_and_merges_pull_requests() -> None:
workflow = _read(".github/workflows/hourly-maintenance.yml")
scheduler_sha = "5983b41ace75040c1d81818171ca7d0f3653254e"
scheduler_sha = "c47afc2dc68488292c1db7c9d6f82dcd5360f181"

assert 'cron: "17 * * * *"' in workflow
assert "workflow_dispatch:" in workflow
assert (
"uses: ContextualWisdomLab/.github/.github/workflows/"
"pr-review-fix-scheduler.yml@"
f"pr-review-fix-scheduler.yml@{scheduler_sha}"
) in workflow
assert "target_repository: ContextualWisdomLab/pg-llm-batch" in workflow
assert 'retry_hours: "1"' in workflow
assert f"canonical_ref: {scheduler_sha}" in workflow
assert "canonical_ref:" not in workflow
assert (
"uses: ContextualWisdomLab/.github/.github/workflows/"
"pr-review-merge-scheduler.yml@"
f"pr-review-merge-scheduler.yml@{scheduler_sha}"
) in workflow
assert "merge_mode: direct_or_auto" in workflow
assert "trigger_reviews: true" in workflow
assert "enable_auto_merge: true" in workflow
assert "update_branches: true" in workflow
assert "secrets: inherit" not in workflow
assert workflow.count(f"@{scheduler_sha}") == 2
_assert_external_actions_are_pinned(workflow)

Expand Down