Skip to content

fix: guard against agents-api dual-load version skew on bootstrap#2485

Merged
chubes4 merged 2 commits into
mainfrom
fix-export-dualload-guardrail
Jun 3, 2026
Merged

fix: guard against agents-api dual-load version skew on bootstrap#2485
chubes4 merged 2 commits into
mainfrom
fix-export-dualload-guardrail

Conversation

@chubes4
Copy link
Copy Markdown
Member

@chubes4 chubes4 commented Jun 3, 2026

Fixes #2477

What this is — and isn't

This is the data-machine consumer-side guardrail for the agents-api dual-load version-skew fatal. It is not the durable substrate fix. The root cause lives in automattic/agents-api's all-or-nothing AGENTS_API_LOADED guard; the durable fix (idempotent per-class loading) is tracked upstream in Automattic/agents-api#291 and is intentionally out of scope here.

Problem (from #2477)

The agents-api substrate guards its entire class-loading block with one coarse constant:

if ( defined( 'AGENTS_API_LOADED' ) ) { return; }
define( 'AGENTS_API_LOADED', true );
// ... require_once every class file ...

When two different versions of the substrate coexist on a network — data-machine's bundled (vendored) copy and a separately activated standalone agents-api plugin — and the older copy wins the load race, the newer bundled copy early-returns before requiring its newer classes. agent export (and any newer-only class path) then fatals with a bare Class "WP_Agent_Package_Artifact_Hasher" not found. All export profiles (share, backup, fork) were dead.

The guardrail

After the agents-api require block in data-machine.php, run a guardrail that:

  1. Detects the skew: defined('AGENTS_API_LOADED') && ! class_exists('WP_Agent_Package_Artifact_Hasher') (the hasher is the canary).
  2. Self-heals where safe: tops up the missing class files from data-machine's own bundled copy. It is drift-free — it parses the bundled bootstrap's require_once AGENTS_API_PATH . 'src/...' list rather than hardcoding it, and every include is require_once so already-loaded files are skipped and only missing classes are topped up. This makes export work even when an older standalone copy won the race.
  3. Fails loud, not cryptic: if self-heal can't complete, it registers an admin_notices error and (under WP-CLI) calls WP_CLI::error() with a clear message naming the cause and the fix (deactivate/remove the older standalone agents-api plugin, or update it to >= the bundled version). Never degrades into a bare "class not found" fatal.

It does not refactor the bootstrap and does not touch the vendored agents-api files (composer artifact).

Files changed

  • inc/agents-api-guardrail.php (new) — detection + self-heal + clear-error helpers.
  • data-machine.php — require + invoke the guardrail right after the agents-api require block.
  • tests/agents-api-dualload-guardrail-smoke.php (new) — simulates the collision and asserts detection → self-heal → idempotency.

Test results

$ php tests/agents-api-dualload-guardrail-smoke.php
All 9 Agents API dual-load guardrail assertions passed.

$ php tests/agents-api-bootstrap-smoke.php
All 419 Agents API bootstrap assertions passed.

$ php tests/export-agent-ability-smoke.php
All export-agent ability smoke assertions passed. (18)

$ php tests/release-bundled-agents-api-smoke.php
Release bundle smoke passed (10 assertions).

$ vendor/bin/phpcs inc/agents-api-guardrail.php tests/agents-api-dualload-guardrail-smoke.php data-machine.php
EXIT=0

The new smoke simulates the exact collision (define AGENTS_API_LOADED with the canary class absent), then asserts the guardrail detects it, self-heals the canary from the bundled copy, and is idempotent on a second run. (On the live VPS the standalone duplicate has already been removed, so the smoke is the durable way to exercise the guardrail.)

Durable fix tracked upstream

The real fix — idempotent per-class loading independent of the AGENTS_API_LOADED bootstrap constant — is filed at Automattic/agents-api#291. This PR is the guardrail so data-machine fails safely (and self-heals) until the substrate ships that fix.

The agents-api substrate guards its entire class-loading block with a single
coarse AGENTS_API_LOADED constant. When two different versions of the substrate
coexist on one network (data-machine's bundled copy plus a separately activated
standalone agents-api plugin) and the OLDER copy wins the load race, the newer
bundled copy early-returns before requiring its newer classes. Any path touching
a newer-only class (e.g. WP_Agent_Package_Artifact_Hasher during agent export)
then fatals with a bare "Class ... not found".

Add a bootstrap guardrail that detects the skew (constant defined but canary
class absent), self-heals by topping up the missing class files from
data-machine's OWN bundled copy where safe (drift-free: it parses the bundled
bootstrap's require list rather than hardcoding it), and otherwise surfaces a
clear admin notice + WP-CLI error instead of degrading into a cryptic fatal.

The durable fix (idempotent per-class loading) belongs upstream in
Automattic/agents-api and is tracked separately; this is the data-machine
guardrail only.

Fixes #2477
@homeboy-ci
Copy link
Copy Markdown
Contributor

homeboy-ci Bot commented Jun 3, 2026

Homeboy Results — data-machine

Lint

lint — passed

ℹ️ Full options: homeboy docs commands/lint
Deep dive: homeboy lint data-machine --changed-since ec7e05c

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-lint-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-lint-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/26915837376

Test

test — passed

ℹ️ Auto-fix lint issues: homeboy refactor data-machine --from lint --write
ℹ️ Collect coverage: homeboy test data-machine --coverage
ℹ️ Pass args to test runner: homeboy test -- [args]
ℹ️ Full options: homeboy docs commands/test
Deep dive: homeboy test data-machine --changed-since ec7e05c

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-test-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-test-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/26915837376

Audit

audit — passed

  • audit — 1 finding(s)
  • Total: 1 finding(s)

Deep dive: homeboy audit data-machine --changed-since ec7e05c

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-audit-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-audit-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/26915837376
Tooling versions
  • Homeboy CLI: homeboy 0.220.0+04e8886d
  • Extension: wordpress from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: 1f9ab9d7
  • Action: unknown@unknown

- Add the repo's standard narrow phpcs:ignore for the local-file
  file_get_contents read in the bootstrap parser (matches the existing
  convention used across the Bundle classes).
- Align assignment whitespace in the self-heal helper.
- Reword an inline comment to end in a full stop.
- In the smoke test, rename the loop collection off the WPCS global-override
  name list and route the src/-prefix check through the assertion helper instead
  of an interpolated echo, removing the output-escaping and global-override
  violations.
@chubes4 chubes4 merged commit 5ec2077 into main Jun 3, 2026
5 checks passed
@chubes4 chubes4 deleted the fix-export-dualload-guardrail branch June 3, 2026 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: agent export fatals — WP_Agent_Package_Artifact_Hasher not found (agents-api dual-load constant collision)

1 participant