Skip to content

Latest commit

 

History

History
546 lines (453 loc) · 19.6 KB

File metadata and controls

546 lines (453 loc) · 19.6 KB

Recipe Contract

WP Codebox recipes use the wp-codebox/workspace-recipe/v1 schema. A recipe is a declarative sandbox setup plus ordered workflow steps. The CLI validates the recipe before booting WordPress Playground, then returns a structured artifact bundle after execution.

Use the generated schema and command catalog as the source of truth:

npm run wp-codebox -- schema recipe --json
npm run wp-codebox -- commands --json

Validate recipes before using them in CI or documentation examples:

npm run wp-codebox -- recipe validate --recipe ./path/to/recipe.json --json
npm run wp-codebox -- recipe-run --recipe ./path/to/recipe.json --dry-run --json

Minimal Shape

{
  "schema": "wp-codebox/workspace-recipe/v1",
  "runtime": {
    "backend": "wordpress-playground",
    "wp": "latest"
  },
  "inputs": {
    "mounts": [
      {
        "source": "../my-plugin",
        "target": "/wordpress/wp-content/plugins/my-plugin",
        "mode": "readonly"
      }
    ]
  },
  "workflow": {
    "steps": [
      {
        "command": "wordpress.run-php",
        "args": ["code=echo get_bloginfo( 'name' );"]
      }
    ]
  }
}

Stable Field Names

Recipe field names are case-sensitive. The current recipe schema accepts these top-level fields:

  • schema
  • distribution
  • runtime
  • inputs
  • workflow
  • artifacts
  • probes

inputs accepts these fields:

  • mounts
  • workspaces
  • workspace_preloads
  • extra_plugins
  • component_manifest
  • dependency_overlays
  • runtimeEnv
  • secretEnv
  • pluginRuntime
  • fixtureDatabases
  • siteSeeds
  • stagedFiles
  • sourcePackages
  • agent_bundles
  • inherit
  • inheritance

Use inputs.extra_plugins, not inputs.extraPlugins. The camelCase form is an old docs/example drift and is not part of wp-codebox/workspace-recipe/v1.

Use inputs.agent_bundles, not inputs.agentBundles, for runtime agent bundle imports.

Use inputs.workspace_preloads for generic agent-runtime/workspace-preload artifact contracts. WP Codebox materializes declared repositories as sandbox workspace mounts; callers own the policy that decides which artifacts to pass.

Extra Plugins

inputs.extra_plugins mounts additional WordPress plugins before workflow steps run. Each entry requires source and may include slug, pluginFile, activate, loadAs, and sha256.

{
  "inputs": {
    "extra_plugins": [
      {
        "source": "https://downloads.wordpress.org/plugin/bbpress.latest-stable.zip",
        "pluginFile": "bbpress/bbpress.php",
        "activate": false
      },
      {
        "source": "../agents-api",
        "slug": "agents-api",
        "pluginFile": "agents-api/agents-api.php",
        "activate": false,
        "loadAs": "mu-plugin"
      }
    ]
  }
}

Supported loadAs values:

  • plugin: mount below /wordpress/wp-content/plugins/<slug> and activate when activate is not false.
  • mu-plugin: mount below /wordpress/wp-content/mu-plugins/wp-codebox-runtime/<slug> and load through WP Codebox's generated MU-plugin loader. Use this for sandbox runtime substrate, not user-visible plugins.

External HTTPS zip downloads are gated by WP_CODEBOX_ALLOW_NETWORK_DOWNLOADS=1. Local paths are resolved relative to the recipe file.

Source Packages

inputs.sourcePackages materializes local recipe source directories below the sandbox workspace. It is directory-only; use inputs.stagedFiles for single-file staging. Each package declares a stable name, local source, workspace-relative target, optional allow/deny path filters, and optional provenance artifact registration.

{
  "inputs": {
    "sourcePackages": [
      {
        "name": "fixture-plugin",
        "source": "./fixtures/plugin",
        "target": "packages/fixture-plugin",
        "allow": ["src*", "composer.json"],
        "deny": ["src/secrets*"],
        "artifact": true
      }
    ]
  }
}

Targets are normalized under /workspace when they are not already absolute workspace paths. Materialization uses WP Codebox prepared-source exclusions, applies deny before allow, hashes the copied package, and writes .wp-codebox-source-package.json into the staged package root with source, filter, target, digest, and timestamp provenance. Validation emits blockers for unsafe names, paths, and filters before runtime setup.

recipe build template compiles declarative source package input into generated artifact declarations while preserving inputs.sourcePackages, so callers use the same validation and run path as hand-written recipes without duplicate mounts.

npm run wp-codebox -- recipe build template --options ./template-options.json --output ./recipe.json

Workflow Steps

Workflow steps are ordered command invocations:

{
  "command": "wordpress.wp-cli",
  "args": ["command=option get home"]
}

Each args entry is a string. Command-specific argument names and repeatability come from the command catalog:

npm run wp-codebox -- commands --json

Use allowFailure: true or advisory: true for evidence-only workflow steps. Failed advisory steps are reported in advisoryFailures and do not make an otherwise successful recipe return success: false.

REST Benchmark Workloads

Recipes can pass REST profiling workloads to wordpress.bench with workloads-json. A workload may use route_matrix to declare a bounded set of REST requests; the runtime expands each route into the existing rest-request step and returns normal benchmark scenarios/artifacts.

{
  "command": "wordpress.bench",
  "args": [
    "plugin-slug=woocommerce",
    "iterations=3",
    "warmup-iterations=1",
    "workloads-json=[{\"id\":\"rest-catalog\",\"source\":\"config\",\"route_matrix\":[{\"id\":\"products-list\",\"method\":\"GET\",\"path\":\"/wc/v3/products\",\"params\":{\"per_page\":10}}],\"artifacts\":{\"route-summary\":{\"path\":\"bench/rest-route-summary.json\",\"kind\":\"json\",\"source\":\"scenario-artifact\"}}}]"
  ]
}

Use site seeds, fixture databases, or staged files for product-specific data setup. Use artifacts on the workload for declared per-scenario outputs that downstream lab tooling should extract from benchResults or an artifact bundle.

Fixture Imports And Bootstrap Declarations

inputs.siteSeeds is the generic fixture import primitive. JSON fixture seeds import through WordPress APIs for posts, options, terms, users, media, active plugins, and active theme declarations. Those APIs can make semantic identifiers stable, such as post slugs, term slugs, option names, user logins, plugin files, and theme stylesheets. They cannot guarantee numeric primary keys.

Use deterministicIds to make that boundary explicit:

{
  "inputs": {
    "siteSeeds": [
      {
        "type": "fixture",
        "name": "demo-content",
        "source": "fixtures/content.json",
        "format": "json",
        "deterministicIds": {
          "strategy": "platform-identifiers",
          "onUnsupported": "block"
        },
        "scopes": {
          "posts": { "slugs": ["home"] },
          "options": { "names": ["blogname"] }
        }
      }
    ]
  }
}

When onUnsupported is block, WP Codebox reports a validation/runtime blocker if the generic importer sees numeric id/ID fixture fields or a recipe requests the numeric strategy. Custom importers may add format-specific support later, but the generic contract stays honest about what platform APIs can guarantee.

Recipes may also attach reusable bootstrap declarations to a site seed:

{
  "bootstrap": {
    "multisite": {
      "enabled": true,
      "install": "subdomain",
      "sites": [{ "domain": "example.test", "path": "/", "title": "Example" }]
    },
    "domains": [{ "domain": "example.test", "path": "/", "primary": true }]
  }
}

These are generic declarations for orchestrators and future runtime setup support. The current built-in fixture importer treats multisite/domain bootstrap as declared metadata and blocks executable fixture imports that require runtime setup not yet provided by WP Codebox.

{
  "command": "wordpress.browser-actions",
  "args": ["url=/", "steps-json=[...]"],
  "advisory": true
}

Fixture Browser Auth Storage State

Hosts that need an authenticated browser session for a disposable WordPress sandbox can use the playground package's fixture auth storage-state helpers instead of baking product-specific site identifiers into recipes. The helper contract is generic: resolve or create a named WordPress fixture user inside the sandbox, mint short-lived WordPress admin cookies for declared browser origins, and return a Playwright-compatible storageState envelope:

import { wordpressFixtureUserStorageStatePhpCode } from "@automattic/wp-codebox-playground"

The emitted PHP returns wp-codebox/browser-auth-storage-state/v1:

{
  "schema": "wp-codebox/browser-auth-storage-state/v1",
  "kind": "wordpress-fixture-user-admin-auth",
  "user": {
    "id": 1,
    "username": "wp-codebox-fixture-admin",
    "email": "wp-codebox-fixture-admin@example.test",
    "role": "administrator",
    "created": true
  },
  "storageState": {
    "cookies": [],
    "origins": []
  }
}

Recipes can export that state as an artifact with the generic command:

{
  "command": "wordpress.export-browser-storage-state",
  "args": [
    "browser-urls=http://127.0.0.1:9400,https://preview.example.test",
    "user-json={\"username\":\"fixture-admin\",\"role\":\"administrator\"}"
  ]
}

The command also accepts caller-produced state with storage-state=<json> or storage-state=@./state.json. That value may be a raw Playwright storageState object or the wp-codebox/browser-auth-storage-state/v1 envelope shown above, so product-specific PHP bootstrap code can stay outside WP Codebox and hand the resulting generic browser state to the exporter.

The command returns wp-codebox/browser-storage-state-export/v1 with artifacts.storageState and artifacts.summary. The storage-state artifact is the token-bearing Playwright JSON and is marked redactionRequired in artifactRefs; the summary artifact and command output contain only reviewer-safe metadata: schema/kind, user id/name/email/role/created flag, cookie counts by host, origin count, and diagnostics. Product policy, production identifiers, and cross-site account mapping stay outside WP Codebox.

wordpress.browser-probe and wordpress.browser-actions can import a reusable storage state with storage-state=<json> or storage-state=@./state.json. The value may be a raw Playwright storageState object or the wp-codebox/browser-auth-storage-state/v1 envelope shown above. WP Codebox passes the state directly to a fresh Playwright context and records only redacted provenance in browser summaries: source type, optional schema/kind, cookie count, cookie hosts, origin count, and diagnostics. Cookie values and localStorage values are never written to browser evidence artifacts.

Use one browser authentication source per command. auth=wordpress-admin creates short-lived in-memory cookies from the disposable WordPress sandbox; storage-state imports caller-provided reusable state. Supplying both is rejected with structured storage-state diagnostics rather than silently preferring one source.

Recipe Output Evidence

recipe-run --json returns wp-codebox/recipe-run/v1. Browser command sidecars are promoted into browserEvidence so callers can discover stable evidence without hardcoding artifact paths or parsing command stdout.

The same output includes result, a normalized wp-codebox/recipe-run-summary/v1 envelope for portable callers. It groups common artifact refs, command stdout/stderr tails, run/runtime metadata, failure phase and summary, and held-preview reviewer access. Consumers should prefer result before scraping latest-runtime.json, commands.jsonl, command stdout, or preview internals from the artifact bundle.

Each browserEvidence entry includes the workflow phase/index, command, summary file, artifact file refs, summary payload, and scriptResult when the browser command produced one. The same browser evidence is mirrored into latest-runtime.json under the run artifact pointer.

When a browser command captures network evidence, WP Codebox writes the raw network.jsonl stream and a derived waterfall.json artifact using the stable wp-codebox/browser-waterfall/v1 schema. The waterfall is HAR-style JSON with redacted URLs, request/response status, resource type, sizes, and timing fields; both files are registered as redaction-required browser artifacts and surfaced through browserEvidence.files.

--preview-hold-seconds <duration> records held-preview lifecycle metadata in the artifact bundle and returns after recipe work finishes. Use --preview-hold-blocking only for operator workflows that need the CLI process to keep a live preview server open for the hold duration.

Generic Host Command Primitive

Hosts that expose recipe/task runner commands through WP Codebox should use the core executeHostCommand() primitive instead of hand-rolled process wrappers. It runs one command without shell expansion, enforces allowed working-directory and environment policy, times out long-running work, terminates the spawned process group, samples process-tree RSS, and returns a structured result with:

  • failureClassification: none, timeout, non_zero_exit, or signal.
  • commandSummary: a compact command line for human diagnostics.
  • Bounded stdout and stderr fields plus outputTruncated.
  • memorySamples and peakRssBytes for runner watchdog evidence.
  • Optional artifacts.stdout, artifacts.stderr, and artifacts.summary refs when artifactsDirectory is provided.

The summary artifact uses wp-codebox/host-command-summary/v1. Product-specific orchestrators can wrap this primitive, but should keep WPCOM, CI-provider, and deployment semantics outside the generic runner layer.

WordPress PHPUnit Runtime

wordpress.phpunit is the lightweight WP Codebox equivalent of plugin PHPUnit commands commonly run through wp-env run ... vendor/bin/phpunit. It boots a disposable WordPress Playground runtime, mounts the tested plugin at /wordpress/wp-content/plugins/<plugin-slug>, prepares the WordPress PHPUnit contract, and captures command output in the artifact bundle.

The runtime provides:

  • WP_TESTS_DIR pointing at the configured WordPress tests library.
  • WP_TESTS_CONFIG_FILE_PATH and WP_PHPUNIT__TESTS_CONFIG pointing at the generated wp-tests-config.php.
  • An isolated SQLite test database via DB_NAME=':memory:'.
  • A plugin working directory via cwd=<sandbox path>, matching the practical role of wp-env run --env-cwd.
  • Structured diagnostics in the recipe artifact bundle, including the raw test result log collected from /tmp/wp-codebox-phpunit-result.txt.

Use recipe build phpunit when generating recipes for plugin CI or offloaded lab runners:

{
  "pluginSlug": "woocommerce",
  "pluginSource": "../woocommerce/plugins/woocommerce",
  "cwd": "/wordpress/wp-content/plugins/woocommerce",
  "autoloadFile": "/wp-codebox-vendor/autoload.php",
  "testsDir": "/wp-codebox-vendor/wp-phpunit/wp-phpunit",
  "bootstrapMode": "project",
  "projectBootstrap": "tests/legacy/bootstrap.php",
  "phpunitArgs": ["--filter", "WC_Checkout_Test::test_checkout"]
}
npm run wp-codebox -- recipe build phpunit --options ./phpunit-options.json --output ./phpunit.recipe.json
npm run wp-codebox -- recipe-run --recipe ./phpunit.recipe.json --artifacts ./artifacts/phpunit --json

Stable Recipe-Builder API

Tools that need to generate WordPress bench or PHPUnit recipes without shelling out to the CLI may import the supported recipe-builder module:

import {
  buildWordPressBenchRecipe,
  buildWordPressPhpunitRecipe,
} from "@automattic/wp-codebox-core/recipe-builders"

The same subpath is exported by the root release package as wp-codebox-workspace/recipe-builders, so callers can use one documented module name for npm package installs and release-tarball installs without probing packages/runtime-core/dist/index.js or other internal build paths. Local checkout consumers should run npm run build first, then import the same workspace package subpath through normal Node package resolution.

The CLI surface remains the stable process boundary for hosts that prefer JSON over an in-process import:

wp-codebox recipe build phpunit --options ./phpunit-options.json --output ./phpunit.recipe.json
wp-codebox recipe build bench --options ./bench-options.json --output ./bench.recipe.json
wp-codebox recipe build generic-ability-runtime-run --options ./ability-runtime-options.json --output ./ability-runtime.recipe.json

All builders return wp-codebox/workspace-recipe/v1 recipes. The generated recipes should still be validated with wp-codebox recipe validate or wp-codebox recipe-run --dry-run before execution in CI.

generic-ability-runtime-run is the stable preset for callers that need to run a WordPress Ability in a disposable runtime with generic component/provider contracts. The options JSON maps to buildGenericAbilityRuntimeRunRecipe and accepts abilityId, optional abilityInput, components, providerPlugins, providerPluginPaths, runtimeOverlays, runtimeEnv, secretEnv, toolPolicy, expectedResultSchema, mounts, runtimeStackMounts, stagedFiles, and verifySteps. The generated workflow invokes wordpress.ability with runtime invocation metadata and, when expectedResultSchema is supplied, recipe-run --json records the canonical wp-codebox/generic-ability-runtime-run-result/v1 command output with the ability result and discovered evidenceEnvelope.

For monorepos such as WooCommerce, set pluginSource to the directory that should appear as wp-content/plugins/<plugin-slug> and set cwd to the same sandbox directory a wp-env --env-cwd command would use. Relative cwd values resolve inside the mounted plugin directory.

Browser Assertions

wordpress.browser-probe accepts repeated assert=<assertion> arguments. Supported assertion forms are the current command contract:

  • exists:<selector>
  • not-exists:<selector>
  • visible:<selector>
  • hidden:<selector>
  • count:<selector><op><number>
  • text:<selector> contains <text>
  • attr:<selector>[name][=value]
  • no-console-errors
  • no-page-errors
  • no-errors
  • request-count-by-host:<host><op><number>
  • request-count-by-type:<type><op><number>
  • total-transfer-size<op><number>
  • metric budgets such as lcp_ms<=2500, fcp_ms<=1800, ttfb_ms<=800, and nav_duration_ms<=5000

Prefix an assertion with advisory: to record a failing assertion without failing the command.

Do not use assert=script:passed equals true. That syntax is not supported by the current WP Codebox browser assertion contract. Use wordpress.browser-actions with an evaluate step and assert value when a page script must prove state:

{
  "command": "wordpress.browser-actions",
  "args": [
    "url=/wp-admin/tools.php?page=my-plugin",
    "steps-json=[{\"kind\":\"evaluate\",\"expression\":\"window.myPluginReady === true\",\"assert\":true}]"
  ]
}

Recipes that use a browser-actions evaluate step require the runtime policy to allow wordpress.browser-actions.evaluate in addition to wordpress.browser-actions.

Keeping Examples Current

When editing docs or example recipes:

  • Prefer generated schema output over hand-written field lists.
  • Run recipe validate against every recipe changed.
  • Run recipe-run --dry-run --json when changing mounts, workspaces, extra_plugins, secrets, or workflow args.
  • Keep product-specific orchestration, scoring, PR creation, and deployment out of recipes. Parent control planes own those behaviors.