Conversation
belisarh
marked this pull request as ready for review
September 14, 2026 19:52
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.
Monitor 1.0
A rewrite around the control point, designed from the problem rather than from 0.1. Every operation where failure matters becomes a named declaration of what it tolerates, what bounds it, how every outcome is recorded, and who is told when it fails. Because it is a declaration it can be listed, tested by name, enforced in CI, queried from a log backend, and taught to an agent by the shape of the API.
No compatibility layer.
docs/upgrading.mdmaps every 0.1 surface to its replacement.What is in the box
Monitor::control()) and class (ControlPoint+#[Point]), with two terminals:run()returns the value or throws,attempt()returns anOutcomeand never throws.recover(), the handler's return is the result, null included), escalation (escalate()with a closure or class), limits (within()never fails a completed run,attempts()caps,ensure()fails a wrong result), policies (Retry,Transaction,Breaker, fixed pipeline order, custom via one interface), profiles from config.monitor/1) for every transition, redacted through Redactor, events first, NDJSON tap,Monitor::log($origin).traceparent,Http::traced(), jobs and console.monitor.breakers).monitor:outcomes,monitor:prune.Monitor::fake()with eleven assertions,monitor:points --check(table/JSON/SARIF, seven rules),monitor:explain,make:control-point, a Pest expectation and a PHPStan rule.Conventions pass
After the build, four reviews compared the package against Pennant, Pulse, laravel/mcp and Redactor on code organisation, public API, integration points and documentation. What changed as a result:
Contracts\, commands inConsole\Commands\; services are open classes, only value objects arefinal readonly; exceptions implement aMonitorExceptionmarker with their natural SPL parents.ControlisConditionableandMacroable; point names accept backed enums everywhere; policies take constructor arguments;Outcomeand the other value objects areArrayableandJsonSerializable;CircuitBreaker::attempt()ispermit().Monitor::fake()implements the frameworkFakecontract, is idempotent, and has the full positive/negative assertion vocabulary (assertNotRan,assertNotRecovered,assertNotEscalated,assertNotRefused,assertNotRetried,assertRanOnce).runningInConsole(), publishes a timestamped migration withpublishesMigrations()and never auto-loads it, adds anaboutsection, aliases middleware throughcallAfterResolving, and has no boot-time side effects: console traces start onCommandStarting, and the store flushes through the HTTP and console kernel lifecycle hooks, queueLooping/WorkerStopping, and a 100-outcome buffer cap, the way Pulse ingests.ide.jsonand a Boost skill (resources/boost/skills/monitor-development/SKILL.md) ship alongside the guideline.BreakerStatereference, trimmed README opener,@internalmarkers on helpers.After the first review
Nine additions a developer reaches for, plus the queue integration:
once($key, $ttl): an idempotency policy with aDuplicaterisk, released when the run fails inside its policies and kept once the side effect happened.run()is typed with a template so PHPStan knows the value.Http::breaker('stripe'): the same circuit on the HTTP client, refusing before sending and counting connection failures and 5xx.ChargeCard::dispatch(...): a control point as a job, tagged for Horizon, released for the breaker's retry-after when refused.recover(Risk::class, Handler::class)with aCorrectioncontract resolved from the container.escalateLimits()andthrottleEscalation($seconds), with anescalation.throttledrecord.Monitor::log()binds to the running point;OutcomecarriesstartedAt/endedAt.dispatched_from_runid;WaitForBreakerjob middleware releases instead of failing while a circuit is open. Newdocs/jobs.md.Numbers
Review guide
src/Control.php,src/Run.php,src/Outcome.phpare the model; everything else listens to or reads them.src/Records/Record.phpandresources/schema/record-1.jsonare the log contract.src/Inventory/is the static side;src/Testing/MonitorFake.phpthe test side..ai/guidelines/core.blade.phpis what an agent reads.