Devin: deliver authorized context packets to Devin roles and hosted builder input - #932
Conversation
…uilder input Closes #908 Co-Authored-By: bot_apk <apk@cognition.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: Oversized context-bearing follow-up messages persist an unsent pending round, disrupting recovery and subsequent work. Input validation needs to precede that state mutation. Findings:
|
Supplemental adversarial review at
|
…nd declare context capability - Validate the combined follow-up plus context size before any local mutation. - packet_context derives its request from the WorkOrder; mismatched bindings fail closed. - Required policy fails closed; optional policy degrades explicitly and reports it. - public_verdict(provider=Devin) is always informational. - Devin transports declare context as local_runner/agent_handoff; doctor/session text updated. Co-Authored-By: bot_apk <apk@cognition.ai>
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: The patch advertises Devin CLI context support without integrating the review runner. Failed hosted dispatch context validation also leaves durable reservations that prevent legitimate corrections. Findings:
|
- Narrow devin_cli context capability back to unavailable (not wired into the local reviewer). - Render/validate initial context before the work-order record or branch reservation is written. - PacketContext carries only a render closure; the loaded packet's own binding is checked against the WorkOrder. - WorkOrder.context_policy (none/optional/required) decides the outcome, including context=None. - Persist and report the safe context-state enum for dispatch and each message; acknowledge preserves it. Co-Authored-By: bot_apk <apk@cognition.ai>
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: The patch makes existing durable work orders inaccessible after upgrading and permits inconsistent context delivery during crash recovery. Both issues were reproduced with in-memory state and mocked remote calls. Findings:
|
…input, keep legacy records readable - PacketContext now only loads a ValidatedPacket; evidence is rendered inside _evidence() from that exact packet after checking repository, work item and the devin:builder recipient, so no separately supplied evidence is accepted. - Required unavailable context returns the closed UNKNOWN/paused outcome at the run() boundary with no reservation or consumed round. - A digest of the complete intended input (prose plus evidence/state) is stored before the local dispatch/message intent is durable and validated on retry. - Context-free orders keep the pre-context binding and prompt serialization; legacy records without context/input fields read as context-free. Co-Authored-By: bot_apk <apk@cognition.ai>
Codex audit (merge-authority lane)Head SHA: Codex Audit: BLOCKED Summary: The capability change breaks previously valid hosted Devin configurations without a migration path. Findings:
|
…aration Co-Authored-By: bot_apk <apk@cognition.ai>
|
Re Codex audit finding |
Supplemental authorization-boundary reviewExact reviewed head:
The legacy hosted-capability migration at this head appears narrowly scoped and the prior formal blocker appears resolved. Preserve that compatibility while fixing these findings. Keep private packet content and authorization details out of public evidence. |
|
Re supplemental authorization-boundary review at |
Supplemental context identity findingExact head:
|
…ker-neutral context work item PacketContext now carries only the protected store, connection name, packet handle, normalized policy, and backend; _evidence() calls load_authorized() with a request derived from the order and renders under the exact authorized handle, the same identity peer Claude/Codex delivery renders. WorkOrder gains context_work_item for context-bearing orders (a Jira key may differ from the integer GitHub issue used for PR verification); context-free serialization and legacy recovery are unchanged. Co-Authored-By: bot_apk <apk@cognition.ai>
|
Re authorization-boundary P1 (comment 5647438504) — addressed in bd3829b. Re tracker-neutral work item P2 (comment 5647449184) — same commit. |
Codex audit (merge-authority lane)Head SHA: Codex Audit: PASS Summary: No actionable regressions were identified. Assessment based on static review; tests were not executed. Findings: none. |
Supplemental exact-head binding reviewExact reviewed head:
The three prior authorization and identity findings are resolved at this head, the focused adversarial checks pass, and the Code Mower Codex exact-head audit passed. Preserve those fixes and the context-free compatibility behavior. Run the focused tests and push one corrected head for final audit. |
…y may defer to the tracker work item Co-Authored-By: bot_apk <apk@cognition.ai>
|
Re supplemental binding review P2 (comment 5647549815) — addressed in 6196388. |
Codex audit (merge-authority lane)Head SHA: Codex Audit: PASS Summary: No actionable regression was identified in the diff; tests were not executed. Findings: none. |
Claude audit (merge-authority lane)Head SHA: Claude Audit: PASS Summary: This PR extends the existing Claude/Codex context-delivery contract to Devin (hosted builder/orchestrator/reviewer roles) and adds a new PacketContext mechanism that lets the hosted Devin work-order boundary re-authorize and render private evidence immediately before each paid create/message call. I traced the control flow carefully: evidence/authorization is freshly checked under the store lock before any branch reservation or record write (so rejected/oversized/unauthorized context leaves no dangling reservation and consumes no round), only digests of the dispatch/message input are persisted (never plaintext evidence), required-context failures fail closed to a returned (not raised) UNKNOWN/paused outcome with merge_authority forced false, and legacy context-free work orders keep their pre-existing serialized shape/binding hash for backward compatibility. The capability-declaration and config-migration changes (context: unavailable -> agent_handoff for devin_api_v3, with an explicit legacy-capabilities allowlist for in-memory migration) are consistent across templates, schema, and doctor checks. The accompanying tests are extensive and exercise the injection/impersonation edge cases (synthetic packets, wrong recipients, cross-ticket packets, budget overflow, crash/retry-with-changed-evidence) in ways that match the implementation's actual branching. I did not find a P0/P1/P2 correctness, security, or data-loss defect. Findings: none. |
Summary
Closes #908. Part of #901.
context_delivery:SUPPORTED_HOSTS = ("claude", "codex", "devin")drivesSUPPORTED_RECIPIENTS(<host>:{orchestrator,builder,reviewer}), feedback keys, andpublic_verdict(provider="Devin"). The CLI (context attach/deliver/feedback), guided prepare/attach/deliver/feedback, and the pre-PR delivery path accept Devin through the same constants; Devin roles therefore go through the unchangedload_authorized()/deliver()identity, expiry, head, recipient, and online-authorization checks and receive the same rendered evidence as Claude/Codex.public_verdict(provider="Devin")forcesmerge_authority=False(header reads "informational only") regardless of the caller.devin_work_orders: hosted builder input can carry the packet without a local prompt file.WorkOrder.context_policyis the trusted context decision:packet_contextrejects a policy whoserequiredflag disagrees with the order (or any packet on anoneorder), and_evidence()enforces it even withcontext=None— arequiredorder without a packet fails closed ascontext_unavailable; onlyoptionalmay runomitted/degraded.PacketContextcarries only the protectedContextStore(exact type), connection name, packet handle, normalized policy, and backend — no callable, packet, evidence, or identity._evidence()itself callsload_authorized(store, name, handle, policy, ContextRequest(order.repository, order.work_item, "devin:builder"))immediately before the provider write (selected account, current authorization, revocation/expiry, binding, freshness all rechecked under the store lock), re-checks the returned packet'sbindingagainst the order anddevin:builder, and rendersrender_evidence(packet, context.handle)— the exact authorized handle, the samePacket identitythe Claude/Codexdeliver()path renders (no digest-derived UUID). A synthetic local packet with matching repository/issue/recipient fields therefore never reaches the store lookup; a handle for another ticket, wrong recipients, or a store without an authorizable connection isunavailable; a bare handle/packet/string, look-alike object,ContextStoresubclass, or context on anoneorder iscontext_binding_mismatch— all with no provider write. Because the handle is part of the rendered text it is bound into the dispatch/message input digests.WorkOrder.context_work_item(new, default"") is the tracker-neutral packet work item for context-bearing orders (e.g. a Jira key from the guided session/manifest binding);WorkOrder.work_itemreturns it orstr(issue).WorkOrder.issueremains the integer GitHub delivery issue used for_verify(claimissue, closinglinked_issues).from_manifest(..., context_work_item=...)lets a keyed source omitissue_number(a present one must still match); a key on anoneorder isinvalid_work_order. Context-free orders serialize withoutcontext_policy/context_work_item, so legacy bindings/prompts are unchanged. Required-unavailable is explicit at the public boundary:run()returns{"outcome": "UNKNOWN", "state": "paused", "reason": "context_unavailable", "context": {"policy": "required", "dispatch"|"message": "unavailable"}, "merge_authority": false}instead of raising, with no record, reservation, round, or provider write. Initial dispatch renders the context and runs the 64 KiB combined check before the work-order record or branch reservation is written; follow-ups do the same before round/claim/request/pending mutation, so a rejected input leaves no undispatched reservation and consumes no round. The chosen state (delivered/degraded/omitted) is persisted per dispatch (record["context"]) and per message intent (record["message"]["context"]) and returned by every command as"context": {"policy", "dispatch", "message"}; replay,acknowledge_delivered, status, reconciliation/restart, and collect return the saved state rather than recomputing or resetting it. Context is never rendered in preview or for status/collect/cancel; only digests plus the enum are persisted. A digest of the complete intended input (prose + evidence + state) is stored with the local dispatch/message intent before it becomes durable and validated on retry, so a stop between the local and remote intent writes cannot turn delivered context into code-only (request_conflict; status keeps reporting the saved state). Backward compatibility:_fields()serializes acontext_policy="none"order in the pre-context shape for both the durable binding and the dispatch prompt, and records lackingcontext/inputfields are read as context-free, so pre-upgrade status/collect/cancel/clarify/dispatch replay keep working whileoptional/requiredorders bind the field.devin_api_v3.context = agent_handoff(hosted builder input);devin_cli.contextstaysunavailablebecausedevin_cli_audit_pr.run_auditdoes not invoke the authorized context lifecycle. Consistent acrossprovider_capabilities.py, the JSON schema,templates/providers*.yml, doctor remediation,docs/sessions.md, and tests (messaging/cancel remain unavailable; no merge authority). Compatibility:LEGACY_CAPABILITIES["devin_api_v3"]holds the one exact pre-D5 template declaration (canonical hosted mapping withcontext: unavailable);lane_transport()accepts only that mapping andnormalize_lane()upgrades it in memory to the current declaration (transport/driver unchanged, files never rewritten). Any other deviation — a different single value, an extra/missing key, or the legacy mapping ondevin_cli— still failscapabilities must match.docs/context-delivery.md("Hosted Devin builder" section),docs/sessions.md,README.md.Validation
SUPPORTED_RECIPIENTSrender identical evidence; wrong account, revoked authorization, a connection withoutdevin:*recipients, changed head/revision, and refreshed packets are rejected; Devin reviewer feedback/public verdict redaction and forced informational posture; guided Devin host/builder/reviewer flow; hosted work order create/message injection with a private canary present in provider input and absent from every public output, local record, and file under the store; preview never retrieves; status/collect/cancel reject context; ticket‑B packet relabelled as ticket A, other-repo, unbound callable, and context on anoneorder rejected without a provider write; required order withcontext=None, failed reauthorization, oversized, or mismatched context leaves no work-order record or branch reservation (work_order_not_found, then the corrected order and the other issue's branch dispatch cleanly); required context fails closed and optional degrades explicitly with no local round consumed; saveddegraded/delivered/omittedstates survive replay,acknowledge_delivered, status, and the on-disk record; policy/order mismatches rejected at bind time; mixed packet/evidence negatives (ticket‑B packet returned directly for ticket A, forged packet whose recipients excludedevin:builder, string evidence) rejected on dispatch and message; required-unavailable returns the closed UNKNOWN/paused object withwork_order_not_foundafterwards; crash between local and remote intent then retry with dropped/failed/refreshed context isrequest_conflictwhile status still reportsdelivered; legacy context-free records (fields removed) replay dispatch, status, clarify, collect, cancel with the original binding and prompt; a pre-D5devin_api_v3lane withcontext: unavailablepassesnormalize_lane,load_config/validate_config/normalize_config/render_dry_run(file bytes unchanged) andsession.build_session, while five unrelated drifts, a missing key, and the legacy mapping ondevin_cliare rejected.context_binding_mismatchwith zero provider writes;PacketContextfield set is exactly(store, name, handle, policy, backend)andpacket_contextbinds no order identity; Jira-key order (context_work_item="EXAMPLE-1",issue=907, source withoutissue_number): GitHub-issue packet rejected, Jira packet delivered under its handle, the identical handle rendered by peerdeliver()for claude/codex/devin recipients, retry digest conflicts on a refreshed handle,collectverifies issue 907 / PR 42 and rejectslinked_issues908.scripts/privacy_scan.py,ruff check .,compileall,unittest discover -s tests(2980 tests, OK),scripts/smoke_easy_mode.py --json(pass).Risk / Rollback
WorkOrder.context_policydefaults tonone, so existing callers keep code-only behaviour; it joins the durable binding, so a policy change is a new work order. Capability declaration change affects only the hosted Devin transport; the exact prior hosted declaration is accepted and interpreted in memory, so no config rewrite is needed. Revert the branch commits to roll back.Data Or Secret Exposure
Link to Devin session: https://app.devin.ai/sessions/c39dc23c8564443eb22a6c65d2620c69
Open in Devin Desktop: https://app.devin.ai/desktop/session/c39dc23c8564443eb22a6c65d2620c69?variant=devin