MechaCat02
343f6d3b4d
feat(vars): VarsService + vars:: SDK + config capabilities
...
Scripts can now read group-inherited config via vars::get(key) / vars::all().
- shared: VarsService trait (read-only get/all) + NoopVarsService; added as
the 14th field on the Services bundle.
- manager-core: VarsServiceImpl resolves the calling app's config via
config_resolver (derives app_id from cx.app_id; AppVarsRead-gated for
authed principals, script-as-gate for anon).
- executor-core: vars:: Rhai bridge (get/all), registered in the SDK.
- authz: AppVarsRead/Write, GroupVarsRead/Write, GroupSecretsRead/Write
capabilities (group caps resolve via the Phase-2 ancestor walk; the
GroupSecretsRead human-read gate is group_admin-only, distinct from an
app's runtime injection).
- wired VarsServiceImpl into the picloud binary; updated the executor-core
test Services::new call sites.
386 manager-core lib tests green; clippy clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-24 20:59:23 +02:00
MechaCat02
ae2134e62d
feat(executor): expose ctx.request.method to scripts (F4)
...
The request map exposed path/headers/body/params/query/rest but not the
HTTP method, forcing one script per verb (the E2E app needed 7 scripts
where ~3 would do). Add a `method` field to ExecRequest (populated from
the HTTP method on the sync-execute bypass and the HTTP outbox payload;
empty for non-HTTP triggers) and surface it as `ctx.request.method`,
uppercased. A single script can now branch GET/POST on one route.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-12 20:43:10 +02:00
MechaCat02
c3baa87415
chore(v1.1.9): clippy clean — workspace -D warnings
...
Surfaced + fixed during the F3 attestation:
- executor-core/sdk/queue.rs: drop redundant .map(|()| ()) call sites;
enqueue_blocking discards QueueMessageId via .map(|_id| ()) (intentional)
- executor-core/sdk/retry.rs: hoist use std::collections::hash_map::DefaultHasher
+ use std::hash::Hasher to the top of the file (clippy::items_after_statements);
replace `as i64` casts with i64::try_from + clear comment about jitter
bound (clippy::cast_possible_wrap)
- executor-core/sdk/invoke.rs: move _LIMITS_IS_COPY const before #[cfg(test)]
mod tests (clippy::items_after_test_module)
- manager-core/dispatcher.rs: dispatch_one_queue gains #[allow(too_many_lines)]
(it's the queue tick's whole logic — split makes it less readable than
the lint); .map(...).unwrap_or(default) → .map_or(default, ...)
- picloud/lib.rs: Limits { trigger_depth_max, ..Limits::default() } via
struct-update instead of let-mut-assign (clippy::field_reassign_with_default)
- tests: r#"..."# → r"..." where there are no `"` inside (clippy::needless_raw_string_hashes);
combine InvokeTarget::Name | InvokeTarget::Path arms with identical bodies
in sdk_invoke.rs (clippy::match_same_arms)
cargo fmt --all -- --check 2>&1 | tail -3: no output (exit 0)
cargo clippy --workspace --all-targets --all-features -- -D warnings:
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.41s
Workspace lib smoke: 432 passing (306 manager-core + 74 executor-core + 34 shared + 18 orchestrator-core).
DB-gated E2E (against docker compose postgres on localhost:15432):
queue_e2e: 4 ok / invoke_e2e: 4 ok / retry_e2e: 3 ok / migration_queue_messages: 4 ok / schema_snapshot: 1 ok
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-06-07 11:02:22 +02:00
MechaCat02
302c1df577
feat(v1.1.9): invoke:: Rhai SDK module — sync re-entry + invoke_async
...
executor-core/sdk/invoke.rs adds `invoke()` and `invoke_async()` as
top-level Rhai helpers (no `::` namespace, mirroring the brief):
invoke(target, args) -> Dynamic (sync, returns callee's value)
invoke_async(target, args) -> String (fire-and-forget; returns execution_id)
Sync re-entry pattern:
- bridge captures Arc<Engine> via Engine::self_arc()
- calls engine.execute(&source, req) directly — same engine instance,
same Services, same Limits; only SdkCallCx changes per call
- runs inside the caller's spawn_blocking thread (no nested
spawn_blocking, no gate re-admission — the outer execution already
holds a permit)
Back-reference plumbing:
- Engine gains self_weak: OnceLock<Weak<Engine>> + set_self_weak() +
self_arc() accessor
- picloud binary calls engine.set_self_weak(Arc::downgrade(&engine))
right after construction
- register_all extended with limits + Option<Arc<Engine>> params
- Engine::execute_ast threads both through
Limits.trigger_depth_max added — mirrors TriggerConfig::max_trigger_depth.
Default 8; the picloud binary syncs from TriggerConfig::from_env() so
PICLOUD_MAX_TRIGGER_DEPTH governs both the dispatcher's fan-out cap and
the invoke depth bound.
Target parsing (string-first):
- "/api/foo" → InvokeTarget::Path
- 36-char UUID-like → InvokeTarget::Id (uuid::Uuid parse-validated)
- everything else → InvokeTarget::Name
Cross-app + depth + FnPtr guards:
- resolve() returns InvokeError::CrossApp if resolved.app_id != cx.app_id
- bridge throws "invoke: depth limit exceeded (max N)" when
cx.trigger_depth + 1 > limits.trigger_depth_max (checked BEFORE
resolve to avoid a wasted DB round-trip)
- args_to_json rejects FnPtr at any depth — closures don't survive
invoke boundaries
invoke_async path:
- calls services.invoke.enqueue_async() which writes an
OutboxSourceKind::Invoke row (commit 10 wires the dispatcher arm)
- returns the new ExecutionId as a string for caller tracking
Integration tests (sdk_invoke.rs, 6 binaries):
- sync invoke returns callee's value (script returns body.x + 1)
- cross-app invoke rejected (target in other app)
- depth limit exceeded (recursive script that calls itself; throws
before stack overflow)
- callee receives incremented depth in cx (smoke — strict assertion
needs ctx.trigger_depth surface, deferred to v1.2)
- args_to_json rejects FnPtr in payload
- invoke_async returns valid UUID string + queues args payload
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-06-06 19:56:05 +02:00