Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7f9f9f0
fix: grant hourly callers reusable workflow OIDC scope
seonghobae Aug 21, 2026
07cdefc
docs: complete coordinator client docstring
seonghobae Aug 21, 2026
82cd117
test(ci): close hourly contract docstring gap
seonghobae Aug 21, 2026
f05c9b2
docs(test): complete coordinator fixture docstrings
seonghobae Aug 21, 2026
4ceb9f6
Merge branch 'main' into fix/hourly-reusable-oidc
opencode-agent[bot] Aug 21, 2026
19cc340
Merge branch 'main' into codex/central-docstring-coverage
opencode-agent[bot] Aug 21, 2026
a040740
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 21, 2026
3ab34b5
Merge remote-tracking branch 'origin/fix/hourly-reusable-oidc' into HEAD
seonghobae Aug 21, 2026
a625505
Merge branch 'main' into codex/central-docstring-coverage
opencode-agent[bot] Aug 21, 2026
97b14e8
fix(ci): refresh pip audit lock
seonghobae Aug 21, 2026
1e3b6e7
docs: record pip audit lock refresh
seonghobae Aug 21, 2026
b39a9c4
Merge remote-tracking branch 'origin/main' into fix/hourly-reusable-oidc
seonghobae Aug 21, 2026
cb1ed72
chore(ci): keep OIDC repair scope focused
seonghobae Aug 21, 2026
5838e0a
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 21, 2026
7c131ad
fix(ci): retain secure pip audit lock
seonghobae Aug 21, 2026
6c6b740
docs: correct Clearfolio concurrency contract
seonghobae Aug 21, 2026
f1f639e
Merge remote-tracking branch 'refs/remotes/origin/pr1189-current' int…
seonghobae Aug 21, 2026
bf2bce1
Merge branch 'main' into fix/hourly-reusable-oidc
opencode-agent[bot] Aug 21, 2026
f2f79b9
test(ci): align scheduler contract assertion
seonghobae Aug 21, 2026
bce92b8
Merge branch 'main' into fix/hourly-reusable-oidc
opencode-agent[bot] Aug 21, 2026
89cc59b
fix(ci): align scheduler cancellation contract
seonghobae Aug 21, 2026
3e2dc26
Merge remote-tracking branch 'origin/fix/hourly-reusable-oidc' into r…
seonghobae Aug 21, 2026
2c05f05
Merge main into hourly OIDC branch
seonghobae Aug 21, 2026
1a0cc1f
Merge branch 'main' into fix/hourly-reusable-oidc
opencode-agent[bot] Aug 21, 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
5 changes: 5 additions & 0 deletions .github/workflows/clearfolio-hourly-review-repair.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ permissions:

jobs:
dispatch-review-repair:
# The reusable scheduler exchanges a GitHub OIDC token for its established
# OpenCode app credential; reusable workflows cannot elevate this permission.
permissions:
contents: read
id-token: write
uses: ./.github/workflows/pr-review-fix-scheduler.yml
with:
target_repository: ContextualWisdomLab/clearfolio
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/disksage-hourly-review-repair.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ permissions:

jobs:
dispatch-review-repair:
# The reusable scheduler exchanges a GitHub OIDC token for its established
# OpenCode app credential; reusable workflows cannot elevate this permission.
permissions:
contents: read
id-token: write
Comment thread
seonghobae marked this conversation as resolved.
uses: ./.github/workflows/pr-review-fix-scheduler.yml
with:
target_repository: ContextualWisdomLab/disksage
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Refresh the hash-pinned `pip` used by the Python security audit from 26.1.2 to
26.2.1 so the audit no longer installs the vulnerable PYSEC-2026-3721 release.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

- Refused PR Review Merge Scheduler head mutations, `update-branch` and the last-push approval head restamp, whenever the resolved mutation credential is the workflow `GITHUB_TOKEN`. GitHub starts no workflow run for events created with that credential, so the moved head collected no current-head required checks and the PR stayed permanently `BLOCKED` with a `github-actions[bot]` merge commit that no later scheduler run could repair, because the branch was no longer behind. The scheduler now waits with `head_mutation_credential_upgrade` guidance naming `PR_REVIEW_MERGE_TOKEN`, `OPENCODE_APPROVE_TOKEN`, and the OpenCode app token exchange.
- Parsed `opencode.jsonc` as JSONC (stripping `//` and `/* */` comments outside string literals) in the reasoning-effort guard and its contract tests, instead of raw `json.loads`, which rejected the file the moment it carried its first explanatory comment (added for the `contextual-orchestrator` provider block) with `Expecting property name enclosed in double quotes`. Comment markers inside string values, such as the `$schema` URL, are left untouched.
- Download the pinned `uv` 0.12.1 exporter from the official GitHub Releases URL instead of `releases.astral.sh`, which now returns HTTP 403 and blocks org-wide OpenCode `coverage-evidence`. The SHA-256 pin is unchanged. The opener may follow one hop onto `release-assets.githubusercontent.com` or `objects.githubusercontent.com` and still rejects every other host, userinfo, non-HTTPS scheme, and nondefault port (ContextualWisdomLab/.github#1109).
Expand Down
21 changes: 13 additions & 8 deletions docs/doctoring/clearfolio-hourly-review-caller.md
Comment thread
seonghobae marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ max_dispatches: "1"
retry_hours: "1"
```

The caller and reusable engine both use `cancel-in-progress: true`. This keeps
queue inspection single-flight at the product and engine boundaries. At most one
autofix dispatch is issued during an invocation, and the same exact PR head is
not retried more than once per hour.
The caller uses `cancel-in-progress: false`, while the reusable engine applies
its own event-specific concurrency policy. This preserves each hourly handoff
long enough for the reusable scheduler to inspect the queue, while the engine
still prevents duplicate work for the same target event. At most one autofix
dispatch is issued during an invocation, and the same exact PR head is not
retried more than once per hour.

## Modular MSA contract

Expand Down Expand Up @@ -65,9 +67,11 @@ It does not use `secrets: inherit`. It does not receive
execution. The NVIDIA credential is bound only inside the separately reviewed
`PR Review Autofix` workflow's two OpenCode execution steps.

Both the caller and reusable scheduler keep the workflow-generated
`GITHUB_TOKEN` read-only with only `contents: read`; neither declares job-level
write elevation. Cross-repository PR inspection, acknowledgement, workflow
The caller keeps the workflow-generated `GITHUB_TOKEN` read-only with
`contents: read` and grants only job-scoped `id-token: write` so the reusable
scheduler can exchange the signed GitHub OIDC token for its established
OpenCode app credential. The caller does not grant repository, pull-request, or
workflow write permissions. Cross-repository PR inspection, acknowledgement, workflow
dispatch, and branch updates are authorized only through the explicitly mapped
`PR_REVIEW_MERGE_TOKEN` or `OPENCODE_APPROVE_TOKEN`, exposed to the scheduler as
`GH_TOKEN`. The scheduler has no `github.token` fallback. Missing credentials
Expand Down Expand Up @@ -104,7 +108,8 @@ Permanent tests require all of the following:
binding are absent from the caller;
9. the focused exact-head contract workflow reruns whenever the caller changes;
10. the caller and reusable scheduler retain read-only workflow-token
permissions, declare no job-level write elevation, and contain no
permissions, the caller declares only job-scoped `id-token: write` for the
OIDC exchange, and contain no
`github.token` mutation fallback.

Repository acceptance still requires current-head workflow, security,
Expand Down
6 changes: 4 additions & 2 deletions docs/doctoring/disksage-hourly-review-caller.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ controls every mutation and merge decision.

## Credential and model boundary

The queue-scanning caller has only `contents: read`. It maps only the established
The queue-scanning caller keeps the workflow token at `contents: read` and grants
only job-scoped `id-token: write`, which is required for the reusable scheduler's
OIDC-to-OpenCode app-token exchange. It maps only the established
`PR_REVIEW_MERGE_TOKEN` and `OPENCODE_APPROVE_TOKEN` scheduler credentials and
does not use `secrets: inherit`.

Expand Down Expand Up @@ -89,7 +91,7 @@ neutral-required, stale-head, or synthetic-merge evidence is not success.

Repository contracts require the exact cron, target repository, one-dispatch
budget, two-hour retry floor, non-cancelling single-flight policy, read-only
workflow token, explicit secret mapping, and absence of both
workflow token plus job-scoped `id-token: write`, explicit secret mapping, and absence of both
`NVIDIA_NIM_API_KEY` and `COPILOT_GITHUB_TOKEN` from the caller.

Rollback is a reviewed source change. Do not disable exact-head binding, reduce
Expand Down
1 change: 1 addition & 0 deletions organization_commercial_readiness_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def __init__(
repositories: list[dict[str, Any]],
snapshots: dict[str, list[RepositorySnapshot | Exception]],
) -> None:
"""Initialize deterministic repository and snapshot responses."""
self.repositories = repositories
self.snapshots = snapshots
self.dispatched_repairs: list[tuple[str, str]] = []
Expand Down
3 changes: 2 additions & 1 deletion scripts/ci/organization_commercial_readiness_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ class GitHubClient:
"""Use the GitHub CLI as an authenticated, bounded REST transport."""

def __init__(self, token: str, *, timeout_seconds: int = 60) -> None:
"""Initialize the client with one bounded GitHub credential."""
if not token:
raise GitHubError("GH_TOKEN is required for organization coordination")
self._token = token
Expand Down Expand Up @@ -853,4 +854,4 @@ def main(


if __name__ == "__main__": # pragma: no cover - exercised through main()
raise SystemExit(main())
raise SystemExit(main())
3 changes: 2 additions & 1 deletion tests/test_disksage_hourly_review_caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_disksage_caller_preserves_credentials_and_read_only_token_scope() -> No
workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1)

assert "\npermissions:\n contents: read\n" in workflow_scope
assert "\n permissions:\n" not in jobs_scope
assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope
assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller
assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller
assert "secrets: inherit" not in caller
Expand All @@ -48,6 +48,7 @@ def test_disksage_caller_preserves_credentials_and_read_only_token_scope() -> No
"statuses: write",
):
assert forbidden not in caller
assert "id-token: write" in caller


def test_disksage_caller_doctoring_records_rca_feasibility_and_latency() -> None:
Expand Down
7 changes: 5 additions & 2 deletions tests/test_pr_review_fix_hourly_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ def test_clearfolio_caller_runs_once_each_hour() -> None:


def test_clearfolio_caller_keeps_github_token_read_only() -> None:
"""The hourly caller delegates with explicit secrets and no token elevation."""
"""The hourly caller delegates with explicit secrets and only OIDC elevation."""
text = _read(_CLEARFOLIO_CALLER)
workflow_scope, jobs_scope = text.split("\njobs:\n", maxsplit=1)

assert "\npermissions:\n contents: read\n" in workflow_scope
assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope
for permission in (
"actions: write",
"issues: write",
Expand All @@ -77,7 +78,7 @@ def test_clearfolio_caller_keeps_github_token_read_only() -> None:
"statuses: write",
):
assert permission not in text
assert "\n permissions:\n" not in jobs_scope
assert "id-token: write" in text


def test_reusable_scheduler_has_no_product_specific_timer() -> None:
Expand Down Expand Up @@ -323,6 +324,8 @@ def test_rca_dispatch_carries_an_explicit_worker_mode(monkeypatch) -> None:
captured: dict[str, str | None] = {}

def fake_run(args: list[str], *, stdin: str | None = None) -> str:
"""Capture the dispatch payload without invoking GitHub."""
del args
captured["stdin"] = stdin
return ""

Expand Down
Loading