feat(resolution): bridge Celery .delay()/.apply_async() dispatch to the task body
Celery decouples a task's call site from its body: a @shared_task / @app.task decorated def is invoked via task.delay(...) / task.apply_async(...), a dynamic hop with no static edge, so flows dead-end at the dispatch and the agent reads tasks.py to reconstruct them. celeryDispatchEdges links the enclosing function at each .delay/.apply_async site -> the task function body. Precision rests on a DECORATOR gate: the dispatched name must resolve to a Python function carrying a task decorator, read from the source lines ABOVE its def (the def's startLine excludes the decorator, and no decorates edge exists since @shared_task is an unresolved external import). The kind==='function' filter drops same-named test-method collisions; canvas forms (group(t).delay(), t.s()/.si()) have no single identifier before .delay so they're skipped, not mis-bridged; cross-module name collisions prefer a same-file task else bail. Surfaces as `dynamic: celery dispatch` via the generic synth-edge fallback. Validated 100% precision on two grep-confirmed repos exercising both decorator dialects: paperless-ngx (small, @shared_task, 31 edges, 31/31 real) and pretix (medium, @app.task, 63 edges across 21 tasks, 0/21 false positives); 0 on the httpie control (no Celery). Node-stable (pure edge synth). Suite 1615 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
80a1044d3d
commit
6e5c3a9336
@@ -64,7 +64,8 @@ Status legend (matches the playbook): ✅ done+validated · 🟡 shipped but und
|
||||
| Shape | Ecosystem | Anchor | Mechanism | Status |
|
||||
|---|---|---|---|---|
|
||||
| **MediatR / CQRS** | .NET | `IRequest<T>` → `IRequestHandler<TReq,T>` by the generic request type; `_mediator.Send(new GetFooQuery())` → handler | S (generic-type-keyed) | 🔬 named a frontier in CLAUDE.md, but it's statically keyable via the generic — worth a real attempt |
|
||||
| **Celery / Sidekiq** | Python / Ruby | `@task`/`@shared_task` + `.delay()`/`.apply_async()`; `Worker.perform_async` → `perform` | R/X (decorator + name) | ⬜ |
|
||||
| **Celery** | Python | `@shared_task`/`@app.task`/`@<app>.task`/`@task` def + `.delay()`/`.apply_async()` call → task body | S (decorator-gated name) | ✅ **SHIPPED (2026-06-20)** — `synthesizedBy:'celery-dispatch'` (`celeryDispatchEdges`). Link the enclosing fn at each `.delay(`/`.apply_async(` site → the task fn. Precision rests on the DECORATOR gate: the dispatched name must resolve to a Python `function` carrying a task decorator, read from the source lines ABOVE its `def` (the def's own startLine excludes the decorator; no `decorates` edge exists — `@shared_task` is an unresolved external import). `kind==='function'` filter drops the same-named test-method collision (`consume_file`). Canvas forms (`group(t).delay()`, `t.s()`/`.si()`) have no single identifier before `.delay` → skipped, not mis-bridged. Cross-module name collision → same-file preference else bail. **100% precision: paperless-ngx (small, `@shared_task`, 31 edges, 31/31 real), pretix (medium, `@app.task`, 63 edges across 21 tasks, 0/21 FP); 0 on the httpie control (no Celery).** Node-stable (pure edge synth, no extraction change). Surfaces as `dynamic: celery dispatch @site` via the generic fallback. `+ celery-dispatch-synthesizer.test.ts`. **Deferred (recall):** canvas dispatch, class-based `Task` subclasses, `app.send_task('dotted.name')` string dispatch, aliased imports (`import send_email as s; s.delay()`). |
|
||||
| **Sidekiq** | Ruby | `class W; include Sidekiq::Job; def perform; end` + `W.perform_async(...)` → `perform` | S (class→perform) | ⬜ — the Ruby sibling of Celery; build next-to-it (grep-confirm ≥2 `perform_async` repos). |
|
||||
| **Laravel / Spring events** | PHP / Java | `event(OrderShipped::class)` → `EventServiceProvider` listener map; `@EventListener onX(EventT)` → publisher by event type | R (mapped) | ⬜ |
|
||||
|
||||
### Tier C — frontier, ⛔ do **not** build (no static anchor; would add noise)
|
||||
@@ -82,6 +83,9 @@ Status legend (matches the playbook): ✅ done+validated · 🟡 shipped but und
|
||||
| Redux thunk | `redux-thunk` | ✅ **generalizes (2026-06-20)** — precise on uwave-web (small, 5 edges), session-desktop (medium, 2), trezor (large, 211); control shapeshift (RTK Query, no thunks) = 0. Receiver-agnostic (`api.dispatch`/`thunkApi.dispatch`/`window.…dispatch` all matched). **⚠️ 2 follow-ups below.** |
|
||||
| Object-literal registry | `object-registry` | ✅ **shipped (2026-06-20)** — xrengine `CommandManager` (64), Prebid.js (7), warp-drive (1); 0 false positives after 4 precision gates. |
|
||||
| RTK Query | `rtk-query` | ✅ **shipped (2026-06-20)** — 100% precision (hooks == synth edges, 0 cross-file) on basetool (54), minusx-metabase (11), shapeshift (13); 0 on uwave-web control. Extraction mints endpoint + generated-hook nodes; synth bridges hook→endpoint by convention. |
|
||||
| Pinia store | `pinia-store` | ✅ **shipped (2026-06-20)** — `useStore().action()` instance dispatch → action; 100% precision Geeker (41) / MallChat (64), 0 on element-admin (Vuex) control. |
|
||||
| Vuex dispatch | `vuex-dispatch` | ✅ **shipped (2026-06-20)** — string `dispatch('ns/action')`/`commit('M')` → handler; 100% precision element-admin (55) / vue-admin-template (12) / d2-admin (63), 0 on Redux controls. |
|
||||
| Celery | `celery-dispatch` | ✅ **shipped (2026-06-20)** — `.delay()`/`.apply_async()` → `@shared_task`/`@app.task` body; 100% precision paperless-ngx (31) / pretix (63 across 21 tasks), 0 on httpie control. Decorator-gated via source above the `def`. |
|
||||
| (see playbook §6 / `callback-synthesizer.ts` for the other ~20 channels) | | |
|
||||
|
||||
### redux-thunk follow-ups (found by the n>1 validation — this is exactly what it's for)
|
||||
|
||||
Reference in New Issue
Block a user