The project owner asked to continue hardening the Google Jules integration so IDKMesh can develop quickly, automatically route simple coding work, and keep the complete operating model documented in the repository.
Current main already contained an API-backed Jules dispatcher, but the live router/dispatcher contracts had diverged after later repairs:
.github/workflows/issue-model-router.yml emitted agent:jules-eligible and invoked jules-dispatch.yml with a workflow_dispatch input named issue_number..github/workflows/jules-dispatch.yml no longer declared that input.config/jules-dispatch.json accepted only agent-ready, so the router’s automatic eligibility label no longer entered the dispatcher queue.QUEUED without a PR for hours; the repository had no automatic stale/failed-session reconciliation.These failures explain why a connected Jules installation can still appear idle even when the provider credential and REST session creation work.
IDKMesh now distinguishes two execution ingress paths:
agent:jules-eligible, emitted by the deterministic Issue Model Router, accepted by the dispatcher only for GitHub author associations OWNER, MEMBER, or COLLABORATOR;agent-ready, explicitly applied by a maintainer/trusted triager.Both routes remain subject to the same hard-veto labels, capacity controls, duplicate-session protection, normal CI, and no-auto-merge rule.
The router-to-dispatcher handoff now uses a local reusable workflow through typed workflow_call.issue_number. This avoids relying on recursive GITHUB_TOKEN events and lets GitHub validate the caller/callee input contract before execution. A stdlib-only tools/check_jules_contract.py guard is also part of the required PR Gate so label-policy drift, missing reusable inputs, or privilege expansion becomes a merge-blocking failure.
The Jules API documents these session states:
QUEUEDPLANNINGAWAITING_PLAN_APPROVALAWAITING_USER_FEEDBACKIN_PROGRESSPAUSEDFAILEDCOMPLETEDReferences:
The repository policy adds agent:jules-needs-attention. Reconciliation adds that blocking label and removes agent:jules-dispatched when a session:
QUEUED, PLANNING, or IN_PROGRESS beyond the configured no-update threshold.The blocking label is added before the active reservation is removed, so the issue cannot become automatically redispatchable during the transition. Reconciliation never creates a replacement provider session.
Current initial thresholds are:
These are operational defaults to measure and revise, not claims about normal Jules service latency.
The repository-side concurrency cap remains four active Jules issues, with at most two new dispatches in a recovery sweep.
For lower API pressure and faster batches:
Completed Jules work continues to occupy its issue slot until the issue closes, preserving review backpressure. Failed/stalled work is quarantined and frees its slot so unrelated bounded work can continue.
Tracked under issue #768:
config/jules-dispatch.jsontools/jules_dispatcher.py.github/workflows/jules-dispatch.ymltests/test_jules_dispatcher.pydocs/operations/JULES_AUTOMATION.mdAGENTS.mdCONTRIBUTING.mddocs/README.mdThe local execution container could not resolve github.com, so it could not clone the branch for local pytest execution. The implementation therefore must be validated by the normal IDKMesh pull-request gate on the exact branch head before integration.
The Jules REST API remains an external alpha contract. Provider behavior is observed evidence, not repository authority. Jules output never self-verifies and no dispatcher path merges to main.
The durable fix is intentionally stronger than repairing the current workflow pair:
.github/workflows/jules-dispatch.yml as a
repository-local reusable workflow instead of shelling out to
gh workflow run;issue_number through both workflow_call and
operator-facing workflow_dispatch;config/jules-dispatch.json;tools/check_jules_contract.py cross-checks routing policy, dispatcher
policy, reusable-workflow inputs, trust labels, authority limits, and the
AUTO_CREATE_PR provider contract;This specifically prevents the failure mode where one PR removes the
issue_number input or stops accepting agent:jules-eligible while the
other side continues assuming those contracts exist.
After the typed-contract repair merged, live execution proved the automatic lane
was functioning: IDKMesh created three new Jules sessions without manual starts
for issues #652, #669, and #643. The next session-create request was rejected by
Jules with HTTP 400 and provider status FAILED_PRECONDITION.
The current official Jules limits page documents 15 daily tasks and 3 concurrent
tasks for the base Jules plan. IDKMesh had independently retained a
repository review/backpressure cap of four, so the provider limit was the
stricter runtime boundary.
Issue #788 makes that distinction durable:
max_in_flight remains the repository review/backpressure cap;provider_concurrency.max_concurrent_tasks records the provider/account cap
separately, with plan, official source URL, and checked date;FAILED_PRECONDITION, provider RESOURCE_EXHAUSTED, and HTTP 429
are treated as explicitly rejected provider backpressure: the temporary
reservation is rolled back, the issue remains queued, and the rest of that
sweep stops;This allows a Jules plan upgrade to be represented as a reviewed policy change instead of a code rewrite, while provider-side tasks outside IDKMesh remain safe because explicit provider rejection is still handled as backpressure.
A later live observation showed that control-plane changes could start two
equivalent dispatcher sweeps: one from the dispatcher’s own push trigger and
one from the router’s reusable-workflow recovery path. The duplicate runs were
serialized by concurrency, but they still consumed Actions/API budget and made
the ownership model harder to reason about.
The durable follow-up therefore adds two rules:
tools/check_jules_contract.py is executed not only by the required PR Gate
but also inside both production workflows before mutation/provider work;push recovery.
Its reusable call passes bootstrap_labels: true, while Jules Dispatcher has
no independent push event.The contract checker guards these properties as well, including the watched control-plane paths. This gives three layers of protection: GitHub reusable workflow type validation, merge-time contract validation, and fail-closed runtime validation after integration.
Live execution after the provider-cap policy landed exposed a subtler
conflation: dispatch() subtracted open GitHub dispatch reservations from the
provider concurrency cap. That is conservative, but it is not the same quantity
as provider-running work. Jules PRs 790 and 792 were merged while their broader
issues stayed open, so those completed sessions still made the dispatcher report
the 3/3 provider cap as full.
The corrected model keeps two budgets:
max_in_flight - open dispatch reservations;max_concurrent_tasks - non-terminal account sessions;The Jules API documents COMPLETED and FAILED as terminal SessionState
values. They therefore do not consume provider concurrency, while all other
known/unspecified states are counted conservatively. The provider session list is
account-wide, so work started outside this repository is naturally included.
This preserves the intentional repository review backpressure: a completed Jules task may still occupy an open issue reservation until the issue closes, but it no longer falsely occupies a provider-running-task slot.
The first post-merge execution of the runtime self-check topology caught an
event-context edge case. The router correctly called Jules Dispatcher as a
reusable workflow after a control-plane push, but reusable workflows retain
the caller event context. The dispatcher therefore saw github.event_name ==
'push'; after removing its own direct push trigger, the old event-derived
capacity-fill condition skipped the actual reconciliation/fill step.
The durable correction is another typed workflow contract:
workflow_call.fill_capacity is an explicit boolean input;bootstrap_labels: true and
fill_capacity: true;fill_capacity: true;issue_number;This avoids inferring reusable-workflow intent from the inherited event name and turns another implicit cross-workflow assumption into a checked interface.
After the router/provider contract and capacity accounting were repaired, the next limiting resource is repository verification throughput. Provider slots can be free while GitHub Actions is already saturated; continuing to create Jules work in that state grows candidate generation faster than CI/review can absorb.
The bounded correction implements issue 651 as a third independent admission gate:
max_in_flight;ci_backpressure.enabled=false is the only explicit policy bypass;actions: read only.The dispatcher checks this signal before listing/selecting candidate issues, so
a saturated repository produces no new agent:jules-dispatched mutation. The
normal recovery schedule retries queued work after verification pressure falls.