Primitive event bus, local PostOffice delivery, and the engines' actuator endpoints - #17
Merged
Merged
Conversation
Ratified design (Eric, 2026-08-23): every invocation reaches a function the same way - a per-route FIFO mailbox consumed by 'instances' worker tasks (the parameter becomes faithful; the semaphore is gone). The HTTP host and the local side of PostOffice are thin ingress adapters over the bus. - bus.py (internal): deliver (RPC, ttl-bounded -> 408; dead-work skip for queued calls whose caller already timed out) and publish (drop-n-forget, 202-shape ack). Worker owns the invocation pipeline: per-delivery trace context, AppException -> portable error envelope, exec_time, sync handlers via the executor, error logging for un-replied publishes. - PostOffice without an endpoint = local ingress with the engines' semantics: private routes callable in-app (the wire keeps its 403), headers verbatim, reply envelope identical to the remote path; unregistered route -> 404 envelope. With an endpoint: unchanged wire client. - No spill tier, no queue cap (ratified): back-pressure belongs to the tier that owns recovery - the engines' flows and graphs; a leaf host fails fast by deadline. In-memory only; send() has no ttl valve (engine parity). - Demo gains the local-composition pair (public hello.chain -> private demo.suffix.helper); README documents local calls, faithful private and the workflow boundary; scope statement amended. Pins: tests/test_bus.py - local RPC public+private, unregistered 404, FIFO order, instances=2 concurrency peak, local 408 + dead-work skip proven, trace chained caller -> entry -> private helper, local send ack. The whole existing host suite now exercises the bus path implicitly. Gates: ruff clean, basedpyright 0 errors, tests 40/40. Co-Authored-By: Claude Code <noreply@anthropic.com>
…c execute Eric's Sonar scan of the new code: the trailing 'None = drop-n-forget' comment parsed as commented-out code (the S125 wording lesson); close() now uses gather(return_exceptions=True) - the workers' own CancelledError outcomes are collected while a cancellation of close() itself still propagates; _execute is a staticmethod (uses nothing from self). Gates: ruff clean, basedpyright 0 errors, tests 40/40. Co-Authored-By: Claude Code <noreply@anthropic.com>
Eric's IDE review: the entry handler's local 'po' shadowed the test's outer 'po' - renamed inner_po; node twin aligned in its own commit. Co-Authored-By: Claude Code <noreply@anthropic.com>
…netes GET /info, /info/routes, /env, /health, /livenessprobe on the Event API host, mirroring the Java ActuatorServices shapes as ported to Rust. Health-check functions are normal registered functions speaking the engines' type=info / type=health contract, listed in mandatory/optional.health.dependencies and called through the event bus; /livenessprobe follows the most recent /health outcome (400 when down, engine parity). Version moved to version.py (single Python-side source). Co-Authored-By: Claude Code <noreply@anthropic.com>
…rowing, spelling One async handle() dispatcher (awaits the /health branch; the constant endpoints render synchronously) replaces four async-without-await handlers - and now mirrors the node twin's dispatcher shape. app_origin uses the local-narrowing pattern (checkers do not narrow module globals). 'health check' spelling; origin test no longer compares an expression to itself. Co-Authored-By: Claude Code <noreply@anthropic.com>
…or the demo log.format now carries the engines' three presentations: text (default), json (pretty-printed, matching the engine's pretty serializer) and compact (single-line JSONL for log aggregators). The demo app gains examples/resources/application.yml - the engines' resources convention, auto-loaded next to the app file - wiring the demo.health dependency and demonstrating the well-known keys. README quick start teaches the -D override syntax instead of --port. Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Three features that complete the wrapper as a deployable polyglot function host:
bus.py) — the single dispatch pipeline. Everyinvocation reaches a function the same way: a per-route FIFO mailbox (asyncio.Queue)
consumed by
instancesworker tasks, making the@preloadparameter faithful. Twooperations only:
deliver(RPC, ttl-bounded → the standard 408 envelope, with adead-work skip for queued calls whose caller already timed out) and
publish(drop-n-forget, returning the 202-shape ack). The HTTP host and the local side of
PostOffice are thin ingress adapters over it; neither has its own invocation path.
PostOfficewithout an endpoint delivers through theapplication's own bus (engine semantics):
private=Truenow means exactly what itmeans in the engines — callable in-app while the wire keeps its 403; headers pass
verbatim; trace context chains through local calls; the reply envelope shape is
identical to the remote path.
actuator.py) —GET /info,/info/routes,/env,/health,/livenessprobeon the Event API port, mirroring the JavaActuatorServicesshapes as ported to Rust. Health check functions are normalregistered functions speaking the engines'
type=info/type=healthinterfacecontract, listed in
mandatory.health.dependencies/optional.health.dependenciesand called through the event bus;
/healthanswers UP (200) / DOWN (400) and feeds/livenessprobe.Supporting:
log.formatnow carries the engines' three presentations —text(default),json(pretty-printed) andcompact(single-line JSONL) — verified against the engine'sJsonAppender/CompactAppender pair; a sample
examples/resources/application.ymldemonstrates the resources convention and the well-known keys (the demo now runs with
plain
mercury-serve examples/demo_app.py); version single-sourced inversion.py.Why
instancesandprivateparameters must be faithful, not decorative. Beforethe bus,
instanceswas a semaphore andprivateonly a wire-side rejection — adeveloper reading engine documentation would form expectations the wrapper didn't meet.
With the bus, worker assignment is real and private functions are reachable in-app
through PostOffice, exactly like an engine
pocall.back-pressure belongs to the tier that owns recovery, the engines' flows and graphs; a
leaf host fails fast by deadline instead of hoarding work. Local eventing is for simple
leaf-side composition; workflow processing stays in Event Script / Knowledge Graph.
surface — one probe wiring and one dashboard shape across Java, Rust, Python and
Node.js apps (
livenessProbe→/livenessprobe,readinessProbe→/health), withthe same health-function contract developers already know from the engines.
Verification
skip, private reachability, trace chain; 10 actuator pins: all five shapes,
liveness-follows-health round trip, optional-failure-never-downs, engine
elapsed-time/origin format vectors; JSON pretty-vs-compact pin); ruff clean;
basedpyright 0 errors.
hello.chaincalls the privatedemo.suffix.helperthroughthe bus (
{'text': 'polyglot!', ...}); direct wire call to the private route → 403./healthUPwith the full
demo.healthdependency entry;-Dlog.format=compactemits JSONL.Twin PR on mercury-nodejs implements the identical surface in lock-step.
Co-Authored-By: Claude Code noreply@anthropic.com
🤖 Generated with Claude Code