Skip to content

Layer purity: WP plugin substrate hardcodes Data Machine / agents-api consumer names + Extra-Chill release URLs #633

@chubes4

Description

@chubes4

Summary

wp-codebox describes itself as a generic substrate — "Secure coding environments inside WordPress... disposable WP Codebox Playground sandboxes." But its WordPress plugin source names specific downstream consumers (agents-api, data-machine, data-machine-code), ships pinned Extra-Chill/ GitHub release-zip URLs, and compiles a feature against a \DataMachine\... interface. A generic sandbox layer should not know its consumers exist.

The grep test (the standard for layer-purity audits) finds the leak:

grep -riE 'data-machine|datamachine' packages/wordpress-plugin/src

Exact locations (v0.5.0, packages/wordpress-plugin/src/)

1. trait-wp-codebox-abilities-browser-runtime.php

Hardcoded required consumer set (browser_runtime_component_slugs, ~L501-505):

private static function browser_runtime_component_slugs( array $declared_components, bool $include_required ): array {
    $slugs = array();
    if ( $include_required ) {
        $slugs = array( 'agents-api', 'data-machine', 'data-machine-code' );  // <-- consumer names in the substrate
    }
    ...

Default registry seeded with consumer names + vendor release URLs (browser_runtime_component_registry, ~L522-545):

$registry = array(
    'agents-api'        => array( 'url' => 'https://github.com/Automattic/agents-api/releases/latest/download/agents-api.zip', ... ),
    'data-machine'      => array( 'url' => 'https://github.com/Extra-Chill/data-machine/releases/latest/download/data-machine.zip', ... ),
    'data-machine-code' => array( 'url' => 'https://github.com/Extra-Chill/data-machine-code/releases/latest/download/data-machine-code.zip', ... ),
);
if ( function_exists( 'apply_filters' ) ) {
    $registry = apply_filters( 'wp_codebox_browser_runtime_component_registry', $registry );  // <-- filter already exists!
}

2. trait-wp-codebox-abilities-browser-runner.php

Event-sink hard-coupled to a Data Machine interface by name (~L334-335, L1035-1039):

if ( interface_exists( "\\DataMachine\\Engine\\AI\\LoopEventSinkInterface" ) && ! class_exists( "WP_Codebox_Browser_Event_File_Sink" ) ) {
    class WP_Codebox_Browser_Event_File_Sink implements \DataMachine\Engine\AI\LoopEventSinkInterface { ... }
}
...
if ( interface_exists( "\\DataMachine\\Engine\\AI\\LoopEventSinkInterface" ) && class_exists( "WP_Codebox_Browser_Event_File_Sink" ) ) {
    $input['event_sink'] = new WP_Codebox_Browser_Event_File_Sink( $event_path );
}

Why this is a problem

This is the same anti-pattern as a generic transport hardcoding kimaki/discord/telegram: the substrate names the layers above it. Concretely:

  • Adding/removing a runtime consumer requires editing the substrate. A new agent stack can't be supported via config — it needs a code change in wp-codebox source.
  • Vendor-org release URLs are baked in. The Extra-Chill/ URLs are a downstream deployment's concern, not the sandbox's.
  • The \DataMachine\... interface coupling means the substrate compiles a class against a specific consumer's contract. The interface_exists() guard makes it soft (degrades when absent), which is better than a hard dep — but soft-coupling-by-name is still coupling-by-name.

The fix (config, not code)

The mechanism already mostly exists — the wp_codebox_browser_runtime_component_registry filter is right there. Complete the pattern:

  1. Ship empty/neutral defaults. browser_runtime_component_registry() should default to array() (or only truly-generic entries), with consumers registering their own slugs + URLs via the existing filter from the integration plugin (e.g. data-machine / wp-coding-agents register data-machine, data-machine-code, agents-api themselves).
  2. Make the required set filterable too. Replace the hardcoded array( 'agents-api', 'data-machine', 'data-machine-code' ) in browser_runtime_component_slugs() with a filter (e.g. wp_codebox_browser_runtime_required_components) defaulting to array().
  3. Decouple the event sink. Instead of implements \DataMachine\Engine\AI\LoopEventSinkInterface, expose a generic event-sink hook/contract owned by wp-codebox and let Data Machine adapt to it (or register its sink via a filter like wp_codebox_browser_runtime_event_sink). The substrate should define the seam; the consumer implements it.

Net result: grep -riE 'data-machine|datamachine|extra-chill' over packages/wordpress-plugin/src returns clean, and shipping a new agent runtime is a config/filter registration in the consumer plugin — never a code change in wp-codebox.

Scope note

The PHPUnit / run-php / wp-cli / browser-probe / recipe/mount core is already clean and agnostic — no consumer names there. This leak is confined to the agent-runtime browser bridge (the two browser-runtime/browser-runner traits). The fix is well-scoped to those files plus moving the defaults into the consumer plugin.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions