Skip to content

long-running: the four start routes cannot create an operation — create_operation gets estimated_items/context it does not accept (TypeError → 500) #17017

Description

@mrveiss

Found while fixing #17010. The evidence below comes from reading the code; nothing was run.

Problem

The four work-starting routes in api/long_running_operations.py (/codebase/index, /testing/comprehensive, /knowledge-base/populate, /security/scan) build a CreateOperationRequest and call manager.router.routes[0].endpoint(create_request). That reaches OperationIntegrationManager._handle_create_operation (utils/operation_timeout_integration.py), which calls:

self.operation_manager.create_operation(..., estimated_items=request.estimated_items, context=request.context, execute_immediately=...)

self.operation_manager is LongRunningOperationManager (utils/long_running_operations/operation_manager.py), whose create_operation signature is:

create_operation(self, operation_type, name, description, operation_function, priority=..., metadata=None, execute_immediately=False)

It has no estimated_items, no context and no **kwargs. So the call raises TypeError. _handle_create_operation catches it as Exception and answers "Internal server error", and the route turns that into "Failed to start operation" (500). Unless something outside these two files adapts the call, none of the four routes can start an operation today.

A second point: the routes reach the create handler by indexing manager.router.routes[0] on a router that is never mounted (#17011). Which route sits at index 0 depends on registration order.

Why it matters

This is not a security gap. #17010 makes these routes admin-only. But it is a working-looking feature that cannot work. Once fixed, operations should record their creator (metadata={"created_by": …}) so #17010's creator-or-admin scoping can replace today's admin-only rule.

Acceptance criteria

Related

#17010, #17011, #17009

Activity

  1. added this to the v0.9.0 milestone on Sep 18, 2026
  2. mrveiss commented on Sep 23, 2026

    @mrveiss
    OwnerAuthor

    AC verification against merged main — 3 of 5 met, issue stays OPEN

    Verified from origin/main at 31310392df. No branch carries this work; it landed and was never
    closed.

    • A test calls each start route as an admin and shows an operation is created.
      NOT MET, and deliberately so — recorded here rather than silently dropped.

      Two of the five create an operation: /testing/comprehensive
      (test_the_test_suite_route_creates_an_operation_that_records_its_creator asserts a 200 and
      metadata["created_by"] == "root") and /migrate/existing
      (test_a_migrated_operation_records_its_creator).
      The other three answer 501 on purpose — _not_implemented at :94 — because the
      operation behind each called a method that exists nowhere. That is tracked as long-running: wire the indexing, KB-population, security-scan and code-analysis operations to their existing implementations, or retire them — no parallel implementation #17023
      (open), and it is covered by a test that also asserts nothing is queued
      (test_a_start_route_with_no_working_operation_answers_501_and_queues_nothing, parametrised
      over /codebase/index, /knowledge-base/populate, /security/scan). An honest 501 is a
      better answer than a 500, but it is not this criterion.
    • The create path passes arguments create_operation accepts. CreateOperationRequest
      (:184-193) carries estimated_items and context as fields of the request object, and
      created_by, rather than as stray keyword arguments to a call that never accepted them.
    • The routes call the create handler directly. await manager._handle_create_operation(create_request)
      (:196), with the reason recorded inline: "not routes[0] of an unmounted router".
      grep -n 'routes\[0\]' over the module returns nothing.
    • The creator is recorded, and security: /api/long-running has no authentication — anyone can start test runs, scans and KB population, or cancel any operation #17010's checks admit the creator as well as admins.
      created_by is set on both create paths; _may_see (:99) is creator-or-admin;
      _owned_operation (:108) 404s rather than 403s so existence is not disclosed; the
      progress socket uses _may_see_id (:120). Cross-user tests exist and pass:
      test_another_users_or_an_unowned_operation_is_refused_without_disclosing_it,
      test_an_admin_reads_and_cancels_anyones, test_a_resumed_operation_keeps_its_original_creator.
      An operation with no recorded creator is an admin's alone, which the list test exercises
      with a legacy row.
    • Non-admins see and control their own, and the frontend panel works for them again, with a
      test. HALF MET.

      Backend: yes. GET / carries no admin dependency and filters through _may_see
      (:310, :316), and test_a_non_admin_lists_only_their_own_and_an_admin_lists_all passes.
      Frontend: no evidence either way. grep -rn '17018' autobot-frontend/src returns
      nothing, and neither useOperationsApi.ts nor OperationsPanel.test.ts mentions a
      non-admin or creator case. The composable has no admin branch to remove, so the panel
      plausibly works for a non-admin now — but this criterion asked for a test, and there is
      none, so it is not ticked. fix(security): authenticate nine WebSocket endpoints before accept, owner-scope the overseer and workflow sockets, admin-only /api/long-running (#17009, #17010) #17018 is closed, so its 403 is not the blocker.

    autobot-backend/api/long_running_operations_17017_test.py — 15 passed locally. Local passes are
    not CI (#17144).

    Verdict: the TypeError-to-500 defect and the ownership model are delivered and verified. What
    keeps this open is one criterion superseded by #17023 and one frontend test that was never
    written. Neither is provable from this diff, so neither is ticked.

  3. mrveiss commented on Sep 28, 2026

    @mrveiss
    OwnerAuthor

    Classification (for work assignment — not a fix proposal)


    Generated by Claude Code

  4. mrveiss commented on Sep 28, 2026

    @mrveiss
    OwnerAuthor

    Chunking triage — a proposal, not an assignment

    Nothing was relabelled, moved or closed by this pass.

  5. mrveiss commented on Sep 28, 2026

    @mrveiss
    OwnerAuthor

    Closure pass — 4 of 5 criteria met. The remaining one is not a regression: three of the four start routes now fail cleanly instead of creating anything. Not closed.

    Evidence at origin/main 7adaca8c29.

    AC2 — the create path passes arguments the handler accepts — MET. api/long_running_operations.py:185-196 no longer calls create_operation(...) with unsupported keywords. It builds a CreateOperationRequest (:185-194) carrying estimated_items (:190), context (:191) and created_by (:193) as model fields, then calls manager._handle_create_operation(create_request) (:196).
    Method note, because I got this wrong first: operation_manager.create_operation (utils/long_running_operations/operation_manager.py:192-201) still accepts none of estimated_items/context — so grepping for estimated_items= next to that signature reads like the original TypeError surviving. It is a different callee. The receiver decides, not the keyword.

    AC3 — the routes call the create handler directly, not through router.routes[0] of an unmounted router — MET. :196 carries the comment "#17017: not routes[0] of an unmounted…", and the call is to manager._handle_create_operation.

    AC4 and AC5 — the creator is recorded, and #17010's checks admit the creator; non-admins see and control their own — MET, with tests. :193 records created_by. In api/long_running_operations_17017_test.py: test_the_creator_reads_and_cancels_their_own (:259), test_another_users_or_an_unowned_operation_is_refused_without_disclosing_it (:268), test_an_admin_reads_and_cancels_anyones (:280), test_a_non_admin_lists_only_their_own_and_an_admin_lists_all (:243), plus test_a_migrated_operation_records_its_creator (:319) and test_a_resumed_operation_keeps_its_original_creator (:332).

    AC1 — met for one of four routes, and the test says so

    "A test calls each start route as an admin and shows an operation is created."

    • /testing/comprehensive — creates: test_the_test_suite_route_creates_an_operation_that_records_its_creator (:229).
    • /codebase/index, /knowledge-base/populate, /security/scan — the test that covers them is test_a_start_route_with_no_working_operation_answers_501_and_queues_nothing (:221), parametrised over exactly those three.

    So the 500 from a TypeError is gone and three routes now answer 501, queueing nothing. That is a strictly better failure — legible, and not a create path that half-runs — but it is not "an operation is created", and the issue's title is "the four start routes cannot create an operation". Three of four still cannot.

    What is left is a scope question, not a bug hunt: whether those three routes are supposed to have working operation functions in v0.9.0, or whether 501 is the accepted answer and AC1 should be narrowed to the one route that creates. Not something I can settle from the repository — the test documents the current behaviour deliberately, which reads as a decision already taken by whoever wrote it, but the criterion was never updated to match.

  6. mrveiss commented on Sep 28, 2026

    @mrveiss
    OwnerAuthor

    v0.9.1 triage (classification only — not a fix proposal)

    Supplements the earlier classification comment (2026-09-28, comment 5864324099). This comment adds only the pre-filter results, the premise check and new observations. Scope, files, the same-file note and the #17023 blocker stand as recorded there.


    Generated by Claude Code

  7. mrveiss commented on Oct 3, 2026

    @mrveiss
    OwnerAuthor

    AC1 is unsatisfiable as worded — the design changed, and the criterion did not

    Verified at 2a6c281760. Four of five criteria are ticked. The open one reads:

    A test calls each start route as an admin and shows an operation is created.

    autobot-backend/api/long_running_operations_17017_test.py shows why that cannot be met and should not be:

    • :229 test_the_test_suite_route_creates_an_operation_that_records_its_creator — one route genuinely creates.
    • :221 test_a_start_route_with_no_working_operation_answers_501_and_queues_nothing, parametrized over the others — they answer 501 and queue nothing, deliberately.

    The file's own summary line is "honest start routes, a create that works, creator-scoped access". So three of the four routes are now honest about having no working operation behind them, which is a better outcome than four routes pretending to create. A test asserting all four create would have to make three of them lie.

    This is the correct-the-criterion case, not the unmet case. The criterion was written against the original ambition (make all four work); what shipped answers the defect (they silently did nothing) by splitting the routes into one that works and three that refuse loudly.

    Suggested rewording, so this can close on evidence rather than stay open on a sentence the code deliberately contradicts:

    A test calls each start route as an admin: the routes with a working operation create one and record its creator; the routes without answer 501 and queue nothing. Both are asserted.

    Both halves already exist at :229 and :221. I have not edited the body — the rewording is a judgement about original intent, and that is the author's or owner's to make rather than mine.

  8. mrveiss commented on Oct 3, 2026

    @mrveiss
    OwnerAuthor

    AC1 as worded contradicts a deliberate, tested design. Posting the evidence and a recommendation rather than ticking or rewording unilaterally.

    AC1 reads: "A test calls each start route as an admin and shows an operation is created (currently expected to fail, proving this issue)." It was written as a defect pin — note the parenthetical. The defect it pinned is gone, but what replaced it is not "an operation is created" for all four routes.

    Verified against merged origin/main:

    Three of the four routes answer 501 by design, and a test asserts it. autobot-backend/api/long_running_operations.py:95-97 defines _not_implemented, whose docstring states the intent outright: "501 for a start route with no working operation behind it; nothing is queued (#17017, #17023)." The detail string names #17023 as the issue that will implement them. autobot-backend/api/long_running_operations_17017_test.py:212-226 pins exactly that, parametrised over /codebase/index, /knowledge-base/populate and /security/scan — asserting 501, asserting #17023 appears in the detail, and asserting integration.operation_manager.operations == {} so nothing is silently queued behind the 501.

    The fourth route does create an operation, and its test is the one AC1 actually asked for: test_the_test_suite_route_creates_an_operation_that_records_its_creator, whose docstring records the original failure — "this route raised TypeError inside the create call and answered 500."

    So this issue's subject is fixed. The title is "create_operation gets estimated_items/context it does not accept (TypeError → 500)". There is no TypeError and no 500 on any of the four routes. What remains is that three routes have no operation behind them yet, which is #17023's scope, not this one's — and the code says so in both the docstring and the response body.

    AC1 is therefore unsatisfiable as written while the design is correct. Ticking it would be false; leaving it blocks closure on work that belongs to another issue. The other four criteria are ticked, including creator-recording and the non-admin access path.

    Recommendation — reword AC1 to match the delivered design:

    A test calls each start route as an admin. The test-suite route creates an operation recording its creator; the other three answer 501 naming #17023 and queue nothing. Both halves are asserted.

    That is already true on main, so the rewording closes the issue rather than opening work.

    Labelling needs-decision rather than applying it. Rewording an acceptance criterion changes what "done" means here, and a criterion deliberately written as a defect pin is exactly the kind whose author may have intended the stricter reading — that all four routes eventually create operations, with closure waiting on #17023. If that was the intent, the correct action is the opposite of mine: keep AC1 and mark this blocked on #17023.

    The alternative, if rewording is rejected: add a blocked_by edge to #17023 and leave AC1 untouched. What should not happen is AC1 being ticked against the 501 tests — those assert the absence of the behaviour it describes, and reading a guard as the thing it guards is an error this backlog has produced four times today.

  9. added
    needs-decisionBlocked on an owner decision; options and a recommendation are on the issue
    on Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendbugSomething isn't workingfrontendneeds-decisionBlocked on an owner decision; options and a recommendation are on the issuepriority: low

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions