Skip to content

fix(ci): remove elapsed-time job cap on the hourly loop; pin model to orchestrator/free - #1027

Merged
seonghobae merged 9 commits into
mainfrom
fix/hourly-loop-job-timeout-cap
Sep 3, 2026
Merged

fix(ci): remove elapsed-time job cap on the hourly loop; pin model to orchestrator/free#1027
seonghobae merged 9 commits into
mainfrom
fix/hourly-loop-job-timeout-cap

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Integration update (canonical successor to #1006 / #1013)

A review comment (#1027 comment)
correctly found that #1006 is not a proven no-op successor of this PR: it
carries docs/adr/0007-hourly-loop-orchestrator-free-pool-pin.md,
docs/adr/README.md registration, and a more explicit caller-vs-gateway
contract test that this PR did not have. It also asked for a documentation
contract test requiring ADR-0007's body/index status to agree and to read
Proposed while the change is open.

I verified all three PRs against their actual current heads and picked this
PR (#1027)
as the canonical successor, not #1006, for a concrete reason the
review comment didn't have visibility into yet: #1006's workflow file still
contains timeout-minutes: 55
on the loop job — it never removed the
caller-authored elapsed-time cap on model/gateway inference that is the
subject of this PR's own root-cause fix (see "Corrected approach" below and
the review comment above it). #1006 is strong on the ADR/decision-record side
and weak on the actual production defect; #1027 was the opposite. Rather than
duplicate effort across three overlapping PRs, I ported #1006's valid,
non-code delta onto this branch (commit 3788cf8):

  • docs/adr/0007-hourly-loop-orchestrator-free-pool-pin.md — carried over
    unchanged from fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006. Its body already read Status: Proposed on fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006's
    current head (1205e9f); the Proposed/Accepted contradiction the review
    comment flagged had already been corrected there (both body and
    docs/adr/README.md index row read Proposed) by the time I read it, so no
    further repair to the ADR text itself was needed — just carrying that
    agreement onto this branch.
  • docs/adr/README.md — registered the ADR-0007 row as Proposed, matching
    the body.
  • .github/workflows/opencode-hourly-loop.yml — restored fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006's top-of-file
    comment explaining orchestrator/free vs. gateway auto-discovery and
    pointing at ADR-0007, plus a note on this PR's own no-job-deadline policy.
  • tests/test_hourly_opencode_loop_contract.py — ported fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006's ADR-content
    test, and added a new
    test_adr_0007_body_and_index_status_agree_and_stay_proposed_while_open
    test: it parses the ADR body's - Status: line and the
    docs/adr/README.md index row for ADR 0007, fails if they disagree, and
    fails if either reads anything but Proposed while the decision is open —
    the exact acceptance criterion the review comment asked for.

#1013 was diffed against both this PR and #1006: its only content is the
same orchestrator/autoorchestrator/free workflow/test edit, already
present here (and in #1006), with no ADR, no job-deadline fix, and no
additional test coverage. It carries no delta this PR is missing and is fully
redundant once this PR merges.

Verified after the port: pytest tests/test_hourly_opencode_loop_contract.py -q
passes (4 passed), interrogate is 100% on the modified test file, and the
workflow YAML parses. I've left comments on #1006 and #1013 explaining this
PR now supersedes them and pointing at this integration; I have not closed
either — that's for the queue owner once this PR actually merges, per this
org's PR-closure discipline.

Still open, not this PR's call: whether docs/adr/0007-... should also be
migrated so the ADR row is Accepted — that only happens after this PR merges
to protected main under ordinary exact-head authority (the new contract test
enforces that ordering).


Corrected approach (superseding the original PR description below the line)

The original version of this PR added timeout-minutes: 300 at the loop job
level to replace main's pre-existing timeout-minutes: 55. A review comment
(#1027 comment)
correctly identified that any finite job-level timeout that covers the
"Start the contextual-orchestrator gateway" / "Run the hourly loop agent"
steps is a caller-owned elapsed-time termination of model/gateway inference —
exactly what this org's standing policy (model/inference timeouts default to
null; only user cancellation, provider termination, or an explicit audited
admin timeout may end a call) forbids. Raising 55 → 300 moved the violation
further out; it did not remove it. main's existing 55-minute cap on this
same job was an equivalent, independent instance of the same bug.

What actually changed (now on this branch, verified against the current
head f0fa0e9):

  1. Removed timeout-minutes from the loop job entirely — no
    replacement value. The job now relies on GitHub's implicit 360-minute
    hosted-runner ceiling as an unavoidable platform fact rather than an
    artificial "model timeout policy." Only the already-narrow setup steps
    (checkout, pip/npm install, token generation) remain scoped by their own
    step timeouts where present; the gateway-start and hourly-loop-agent steps
    stay unbounded.
  2. Pinned orchestrator/autoorchestrator/free in all three call
    sites (opencode.json model, the models catalog key, and the
    opencode run --model flag), plus the workflow's top comment. This closes
    the same gap independently carried by fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006 and fix(hourly-loop): route OpenCode hourly maintenance agent to orchestrator/free #1013 (both still open,
    unmerged, as of this update — see "Relationship to fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006 / fix(hourly-loop): route OpenCode hourly maintenance agent to orchestrator/free #1013" below).
  3. Updated .github/opencode/hourly-loop-prompt.md to drop the "at most 45
    minutes" self-imposed heuristic and the "highest-leverage gap" informal
    ranking, replacing both with fail-closed language: no repository-authored
    elapsed-time limit on model work, and no invented ordering when multiple
    product gaps are simultaneously admissible.
  4. tests/test_hourly_opencode_loop_contract.py: renamed/updated the
    free-pool assertion (forbids orchestrator/auto outright) and added
    test_hourly_loop_has_no_repository_authored_model_job_deadline, which
    asserts the loop job header carries no timeout-minutes: and the prompt
    no longer contains the removed heuristics.
  5. CHANGELOG.d/hourly-loop-job-timeout-cap.md fragment describing the
    correction.

Verified on a fresh clone of this exact branch head: YAML parses
(yaml.safe_load), and pytest tests/test_hourly_opencode_loop_contract.py -q
passes (2 passed).

Known limitation — NOT fixed by this PR

Removing the job-level timeout does not solve the original concurrency
problem this PR set out to address: concurrency: { group: opencode-hourly-loop, cancel-in-progress: false } still means a single truly
wedged run can occupy the group for up to GitHub's implicit ~360-minute
hard ceiling, during which every other hourly trigger queues up and is then
superseded rather than run. Solving that properly needs checkpointing /
resumability in a durable execution boundary (the reviewer's suggested
direction) — a materially larger, separate piece of work this PR
deliberately does not attempt. Filing that as follow-up work rather than
scope-creeping it into this fix.

Relationship to #1006 / #1013

See the integration update at the top of this description — this PR is now
the canonical successor and carries #1006's ADR/documentation delta forward.

Relationship to #971

(unchanged from the original PR: see discussion on
#971
main's loop job carried timeout-minutes: 55 since #834/9c299fa4; #971's
branch drops the job-level bound and replaces it with narrow per-step
timeouts on setup steps only, leaving the gateway/inference steps unbounded,
which is now also this PR's end state. #971 remains untouched by this PR;
it is draft with unrelated open blockers.)


🤖 Generated with Claude Code

https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4


Generated by Claude Code

…efault

The `loop` job in opencode-hourly-loop.yml had no explicit job-level
timeout-minutes on main, so GitHub Actions applied its implicit
360-minute (6h) hard per-job cap. Combined with this workflow's
`concurrency: { group: opencode-hourly-loop, cancel-in-progress: false }`,
a single wedged run (the gateway-start or hourly-loop-agent steps, which
stay deliberately unbounded because model inference is expected to take
2h+ per model) could occupy the concurrency group for up to 6 hours,
during which every subsequent hourly trigger would queue and then be
superseded rather than actually run.

Set an explicit job-level timeout-minutes: 300 (5h) on the `loop` job.
300 was chosen as comfortably above the documented "2+ hours per model"
floor for legitimate long runs, while leaving real margin under the
platform's 360-minute hard ceiling -- generous enough that a genuinely
long legitimate run still completes, tight enough that a truly wedged
run can no longer come close to costing multiple hourly cycles. Placing
it at the job level (rather than per-step) means it covers every step
in `loop`, including "Start the contextual-orchestrator gateway with
auto-discovery" and "Run the hourly loop agent", without needing
per-step values that could conflict with the standing "no timeout on
model/inference calls" policy for those two steps individually.

No contract test pins the previous value: tests/test_hourly_opencode_loop_contract.py
asserts other properties of this workflow file and does not assert
timeout-minutes, so it required no change and still passes.

Addresses a Devin PR review finding on #971 ("Hourly runs can queue
indefinitely") against this same workflow file. Scoped as an independent
fix rather than folded into #971 because this exact job-level timeout
line is itself part of #971's own diff (that branch removes it and adds
narrower per-step timeouts for the deterministic setup steps only,
still leaving no job-level bound) -- see PR body for details.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 6 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 96464c72-7296-4cd5-ae05-6216fbe73bd5

📥 Commits

Reviewing files that changed from the base of the PR and between 394bf4e and 5432205.

📒 Files selected for processing (6)
  • .github/opencode/hourly-loop-prompt.md
  • .github/workflows/opencode-hourly-loop.yml
  • CHANGELOG.d/hourly-loop-job-timeout-cap.md
  • docs/adr/0007-hourly-loop-orchestrator-free-pool-pin.md
  • docs/adr/README.md
  • tests/test_hourly_opencode_loop_contract.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor Author

Fresh exact-head owner handoff for d462118a88018f77a2da3ec6ecb7f2c80a5e88ab against protected main@464da4715b495b5eaaa593eba3796e2d976ee0c9.

This PR correctly identifies a real platform boundary, but timeout-minutes: 300 is not the root-cause GREEN under the current CWL contract. GitHub currently documents jobs.<job_id>.timeout-minutes defaulting to 360 minutes and GitHub-hosted jobs having a 6-hour execution limit (https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idtimeout-minutes, https://docs.github.com/en/actions/reference/limits). An explicit 300-minute job timeout therefore introduces an earlier caller-owned elapsed-time termination of the gateway/inference path; it does not implement default model timeout null. The platform's 6-hour hosted-job ceiling is an external execution-boundary fact and must be designed around rather than converted into a 300-minute model policy.

There is also a live overlapping valid delta that must not be discarded: protected main still requests contextual_orchestrator_gateway/orchestrator/auto in this hourly model-backed Actions workflow, while #1006@c7ca8a06901bc65b7376fbe7a4852267d134cf96 and #1013@0a800f87c6df8bb310e1a09859f44d5451fcc969 independently carry the orchestrator/free correction. Do not merge #1027 in isolation and leave the current auto route behind; integrate the valid free-pool delta through one canonical successor/owner lane.

Required RED before production change:

  1. Parse the actual hourly workflow and fail if the model-backed execution selects anything except contextual_orchestrator_gateway/orchestrator/free (including orchestrator/auto, a provider/model id, or a paid fallback).
  2. Fail if a caller-authored timeout-minutes/step timeout can terminate the model/gateway inference execution merely because elapsed time passed. Setup/cleanup/network-health bounds may remain distinct when they do not impose an inference deadline.
  3. Exercise a >300-minute-equivalent fake/durable execution path deterministically (virtual clock/test double, not a real 5h CI sleep) and prove the model result is not classified as provider termination/admin timeout because of the leaf workflow.
  4. Exercise the external GitHub-hosted 6h ceiling as an infrastructure-loss/recovery case: checkpoint/idempotency/re-dispatch/resume must not synthesize a model verdict or lose the exact input/head/provenance identity.

Smallest GREEN: integrate the #1006/#1013 orchestrator/free contract into one canonical branch; remove the explicit model-bearing job deadline instead of changing 55→300; if a single GitHub-hosted job cannot safely own long inference because of GitHub's unavoidable 6h platform ceiling, move long-lived inference to the contextual-orchestrator-owned durable execution boundary (or equivalent resumable owner mechanism) and keep Actions as a bounded dispatch/observe/reconcile client. Preserve exact-head identity, cancellation taxonomy (user cancel / provider end / explicit audited admin timeout / infrastructure loss), and fail closed on missing capability/evidence.

Current exact-head admission is independently non-passing: CodeQL PR run 33632677185 is terminal startup_failure with zero jobs, while Tests/Security/Fuzz/SAST/OSV/Scorecard/Security Scan are queued. No predecessor or local test result should be promoted across the eventual integration/reconciliation.

@seonghobae seonghobae changed the title fix(ci): cap the hourly loop job at 300m instead of the implicit 6h default fix(ci): pin hourly OpenCode to orchestrator/free without heuristic deadlines Sep 2, 2026
@seonghobae seonghobae changed the title fix(ci): pin hourly OpenCode to orchestrator/free without heuristic deadlines fix(ci): remove elapsed-time job cap on the hourly loop; pin model to orchestrator/free Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — this correction is right, and I've verified it against a fresh clone of the current branch head (f0fa0e9).

What was right: timeout-minutes: 300 at the job level still covers "Start the contextual-orchestrator gateway" and "Run the hourly loop agent," so it's a caller-owned elapsed-time cutoff on model/gateway inference regardless of the number chosen — raising 55→300 moved the violation further out rather than removing it. main's pre-existing timeout-minutes: 55 on this same job was an independent instance of the identical bug.

What's now on the branch (I found corrective commits already pushed here after your comment landed, verified them, and updated the PR title/body to match — no additional code push was needed from me):

  1. timeout-minutes removed from the loop job entirely — no replacement value. The platform's implicit 360-minute ceiling is accepted as an external fact rather than converted into a model-timeout policy.
  2. orchestrator/autoorchestrator/free corrected at all three call sites.
  3. The hourly-loop prompt no longer self-imposes "at most 45 minutes" or an informal "highest-leverage gap" ranking; it now says explicitly not to invent an elapsed-time limit or an ordering when the docs don't provide one.
  4. A new contract test (test_hourly_loop_has_no_repository_authored_model_job_deadline) pins the no-job-deadline property so it can't regress silently.

Confirmed: YAML parses, pytest tests/test_hourly_opencode_loop_contract.py -q passes (2 passed).

Honestly out of scope, still true: this does not solve the original concurrency problem — a genuinely wedged run can still occupy the cancel-in-progress: false group for up to ~360 minutes, superseding queued hourly triggers. Fixing that needs the checkpointing/durable-execution redesign you described; that's real, separate work this PR isn't attempting. Documented as a known limitation in the PR body rather than implied as solved.

#1006 / #1013: both are still open and unmerged as of this comment (c7ca8a0 / 0a800f8), and independently carry the same orchestrator/free correction. I did not touch or fold them in — since the correction now also lands here, once this PR merges they'll likely be redundant against main and should be closed as superseded, but that call belongs to the PR-queue owner, not to this PR unilaterally.

CodeQL 33632677185 startup_failure: that run was against an earlier commit on this branch. The subsequent pushes already re-triggered CodeQL fresh at the current head (queued as of this check), so no manual re-run was needed.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Fresh successor-carryover check against the current f0fa0e9ed6642006ded5aec90f4af88633e97551 patch: the production fix now correctly removes the caller-authored model job deadline and pins all hourly-loop model call sites to orchestrator/free; keep that delta. However, #1006 is not yet a proven no-op successor candidate after #1027 merges. #1006 carries additional valid decision/documentation delta that #1027 does not currently contain: docs/adr/0007-hourly-loop-orchestrator-free-pool-pin.md (problem/boundary/rejected alternatives/failure semantics/references), docs/adr/README.md registration, and a more explicit caller-vs-gateway contract test. Under the queue policy, do not close #1006 merely because the workflow lines overlap.

There is also a contradiction inside #1006 that must be repaired during integration rather than copied blindly: the ADR body says Status: Proposed while #1006's docs/adr/README.md row marks 0007 Accepted before protected integration. Preserve the valid ADR rationale, keep both index/body Proposed while open, and transition to Accepted only after ordinary protected merge/exact-head authority. #1013's valid delta appears to be the overlapping free-pool workflow/test change and can become redundant only after exact diff/requirement comparison against the verified successor.

Successor RED/GREEN acceptance: add a documentation contract test that requires ADR 0007 body/index status agreement and requires Proposed for an open/unmerged change; integrate the repaired ADR decision/rationale into this canonical successor (or keep #1006 alive until a separate verified successor does); then rerun exact-head checks. Current #1027 exact head is still non-admissible regardless: CodeQL PR 33633560456 is terminal startup_failure, while Tests/Security/Fuzz/SAST/OSV/Scorecard/Security Scan remain queued.

Ports #1006's decision record onto this branch's already-corrected fix
(the removed job-level model deadline and orchestrator/free pin) so the
two overlapping PRs converge on one canonical successor instead of
duplicating the same underlying repair:

- docs/adr/0007-hourly-loop-orchestrator-free-pool-pin.md: #1006's ADR,
  carried over unchanged. Its body already reads `Status: Proposed`.
- docs/adr/README.md: register the ADR-0007 row as `Proposed`, matching
  the body -- the Proposed/Accepted mismatch a review comment flagged
  on #1006 was between an earlier ADR draft and its index row; #1006's
  current head had already corrected both to `Proposed` before this
  port, so no repair was needed here beyond carrying the agreement
  forward.
- .github/workflows/opencode-hourly-loop.yml: restore #1006's top-of-file
  comment block explaining the orchestrator/free vs. auto-discovery
  distinction and pointing at ADR-0007, plus a note on the no-job-deadline
  policy this PR already implements.
- tests/test_hourly_opencode_loop_contract.py: port #1006's ADR-content
  test, and add a new
  test_adr_0007_body_and_index_status_agree_and_stay_proposed_while_open
  acceptance-criteria test that parses both the ADR body `Status:` line
  and the docs/adr/README.md index row and fails if they disagree, or if
  either reads anything but `Proposed` while the decision is open. This
  is the regression test the review comment asked for so the
  body/index contradiction it found on #1006 cannot recur silently, here
  or on any future ADR.

#1013 was compared against this diff and #1006's: its only content is
the same orchestrator/auto -> orchestrator/free workflow/test edit,
already present here and in #1006, with no ADR, no job-deadline fix, and
no additional test coverage -- it carries no delta this PR is missing.

Verified: tests/test_hourly_opencode_loop_contract.py (4 passed),
interrogate 100% on the modified test file, workflow YAML parses.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

Verified all four points against fresh reads of #1006, #1013, and this PR's actual current heads, and acted on them. Summary:

  1. True — fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006 is not a proven no-op successor. It carries docs/adr/0007-hourly-loop-orchestrator-free-pool-pin.md, the docs/adr/README.md registration row, and a more explicit ADR-content contract test that this PR didn't have. I did not close it.

  2. The Proposed/Accepted contradiction had already been fixed on fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006's own head before I read it. fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006's current head (1205e9f) shows both the ADR body and the docs/adr/README.md index row reading Proposed — the author's own correction commits (7a0a4a0, 1205e9f) landed that fix. So there was nothing left to repair in the ADR text itself; I only needed to carry the already-agreeing Proposed/Proposed pair onto this branch.

  3. fix(hourly-loop): route OpenCode hourly maintenance agent to orchestrator/free #1013 is fully redundant. Diffed it against both fix(ci): pin hourly OpenCode loop to orchestrator/free pool #1006 and this PR: its only content is the same orchestrator/autoorchestrator/free workflow/test edit, already present in both. No ADR, no job-deadline fix, no extra test coverage — nothing unique.

  4. Added the suggested acceptance-criteria test: test_adr_0007_body_and_index_status_agree_and_stay_proposed_while_open in tests/test_hourly_opencode_loop_contract.py — parses the ADR body Status: line and the README index row, fails if they disagree, and fails if either isn't Proposed while the PR is open.

Integration path taken (deviates from the suggested default of #1006-as-canonical, with justification): I picked this PR (#1027) as canonical instead of #1006, because #1006's workflow file still has timeout-minutes: 55 on the loop job — it never actually applied the fix this whole thread is about (removing the caller-authored elapsed-time cap on model/gateway inference). #1006 is strong on the ADR/decision-record side and had not touched the underlying defect; #1027 already had the defect fixed and was missing the decision record. So I ported #1006's ADR + README + test delta onto this branch (commit 3788cf8) rather than the reverse. Full detail in the updated PR description above.

Left explanatory comments on #1006 and #1013 noting this PR now supersedes them; I have not closed either, per this org's PR-closure discipline — that's for the queue owner once this PR actually merges.

CI / CodeQL 33633560456: the push that ported the ADR content (3788cf8) retriggered every required check fresh, including a new CodeQL run. As of this reply it's queued, not yet completed, so I can't yet confirm whether the startup_failure pattern recurs a third time on this new head — flagging that as still open rather than claiming it resolved. If it does recur on this exact head, that's the second-on-a-new-commit case worth escalating rather than dismissing as flake twice; I have not triggered a manual re-run since the fresh push already produced a new run.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Canonical-successor integration advanced with #980's only remaining valid semantic delta.

Fresh evidence before the write:

I therefore ported only the non-obsolete setup-node delta into this canonical branch, not #980's whole stale workflow. New #1027 exact head: ad9c23c27663db01745e1614d3c56ccb197c6881.

Exact workflow acceptance at this head:

  • actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/setup-node@v7.0.0 is present;
  • the loop job still has no repository-authored timeout-minutes;
  • OpenCode config/catalog/CLI call-sites remain pinned to orchestrator/free;
  • the branch remains a clean descendant of current protected main and Draft.

The setup-node SHA/tag pair is also independently corroborated by current public GitHub usage/release evidence. This makes #1027 the semantic successor for #980's remaining one-line workflow delta, but #980 should not be closed until this successor actually lands on protected main or another immutable successor proves full inheritance.

@seonghobae seonghobae added area: ci-cd CI, GitHub Actions, checks, release, or supply chain bug Something isn't working priority: high status: draft type: bug Defect or incorrect behavior labels Sep 2, 2026 — with ChatGPT Codex Connector
seonghobae pushed a commit that referenced this pull request Sep 2, 2026
…g-fix session

Adds a dated entry documenting today's remaining #971 session work not yet
covered by an existing entry: the real main-merge conflict resolution
(mergeable_state dirty -> blocked, tests/test_review_gateway.py resolved in
favor of main's evidence-only-admission suite after main's 0db4e5a dropped
max_agents), the two additional ThreadPoolExecutor-atexit-join fixes in
endpoint_race.py and batch_routing.py's new _DaemonWorkerPool, the raw-Future
callback-settlement regression fix, the _DaemonWorkerPool post-shutdown
submit hardening, a verified-false-positive analysis of a proposed
provider_routing equality guard, and a recap of the remaining open,
large-scope work (bootstrap diversity heuristics, hourly-loop
cancel-in-progress gap with a PR #1027 pointer, legacy identifier mixing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
seonghobae and others added 2 commits September 3, 2026 00:31
Adopt main@212ff437 without force-push; current main delta is disjoint from the hourly-loop workflow/docs/test changes.
# Conflicts:
#	docs/adr/README.md

Copy link
Copy Markdown
Contributor Author

Failing check triage: "Full unit and contract suite" (run 33649166922 / job 100311494984)

What failed: tests/test_admin_contract.py::test_model_group_mutations_refresh_audit_events

NameError: name 'json' is not defined. Did you forget to import 'json'

at tests/test_admin_contract.py:187, inside an f-string that calls json.dumps(...) to build a Node.js eval script. 1 failed, 3353 passed, 2 skipped.

Not this PR's defect. This PR's diff only touches .github/workflows/opencode-hourly-loop.yml, .github/opencode/hourly-loop-prompt.md, CHANGELOG.d/hourly-loop-job-timeout-cap.md, docs/adr/0007-..., docs/adr/README.md, and tests/test_hourly_opencode_loop_contract.py — nothing touches tests/test_admin_contract.py or the contextual_orchestrator.admin module. The missing import json was a pre-existing bug introduced on main at commit 212ff437 ("fix(admin): refresh audit after model-group mutations"), which is exactly the base commit this PR's merge captured at the time the check ran. Confirmed by diffing tests/test_admin_contract.py at that commit against the current file.

A fix already exists upstream, no new fix needed. main has since picked up 39a4348e — "fix(tests): repair test_model_group_mutations_refresh_audit_events (#1035)" — which adds import json and restructures the test. This PR just needed to move onto current main to pick it up.

Real merge conflict found and resolved. mergeable_state had gone to dirty against current main (base moved to 394bf4e, "[QUEUE_SATURATION_CHICKEN_EGG] fix(ci): skip docs-only changes for Tests workflow (#1036)"). Merging origin/main produced exactly one real conflict, in docs/adr/README.md's ADR index table: this PR's own 0007 row versus main's newly-added 0123 row (unrelated ADR, different number). Resolved by keeping both rows, per this session's docs/gap-baseline/CHANGELOG conflict convention. No other file conflicted — everything else (including tests/test_admin_contract.py, which picked up the upstream import json fix) auto-merged cleanly.

Pushed (non-force): merge commit 5432205d onto fix/hourly-loop-job-timeout-cap, merging main@394bf4e into the prior head 0b1d43f. This re-triggers every required check fresh at the new head.

Local verification before push (isolated worktree at the merge result, no other in-flight branch touched):

  • tests/test_admin_contract.py — 3 passed (the previously-failing test now passes)
  • tests/test_hourly_opencode_loop_contract.py — 4 passed (this PR's own contract tests, unaffected)
  • python -m interrogate -c pyproject.toml .PASSED (minimum: 100.0%, actual: 100.0%)

No code change was needed in this PR's own diff; the fix is the merge itself.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@seonghobae
seonghobae merged commit f4e5fc6 into main Sep 3, 2026
20 of 22 checks passed
@seonghobae
seonghobae deleted the fix/hourly-loop-job-timeout-cap branch September 3, 2026 03:41

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

{
"\$schema": "https://opencode.ai/config.json",
"model": "contextual_orchestrator_gateway/orchestrator/auto",
"model": "contextual_orchestrator_gateway/orchestrator/free",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Excluded providers can serve hourly work

When discovery labels an OpenAI model free, orchestrator/free admits it through the generic gateway. The hourly agent can use a contractually excluded provider.

Prompt for agents
The hourly workflow requests orchestrator/free from scripts/ci/serve_seeded_gateway.py, which constructs the ordinary TaskOrchestrator. Its free-pool predicate enforces zero price and general-chat modality but does not enforce the review-side provider-account allowlist. contextual_orchestrator/review_gateway.py already defines REVIEW_FREE_POOL_CREDENTIAL_NAMES and excludes OPENAI_API_KEY regardless of discovered price. Route this workflow through a gateway construction path that applies that source-admission policy while preserving loopback authentication, bootstrap KV seeding, auto-discovery, and the required OpenCode API surface. Add an integration contract proving an OpenAI-derived zero-priced model cannot serve the hourly loop.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +117 to +127
## References

ContextualWisdomLab. (2026). *ADR-0003: Vendored contextual-orchestrator
review sidecar with governed gateway pools* [Architecture decision record,
amended 2026-09-02]. `ContextualWisdomLab/.github`,
`docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md`.
https://github.com/ContextualWisdomLab/.github/blob/main/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md

ContextualWisdomLab. (2026). *Pin orchestrator-routed KG extraction to the
`orchestrator/free` pool* [Proposed architecture decision in
ContextualWisdomLab/naruon#1525]. https://github.com/ContextualWisdomLab/naruon/pull/1525

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Research grounding remains undocumented

This process-policy ADR lacks the research artifact requested by AGENTS.md. Add applicable literature or record why none governs this operational decision.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci-cd CI, GitHub Actions, checks, release, or supply chain bug Something isn't working priority: high status: draft type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants