fix(enabled): close disabled-script execution on the async paths (review)

Holistic Phase-1 review found the "a disabled script can't execute via
any path" guarantee (§4.3) held only for the sync user-route, the
execute-by-id bypass, and the trigger outbox arm — three paths still ran
disabled scripts:

- Queue arm: `list_active_queue_consumers` filtered the trigger's
  `enabled` but not the bound script's. Add `JOIN scripts … AND
  s.enabled = TRUE` so a disabled script's queue trigger stops consuming.
- Async-HTTP (202) + queued invoke() arms: `build_http_request` /
  `build_invoke_request` hardcoded `active: true`. Set it to
  `script.enabled`, and MOVE the dispatcher's fire-time `active` drop to
  after the source-kind match so it covers all three arms uniformly
  (previously trigger-only).
- `invoke()` (script-to-script): `resolve_id`/`resolve_name` checked
  cross-app isolation but not `enabled`. A disabled target now resolves
  to NotFound (indistinguishable from absent), matching the data plane.

Also (review LOW/§4.7):
- The route-on/script-off 404 returned `NotFound(script_id)`, leaking the
  internal id and distinguishable from absent. Return the same flat "no
  route matches" 404 as the unmatched case.
- Add the §4.7 "enabled endpoint with no route and no trigger" reachability
  warning (was unimplemented; only disabled-target shipped).

Tested: manager-core lib 368 + orchestrator 75 + 22 project-tool journeys
(incl. a new enabled-route→disabled-script flat-404 e2e) green; clippy
-D warnings clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-23 21:59:50 +02:00
parent 5e62f4acfe
commit b3f05dfe2a
6 changed files with 155 additions and 24 deletions

View File

@@ -227,11 +227,18 @@ where
.resolve(matched.matched.script_id)
.await?
.ok_or(ApiError::NotFound(matched.matched.script_id))?;
// An enabled route bound to a disabled script (§4.3, §4.7) is
// unreachable: 404 as if no route matched (the disabled route is already
// dropped from the match table; this covers the route-on/script-off case).
// An enabled route bound to a disabled script (§4.3, §4.7) is unreachable.
// Return the SAME flat "no route matches" 404 as the unmatched case — not
// `NotFound(script_id)`, which would both leak the internal script id to an
// anonymous caller and be distinguishable from absent.
if !script.enabled {
return Err(ApiError::NotFound(matched.matched.script_id));
return Ok((
StatusCode::NOT_FOUND,
Json(serde_json::json!({
"error": format!("no route matches {method} {path}")
})),
)
.into_response());
}
// Drain the body now that we know we'll execute. 10 MiB cap matches