fix: unconditionally top up agents-api substrate to prevent hasher fatal (#2500)#2503
Merged
Conversation
The agents-api bootstrap loads its WP_Agent_* class files only inside an AGENTS_API_LOADED-guarded block that early-returns. Any condition that defines the constant without completing the require block (version skew, partial/aborted bootstrap, or a load-order regression) leaves the classes missing and produces a bare "Class WP_Agent_Package_Artifact_Hasher not found" fatal the first time a namespaced delegator references the global -- e.g. during agent export. The previous guardrail only topped up the substrate when a dual-load *skew* was detected (canary class missing AND constant defined). That gated recovery behind one specific sub-case. This makes the bundled class top-up run UNCONDITIONALLY whenever AGENTS_API_LOADED is defined: every include is require_once, so it is a no-op in the common single-copy case and a complete recovery whenever the class set is incomplete -- closing the entire "constant defined, classes missing" fatal class rather than just the skew sub-case. If the canary is still missing after the top-up (bundled file absent), it fails loud via admin notice / WP-CLI error instead of a cryptic fatal. Adds a smoke-test assertion exercising the exact #2500 regression path (AgentBundleArtifactHasher::hash() delegating to the topped-up global). Closes #2500.
3 tasks
Contributor
Homeboy Results —
|
Self-review of the prior commit found two issues: 1. The unconditional top-up parsed the bootstrap and ran ~185 require_once on every network request (~0.25ms each) even on the healthy path. The #2500 fatal was the canary class (WP_Agent_Package_Artifact_Hasher) itself missing, which the cheap class_exists() canary gate already detects — so restore that gate. The guardrail now adds zero file I/O on the common healthy path and only tops up when the canary is actually absent. 2. The self_heal docblock falsely claimed "side-effect add_action registrations are intentionally not re-fired here." The parsed require list DOES include 21 register-*.php files that call add_action() at file scope. They are re-run, but require_once dedup means already-loaded ones are no-ops (no double-registration), and ones the incomplete bootstrap skipped are correctly executed. Docblocks corrected to describe the real behavior. No behavior change for the #2500 recovery path; the smoke test (11 assertions, including the delegator regression) still passes.
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.
Summary
Fixes the production fatal
Uncaught Error: Class "WP_Agent_Package_Artifact_Hasher" not foundininc/Engine/Bundle/AgentBundleArtifactHasher.php:24, observed on extrachill.com (03-Jun-2026, tied to agent bundle ops /agent export).Closes #2500.
Root cause
The agents-api substrate's bootstrap (
vendor/.../agents-api/agents-api.php) loads all itsWP_Agent_*class files inside a single block guarded by:That guard is all-or-nothing per-constant. Any condition that leaves
AGENTS_API_LOADEDdefined while the per-classrequire_oncelist did not run to completion for the request leaves theWP_Agent_*classes missing. The first time a DM namespaced delegator (DataMachine\Engine\Bundle\AgentBundleArtifactHasher) references the global\WP_Agent_Package_Artifact_Hasher, PHP fatals with the bare "Class not found".Triggers in this class of failure:
agents-apiplugin) and the older copy wins the load race, early-returning the newer copy before it requires its classes.WP_Agent_*global before the bootstrap require block completes.The prior guardrail (#2485) only topped up the substrate when a skew was detected (
AGENTS_API_LOADEDdefined AND canary class missing). That gated recovery behind one specific sub-case, so the broader "constant defined, classes incomplete" failure was still exposed. (Production was additionally running 0.138.2, which predates #2485 entirely.)The fix
datamachine_agents_api_run_guardrail()now tops up the bundled substrate class set unconditionally wheneverAGENTS_API_LOADEDis defined, instead of only when skew is pre-detected:require_once, so the top-up is a no-op in the common single-copy case and a complete recovery when the class set is incomplete.admin_noticeserror and a hardWP_CLI::error— loud and actionable instead of a cryptic fatal.Docblocks and the operator-facing message were broadened to reflect the wider set of causes.
Verification
tests/agents-api-dualload-guardrail-smoke.phpexercises the exact bug: WP_Agent_Package_Artifact_Hasher not found — fatal in AgentBundleArtifactHasher #2500 regression path: withAGENTS_API_LOADEDdefined and the hasher class absent, the guardrail tops up the substrate andDataMachine\Engine\Bundle\AgentBundleArtifactHasher::hash()returns a valid sha256 without fataling.php tests/agents-api-dualload-guardrail-smoke.php).php -lclean on both changed files.phpcsexit 0 on both changed files (the 31 warnings homeboy's--changed-sincereports are pre-existing alignment warnings inChatOrchestrator.php/AgentsChatHandler.php, which this PR does not touch).Layer purity
The fix lives in DM's own bundle/bootstrap layer (
inc/agents-api-guardrail.php), which owns theWP_Agent_*delegators — no higher layer involved. The durable fix still belongs upstream in Automattic/agents-api (idempotent per-class loading independent of the bootstrap constant); this guardrail is DM's defensive recovery until that lands.