Skip to content

Add test_function to execution tests - #24

Merged
lezama merged 3 commits into
trunkfrom
adds-required-functions
Jul 9, 2026
Merged

Add test_function to execution tests#24
lezama merged 3 commits into
trunkfrom
adds-required-functions

Conversation

@JasonTheAdams

Copy link
Copy Markdown
Member

Intent

Execution tests need a gateway function the verifier can call, but that function is harness scaffolding — not WordPress knowledge worth scoring, and not something the model should have to guess. Several tests invoked functions with arguments or return shapes the prompt never described (e.g. `e-queries-009`'s `bool $previous` flag, `e-post-types-taxonomy-005`'s `$name` argument), making them unfair. This PR makes the entry-point contract explicit, machine-checked, and unscored.

What changed

Harness (first commit):

  • New optional `test_function` schema field: the PHP signature of the entry point the verifier calls.
  • Rendered to the model as a `Define this function: ` line after the requirements.
  • The harness derives a weight-0 runtime `function_exists` assertion from the name — pure diagnostics, no score. A missing gateway already fails the behavioral assertions on its own; scoring weight stays on behavior (runtime assertions) and expert idiom (static patterns).
  • Exported as a plain string column in the Parquet dataset.

Datasets (second commit):

  • All 28 execution suites migrated: 124 tests gained `test_function` (signatures derived from reference solutions, so they are call-compatible with assertions by construction); 26 tests without a gateway function are unchanged.
  • Prompts reworded to natural task statements — the function name/signature no longer appears in prompts; `test_function` owns the naming.
  • Boilerplate "Defines the requested function" static patterns removed (the derived runtime check replaces them, formatting-agnostic).
  • New suite lints: assertions invoking a `wpbp_*` function require a matching `test_function`, and the name may not appear in the prompt or static patterns — the unfair-test bug class is now a CI failure.

Verification

  • `ruff` clean; `mypy` shows only pre-existing errors in untouched files
  • `pytest python` — 27 passed (includes the new suite lints)
  • `wp-bench run --check-reference-solution --test-type execution` — all 150 reference solutions pass at 1.0 through the live wp-env verifier, confirmed both positive and negative paths (a submission missing the gateway scores 0 with a clear diagnostic)
  • `python datasets/export_dataset.py` — 470 tests exported with the new column
  • Every reworded prompt was diffed against its original for lost content words; no behavioral details were dropped

🤖 Generated with Claude Code

JasonTheAdams and others added 2 commits July 7, 2026 20:54
Adds an optional test_function schema field holding the PHP signature of
the entry point the verifier calls. The harness renders it to the model
as a 'Define this function:' line and derives an unscored runtime
function_exists assertion from the name, so the gateway function is
communicated and diagnosable without earning score or requiring a
static pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Moves every gateway function signature out of prompts and static_checks
into test_function across all 28 execution suites (124 tests migrated,
26 without a gateway function unchanged). Prompts now describe the task;
test_function owns the entry-point contract, including previously
undocumented signatures the verifier calls with arguments.

Adds suite lints enforcing the contract: assertions invoking a wpbp_*
function require a matching test_function, and the name may not appear
in the prompt or static patterns. All 150 reference solutions pass
--check-reference-solution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: JasonTheAdams <jason_the_adams@git.wordpress.org>
Co-authored-by: lezama <migueluy@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

# Conflicts:
#	python/tests/test_reference_solution_mode.py
#	python/wp_bench/core.py
@JasonTheAdams
JasonTheAdams requested a review from lezama July 8, 2026 03:03
@lezama

lezama commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

verified live, all 150 ref solutions pass at 1.0, function_exists check stays weight-0 like you said, and missing-gateway drops to 0 with a clean diagnostic. 🙌 merging

@lezama
lezama merged commit 456b6a9 into trunk Jul 9, 2026
1 check passed
@lezama
lezama deleted the adds-required-functions branch July 9, 2026 21:36
lezama added a commit that referenced this pull request Jul 17, 2026
…its (#43)

## What

Hardens the 8 execution tests that the #41 exploit audit
(`--check-exploits`) flagged as exploitable: a zero-effort constant
return (`return true;`, `return 1;`, `return false;`) satisfied their
runtime assertions.

All 8 shared the same root cause: the assertion graded the function's
return value against a single fixed expectation instead of observing
WordPress state, so it could not tell a real implementation from a
hard-coded answer.

## How each test is hardened

Every assertion now verifies behavior a constant cannot fake:

| Test | Old weakness | New verification |
|---|---|---|
| `e-ai-client-001` | `return false` passed | Counting spy on the
`wp_supports_ai` filter proves the support state is actually read during
the call, plus the existing re-enabled-after check |
| `e-ai-client-005` | `return false` passed | Late spy (`PHP_INT_MAX`)
on `wp_ai_client_prevent_prompt` proves prevention is observed **active
during** the support check, and inactive after |
| `e-comments-users-004` | `return 1` passed | Second fixture post with
zero comments; expected counts become `{2, 0}`, so no single constant
passes both calls |
| `e-connectors-api-001` | `return true` passed | Helper is called
twice: with Akismet registered (expects `true`) and temporarily
unregistered via `WP_Connector_Registry::unregister()` (expects
`false`), then restored |
| `e-post-types-taxonomy-001` | assertion returned the function's own
`$ok` | Inspects `get_post_type_object( 'wpbp_book' )`: exists,
`public`, `show_in_rest` |
| `e-post-types-taxonomy-002` | same | Inspects `get_taxonomy(
'wpbp_genre' )`: exists, `hierarchical`, attached to `post` |
| `e-post-types-taxonomy-003` | assertion was literally `return f();` |
Fixtures (`wpbp_movie`, `wpbp_topic`) move to runtime
`setup`/`teardown`; assertion checks the actual attachment with
`is_object_in_taxonomy()` |
| `e-roles-caps-002` | `return true` passed | Spy on `updated_option`
for the roles option observes `wpbp_temp_cap` actually being **added**
to Subscriber before the final not-present check |

## Design decisions

- **Slugs moved into prompts.** Models only see `prompt` +
`requirements` + `test_function` (never `expected_behavior`), so
state-based assertions on `wpbp_book` / `wpbp_genre` / `wpbp_topic` /
`wpbp_movie` require the prompts to name them. Done for the three
post-types tests.
- **Dropped the "Clean up registered types when possible" requirement**
on `e-post-types-taxonomy-001/002`. A post-call state inspection
requires the registration to still exist when the assertion runs;
cleanup is now the assertion's job (as the reference solutions already
assumed).
- **`e-connectors-api-001` keeps its contract** (no-arg helper,
`akismet` required pattern, `WP_Connector_Registry` forbidden for the
model). The registry-toggle happens in assertion code, which static
checks don't apply to. A lazy-init spy on `wp_connectors_init` was tried
first but the registry initializes during `init`, before assertions run.
- **`e-connectors-api-002`-style negative cases** were preferred over
spies wherever the API allowed it; spies are only used where the
observable is a filter/option side effect (`ai-client`, `roles-caps`).
- Each touched suite file bumps `2.1.0` → `2.2.0`, same convention as
#24.

## Verification (live WP 7.0 runtime)

Both directions of the audit loop, against the real wp-env grader:

- `--check-reference-solution` on the 8 tests: **8/8 pass** (correctness
1.0)
- `--check-exploits` on the 8 tests: **0/8 exploitable**
- Full pytest suite: 151 passed; dataset validation green

- `--check-exploits` on the full suite: **0/140 auditable tests
exploitable** (was 8/140 at the #41 baseline). Full end-to-end run: 1120
exploit-candidate executions, each against a freshly reset WordPress,
~1.5h wall clock. The 10 non-auditable tests have no `test_function` or
plugin artifact for the generic battery to target, unchanged from the
baseline.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants