Skip to content

Hosted campaigns: verify Cursor and Devin result transport - #725

Merged
jeffhuber merged 5 commits into
mainfrom
muse/v108-hosted-transport-718
Sep 5, 2026
Merged

Hosted campaigns: verify Cursor and Devin result transport#725
jeffhuber merged 5 commits into
mainfrom
muse/v108-hosted-transport-718

Conversation

@jeffhuber

Copy link
Copy Markdown
Contributor

Closes #718

Summary

  • model hosted campaign readiness as separate auth, installation, trigger, trusted-responder, and result-return checks
  • use Cursor Cloud Agent's builder trigger and fail dry-run closed when hosted transport or bounded result return is unverified
  • add offline verified, unavailable, timeout, spoofed-responder, and successful-return fixtures
  • carry the reviewer-only CLI rejection and corrected Cursor provider fixtures from Provider registry: separate Cursor Cloud Agent and Grok Bot identities #722
  • document the closed hosted-provider contract and exact remediation

Safety

  • hosted providers remain opt-in and paid
  • only explicit --apply can dispatch and only explicit --retry-provider can repeat paid work
  • fixtures are offline and persisted evidence remains metadata-only

Verification

  • 1720 passed, 829 subtests passed
  • focused campaign tests: 363 passed, 208 subtests passed
  • Ruff
  • compileall
  • privacy scan
  • easy-mode smoke
  • release-readiness

Provenance

Builder: Muse (muse-spark-1.3-contributor)

Orchestrator integration: corrected two test expectations and stale post-split Cursor documentation; no contract expansion.

…paid dispatch (#718)

- Model hosted release qualification as a closed five-check dispatch
  profile (auth, installation, trigger, trusted responder, result return).
- Cursor Cloud Agent uses the real @cursor builder mention contract.
- Unverified App transport/result-return reports unavailable with exact
  remediation in doctor and campaign dry-run (hosted_transport_unverified).
- Add bounded offline fixtures (verified, unavailable, timeout, spoofed
  responder, successful return) with fixture-driven tests.
- CLI-level negative test for reviewer-only campaign creation; correct
  stale provider identities in starting-version/context mismatch fixtures.
- Docs update; metadata-only throughout. Built by Muse.
Comment thread src/code_mower/release_campaigns.py Outdated
@jeffhuber jeffhuber added the builder:muse Code Mower Muse builder lane label Sep 5, 2026
@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex audit (merge-authority lane)

Head SHA: e29b06582fff7e122f3b69a0ef1c585bfe9a0e15
Findings: P0=0, P1=1, P2=1, P3=0 (blocker policy: any P0/P1/P2 → BLOCKED)

Codex Audit: BLOCKED

Summary:

The hosted dispatch profile is computed but not consistently enforced, allowing previews or doctor checks to pass despite failed trigger, responder, or timeout checks.

Findings:

  • [P1] Block previews on every failed hosted-profile check -- /private/var/folders/f2/g9zqfdjx7z3ckcsx_f46htwm0000gn/T/codex-audit-qzfj1nce/wt/src/code_mower/release_campaigns.py:2692
    Finding ID: codex:baf847cafb87a7e76acb
    For a custom hosted lane with no trigger or no trusted responder, hosted_dispatch_profile() marks the corresponding check unready, but only installation and result_return are consulted here. The dry-run therefore reports the provider as queued even though applying it cannot trigger work or accept any returned result; use all profile blockers when deciding whether the preview is dispatchable.
  • [P2] Do not pass doctor when profile checks fail -- /private/var/folders/f2/g9zqfdjx7z3ckcsx_f46htwm0000gn/T/codex-audit-qzfj1nce/wt/src/code_mower/doctor_checks/adoption.py:1041
    Finding ID: codex:14eeddb3f04462ed41c0
    When credentials, repository, and transport are ready but a custom hosted lane lacks a trigger/trusted responder or has an invalid response timeout, this branch emits STATUS_PASS even though dispatch_profile contains false entries. This masks the newly computed blockers and contradicts the closed-profile readiness result; emit a warning with the blocker remediations whenever any profile check is unready.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude audit (merge-authority lane)

Head SHA: e29b06582fff7e122f3b69a0ef1c585bfe9a0e15
Findings: P0=0, P1=0, P2=0, P3=1 (blocker policy: any P0/P1/P2 -> BLOCKED)

Claude Audit: PASS

Summary:

PR adds a closed five-check hosted dispatch profile (auth/installation/trigger/trusted_responder/result_return) for Cursor Cloud Agent and Devin, updates the Cursor builder trigger text from "@cursor run"/"cursor run" to the real "@cursor" mention consistently across registry/templates/docs, and makes campaign dry-run report unavailable/hosted_transport_unverified instead of queued when App installation or result-return timeout is unverified. Changes are consistently applied across provider_registry.py, both providers.yml template copies, and templates/providers/cursor_cloud_agent.yml, and are covered by new fixture-driven tests plus updated existing tests (stale cursor_bugbot campaign-provider references correctly swapped to cursor_cloud_agent since BugBot is reviewer-only and rejected by campaign initialization). No correctness blockers found; one minor non-blocking gap noted below.

Findings:

  • [P3] Doctor 'credentials ready' PASS ignores result_return readiness -- src/code_mower/doctor_checks/adoption.py:970
    Finding ID: claude:0a3762bb6b0410b1043f
    The hosted-provider readiness PASS branch (doctor.campaign.credentials, status PASS, message 'hosted credentials and repository target ready') is gated only on has_credentials and has_repo and transport_ready, not on the new result_return profile check. If campaign_response_timeout_seconds were ever misconfigured to a non-positive value for a hosted lane, doctor would still report the provider fully ready even though dispatch_or_advance_campaign's dry-run would correctly mark it unavailable/hosted_transport_unverified. Currently low-risk since this timeout isn't exposed to per-repo config overrides (only campaign_adapter_* keys are), so it can only diverge via a registry-level maintainer mistake, but the two readiness surfaces are not fully aligned.

…octor

Campaign dry-run now marks a hosted lane unavailable with
hosted_transport_unverified whenever any check in
hosted_dispatch_blockers fails -- auth, installation, trigger,
trusted responder, or result return -- instead of only installation
and result return. The exact bounded remediation comes from the
failing profile entry, so no secret values, paths, or output leak.
Auth and issue prerequisites keep their more specific errors where
they already run first.

Doctor warns (doctor.campaign.transport) with the bounded blocker
names and remediations when any closed dispatch-profile check is
false, instead of passing on credentials plus transport alone.

Adds trigger and trusted-responder negative tests for both surfaces.
@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex audit (merge-authority lane)

Head SHA: af4b16cc45aee3d432994b7551ce12590e9a9c2f
Findings: P0=0, P1=0, P2=1, P3=0 (blocker policy: any P0/P1/P2 → BLOCKED)

Codex Audit: BLOCKED

Summary:

Dry-run output loses remediation information whenever more than one hosted dispatch profile check fails.

Findings:

  • [P2] Report remediation for every hosted dispatch blocker -- /private/var/folders/f2/g9zqfdjx7z3ckcsx_f46htwm0000gn/T/codex-audit-nwe5n1vt/wt/src/code_mower/release_campaigns.py:2780
    Finding ID: codex:70ee8f3cfabef9d9d566
    When multiple profile checks fail simultaneously, only the first blocker's remediation is selected. next_detail names the remaining blockers but does not explain how to resolve them, forcing operators to fix and rerun one check at a time. Build the action/detail from all entries in dispatch_blockers, as the doctor path already does.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude audit (merge-authority lane)

Head SHA: af4b16cc45aee3d432994b7551ce12590e9a9c2f
Findings: P0=0, P1=0, P2=0, P3=1 (blocker policy: any P0/P1/P2 -> BLOCKED)

Claude Audit: PASS

Summary:

Adds a closed five-check hosted dispatch profile (auth/installation/trigger/trusted_responder/result_return) used by doctor and by the campaign dry-run preview to fail closed to unavailable/hosted_transport_unverified before any paid dispatch, while leaving the existing --apply path (and its pre-existing transport-override semantics) untouched. New error code is registered in SAFE_ERROR_CODES, checks are ordered correctly (credentials → issue/repo → dispatch-profile blockers → queued), doctor/CLI detail payloads remain metadata-only, and the Cursor trigger-text change is consistently propagated across registry, both template YAMLs, and docs. Extensive new fixture-driven tests and updated existing tests align with the new behavior; no logic contradictions, injection risks, or broken contracts found.

Findings:

  • [P3] Inconsistent int-strictness for campaign_response_timeout_seconds -- src/code_mower/release_campaigns.py:700
    Finding ID: claude:1358ad852283e95071c6
    hosted_dispatch_profile's result_return check requires isinstance(raw_timeout, int) strictly, while _hosted_response_timeout uses a lenient int(value) conversion that also accepts numeric strings/floats. A custom lane configuring the timeout as a numeric string would functionally get a valid deadline via _hosted_response_timeout but would be reported as a dispatch-profile blocker (result_return not ready) in doctor/dry-run. Only matters for non-built-in provider configs since registry values are hardcoded ints; non-blocking.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex audit (merge-authority lane)

Head SHA: c2e78311454d67ddf1a14a70771483c1646118b6
Findings: P0=0, P1=0, P2=1, P3=0 (blocker policy: any P0/P1/P2 → BLOCKED)

Codex Audit: BLOCKED

Summary:

The hosted readiness profile applies a stricter timeout type check than the actual dispatch path, producing false unavailable results for otherwise supported configurations.

Findings:

  • [P2] Evaluate the effective hosted timeout -- /private/var/folders/f2/g9zqfdjx7z3ckcsx_f46htwm0000gn/T/codex-audit-vmszo9mw/wt/src/code_mower/release_campaigns.py:565
    Finding ID: codex:b5abd4aeef2b13f8211c
    When a provider supplies a numeric string such as campaign_response_timeout_seconds: "3600", this check marks result_return unavailable even though _hosted_response_timeout() explicitly parses that value and uses it as a bounded timeout during an applied dispatch. Consequently dry-runs and doctor reject configurations that the dispatch path accepts; derive readiness from the same normalization logic or consistently reject such values everywhere.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude audit (merge-authority lane)

Head SHA: c2e78311454d67ddf1a14a70771483c1646118b6
Findings: P0=0, P1=0, P2=0, P3=0 (blocker policy: any P0/P1/P2 -> BLOCKED)

Claude Audit: PASS

Summary:

Test minimal call.

Findings: none.

@gitar-bot

gitar-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 1 resolved / 1 findings

Hosted campaigns now verify Cursor and Devin result transport before paid dispatch, modeling readiness as separate auth, installation, trigger, trusted-responder, and result-return checks. Result return profile validation stricter than runtime timeout parser has been addressed.

✅ 1 resolved
Edge Case: result_return profile stricter than runtime timeout parser

📄 src/code_mower/release_campaigns.py:561-575 📄 src/code_mower/release_campaigns.py:433-444
hosted_dispatch_profile marks result_return ready only when campaign_response_timeout_seconds is a genuine int (lines 565-569), but the runtime _hosted_response_timeout (lines 433-444) accepts any value coercible via int() — e.g. the string "3600" or float 3600.0 — and otherwise falls back to a positive default. A lane configured with a positive non-int timeout would therefore be reported unavailable ("result-return wait is not a positive integer") in the dry-run and blocked from previewing as queued, even though --apply and the actual response-deadline logic would handle it fine. The shipped Cursor/Devin lanes use int 3600 so this does not affect them, but adopter YAML that quotes the value would hit a confusing false block. Align the profile check with _hosted_response_timeout (attempt the same coercion) so validation and runtime agree.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex audit (merge-authority lane)

Head SHA: 1825a2ed9042ae7cfb5727dc9a74ba62a885a4b4
Findings: P0=0, P1=0, P2=0, P3=0 (blocker policy: any P0/P1/P2 → BLOCKED)

Codex Audit: PASS

Summary:

No actionable correctness issue was identified in the changed code.

Findings: none.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude audit (merge-authority lane)

Head SHA: 1825a2ed9042ae7cfb5727dc9a74ba62a885a4b4
Findings: P0=0, P1=0, P2=0, P3=2 (blocker policy: any P0/P1/P2 -> BLOCKED)

Claude Audit: PASS

Summary:

Hosted dispatch profile changes for Cursor Cloud Agent and Devin are logically sound and well tested; no blocking issues found.

Findings:

  • [P3] Generic error code for non-transport dispatch blockers -- src/code_mower/release_campaigns.py:2857
    Finding ID: claude:87405ba84580bd90c953
    When trigger, trusted_responder, or result_return checks fail (not installation), the persisted error is still the generic hosted_transport_unverified code; next_action/next_detail do name the specific blocker so this is cosmetic only.
  • [P3] Unverified external doc reference -- src/code_mower/provider_registry.py:387
    Finding ID: claude:8142b284b0d73605bf59
    New comment references docs/lanes/cursor.md as source of truth for the @cursor trigger contract; file not included in diff so its existence/content could not be verified.

@jeffhuber
jeffhuber merged commit 686e319 into main Sep 5, 2026
19 checks passed
@jeffhuber
jeffhuber deleted the muse/v108-hosted-transport-718 branch September 5, 2026 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hosted campaigns: verify Cursor and Devin result transport before paid dispatch

1 participant