Turn a one-line hypothesis about php-src into a measurable prototype that proves or disproves the idea. Use when the user says "I have an idea for php-src", "prototype this idea", "/idea-prototype", "let's see if X would speed up Y", "what if PHP supported …", "explore whether …", or otherwise wants to test feasibility/sense of a php-src change before committing to an RFC. Drives a SPEC.md through Context → Hypothesis & Metric → Survey & Plan → Prototype → Measurement & Verdict. Resumable — re-run the same command to advance to the next phase.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Turn a one-line hypothesis about php-src into a measurable prototype that proves or disproves the idea. Use when the user says "I have an idea for php-src", "prototype this idea", "/idea-prototype", "let's see if X would speed up Y", "what if PHP supported …", "explore whether …", or otherwise wants to test feasibility/sense of a php-src change before committing to an RFC. Drives a SPEC.md through Context → Hypothesis & Metric → Survey & Plan → Prototype → Measurement & Verdict. Resumable — re-run the same command to advance to the next phase.
idea-prototype
Single skill that drives a php-src idea prototype from a one-liner to a verdict. Like the bug flow, it's phased and resumable; unlike the bug flow, one skill owns all phases. Each invocation advances one phase forward (or resumes a blocked phase).
Inputs
A one-line hypothesis (required on first run only) — e.g. "caching common zend_string parameter names could speed up function calls", "adding pattern syntax X would simplify Y", "replacing the linear scan in zend_hash_str_find with sorted lookup helps tiny tables".
$PHP_SRC_DIR with built sapi/cli/php.
SPEC.md in the task directory (resolved per _shared/task-dir.md) — created on the first run, read on subsequent runs.
What it produces
SPEC.md (type: idea) in the task directory resolved per _shared/task-dir.md (default $PHP_SRC_DIR/.claude/php-core-dev/idea-<slug>/SPEC.md). Tracks the hypothesis end to end.
Prototype C-code changes under $PHP_SRC_DIR — explicitly throwaway, not production-ready.
Benchmark scripts and/or .phpt examples demonstrating the idea.
A pursue-as-rfc / reframe / kill verdict in the final phase.
Phases owned
#
Phase
Post-guardrail
1
Context
Hypothesis restated in own words; subsystem identified
2
Hypothesis & Metric
Success metric is concrete and falsifiable
3
Research
At least one prior-art source ran (owned by /research, hard-gated for idea flow)
4
Survey & Plan
Smallest viable prototype change identified
5
Prototype
make -j$(nproc) exits 0 with the patch applied; build is clean
6
Measurement & Verdict
Numbers (or examples) recorded; honest verdict written
This skill owns all six phases except Research, which is owned by /research. On each invocation /idea-prototype picks up the first non-done phase and advances it; when the next phase is Research it refuses and instructs the user to run /research (or to mark the phase skipped in SPEC.md with a reason). Research is hard-gated for the idea flow — Survey & Plan will not start until Research is done or skipped. This differs from the bug flow, which soft-warns and proceeds.
Workflow
0. Resume logic
Resolve task dir per _shared/task-dir.md. On first run (no SPEC.md anywhere reachable):
Hypothesis argument is required.
Derive slug idea-<kebab> from the first ~5 significant words of the hypothesis (lowercased, kebab-cased). Example: "caching common zend_string parameter names …" → idea-caching-common-zend-string-parameter.
Create $PHP_SRC_DIR/.claude/php-core-dev/<slug>/, ensure the leaf .gitignore, write the slug to .current.
On subsequent runs:
Resolve task dir (env var → .current → cwd).
If SPEC.md exists with type: idea: read it, find first non-done phase, run that phase. The hypothesis argument is ignored.
If SPEC.md exists with type: bug: refuse — idea-prototype expects an idea SPEC; current task is a bug. Switch tasks or use PHP_CORE_TASK_DIR.
If SPEC.md does not exist at the resolved dir: treat as first run (require hypothesis).
Phase 1 — Context
Write SPEC.md with frontmatter (type: idea, title: set to the hypothesis, php_src_ref: from git -C "$PHP_SRC_DIR" rev-parse HEAD).
Write ## Context with the following required subsections (each a markdown heading or bolded label inside the Context body):
Restate — hypothesis as a problem or opportunity, not an echo of the one-liner.
Who benefits — runtime perf, DX, code clarity, maintenance. Name concrete user groups.
Who pushes back — internals contributors, BC-sensitive consumers, anyone whose existing code or workflow this disrupts. Keep this even when the next subsection lists adversarial points — this subsection is about people, the next is about arguments.
Userland today — how people accomplish this today without the proposed change. 2–4 concrete examples from frameworks, extensions, PECL, polyfills, or composer packages. Include verbatim signatures or a short code snippet for at least one, with a link to the source. If no userland workaround exists, state so explicitly — that itself is evidence.
Cross-language precedent — closest analogue in 2–3 other runtimes (Rust, Python, Ruby, JS, Go, Elixir, Swift, …). For each: name + one-line description + link to its docs/PEP/RFC. "None — no major language has this" is a valid answer and worth recording; it changes the RFC framing materially.
Pros vs Cons — explicit two-column or two-list table of expected arguments for and against the change, 3–5 each. This is the controversy preview the eventual RFC will need anyway. Be honest with the cons — a thin AGAINST list is a smell, not a strength. Suggested format:
| Pro | Con |
|---|---|
| <argumentfor> | <argumentagainst> |
| ... | ... |
Subsystems involved — name the directories/files (Zend/, ext/<name>/, opcache, jit, …) and one-line per subsystem on what role it plays.
Out of scope for this prototype — what the prototype deliberately won't tackle (configurability, RFC drafting, fancier dumpers, etc.). Future-proofs the Survey & Plan against scope creep.
Pre-declare the remaining four phase skeletons with their guardrails. Mark Context done.
Post-guardrail (in addition to the existing one): subsections 1–8 are all present and non-empty. If any subsection is genuinely N/A for this hypothesis, write the reason in one line — don't silently omit.
Progress Log: <datetime> — idea-prototype — Context written from hypothesis.
Report: Context written. Run /idea-prototype again to advance to Hypothesis & Metric.
Phase 2 — Hypothesis & Metric
The most important phase. The metric must be falsifiable — defined sharply enough that the Measurement phase can return a clean yes/no.
Examples of good metrics:
"≥5% reduction in time-per-iteration on Zend/bench.php's simple_call benchmark, repeated 5 runs, 95% CI excludes 0%"
"Two example scripts that today produce error X compile cleanly and produce the obvious result"
"Zero new compiler warnings, RSS delta < 1 MB on the same Zend/bench.php workload"
Examples of bad metrics:
"It's faster" — no number, no workload
"It feels cleaner" — not falsifiable
"It might help" — not measurable
Steps:
metric — record the metric.
workload — record the workload (specific script, specific inputs, specific PHP version).
Post-guardrail: re-read the metric. If it could be defended both ways no matter what the prototype produces, refuse — block, raise an Open Question, stop. Don't accept fuzzy metrics.
Phase 3 — Research
Owned by the /research skill, not this one. Hard-gated for the idea flow. When /idea-prototype reaches a Research phase that is todo, it refuses to continue and tells the user:
Research phase is required before Survey & Plan in the idea flow.
Run /research now to sweep prior art, or mark the phase `skipped`
in SPEC.md with a reason to bypass.
When the user runs /research, it populates the Research phase (keywords + 4 source-steps + Notes) and marks it done. See skills/research/SKILL.md and _shared/search-strategies.md for details.
If Research is done or skipped, this skill advances to Survey & Plan and consults the Research findings while planning. (The skipped path requires a reason recorded in SPEC.md — /idea-prototype reads it but doesn't enforce content.)
Phase 4 — Survey & Plan
Pre-guardrail: Research phase status is done or skipped. Otherwise refuse with the Phase 3 message above — do not start Survey & Plan with Research still todo.
Steps:
subsystem-map — read the relevant files (Grep + Read) and identify the smallest code change that exercises the hypothesis. Throwaway by design — not production-ready. List the files you intend to touch. Cite Research findings where relevant (e.g. Per Research's gh-prs step, PR #54322 attempted this in 2022; the closer's comment names a BC concern we should avoid.).
design-space — enumerate the design dimensions this idea has in its full-RFC form, and for each one record the option this prototype is taking vs the options deferred to RFC discussion. Format as a table:
Purpose: document what the prototype is not deciding, so the Verdict can honestly say "the prototype validated dimensions X, Y; dimensions Z, W are still open." Without this, prototypes silently take positions on N dimensions and the RFC inherits all of them as faits accomplis.
counter-position-binding — for each AGAINST argument recorded in Context's Pros vs Cons subsection and each objection surfaced in the Research phase's Notes (by name where the objector is named — e.g. "Grekas's freeze-innovation argument", "Vollebregt's namespace-collision argument"), record explicitly:
addressable by prototype evidence? yes / partially / no
what evidence would address it? one sentence
Purpose: forces the prototype to know in advance which objections its output can answer and which it cannot. An objection marked no is a known dead-zone — the Verdict should not claim to have answered it.
plan-1, plan-2, … — concrete planned edits.
Post-guardrail: every plan step references a concrete file. Smallest viable prototype, not a full implementation. design-space and counter-position-binding are both populated.
Phase 5 — Prototype
Steps:
test-modification-check — if any impl-N step writes to an existing .phpt (anything not freshly added by this SPEC), run the provenance lookup in _shared/test-modification-policy.md and add the ## Test-modification justification: <path> section to SPEC.md before applying changes. Block the phase if any of the five required bullets is missing.
impl-1, impl-2, … — one per plan-N. Apply the changes with Edit. Match existing style (_shared/php-coding-standards.md) even for throwaway code — a wildly off-style prototype distorts review. Existing .phpt modifications are gated by the test-modification-check step above.
build — make -j$(nproc) exits 0.
commit — stage every modified php-src path from impl-* steps and commit on the current branch:
git -C "$PHP_SRC_DIR" add <each-impl-path>
git -C "$PHP_SRC_DIR" commit -m "$(cat <<'EOF'
idea(<slug>): prototype <one-line subject>
<one-paragraph body from Hypothesis & Metric — what is being tried
and what would prove it. Mark explicitly as prototype / throwaway.>
Prototype-of: <slug>
EOF
)"
Record the commit SHA in the commit step body and Progress Log.
Don't worry about test coverage or BC. Worry about: does the patch compile, does it produce a binary, can the metric be measured against it.
Post-guardrail: make -j$(nproc) exits 0; no new compiler errors; commit SHA recorded.
Phase 6 — Measurement & Verdict
Depends on the metric type:
Performance metric:
Write a benchmark script (or reuse Zend/bench.php / ext/opcache/tests/bench). Place it under <task-dir>/bench/ (inside the gitignored task dir) for transient runs, or at a stable path under $PHP_SRC_DIR/benchmark/ if it should be committed.
Run baseline (no patch): undo the Prototype commit's changes temporarily with git -C "$PHP_SRC_DIR" checkout <prototype-parent-sha> -- <impl-paths> (paths only — keep the bench script in place). Rebuild, run 5+ times, capture mean/stddev.
Restore the prototype: git -C "$PHP_SRC_DIR" checkout <prototype-commit-sha> -- <impl-paths>. Rebuild, run benchmark same way.
Compute delta. If the metric defines a CI, compute it (e.g. with bc/python/awk).
Steps:
baseline-run — numbers recorded.
patched-run — numbers recorded.
delta — computed; verdict pre-recorded.
DX/syntax metric: write 2–4 .phpt examples (or .php scripts) that exercise the idea. Run them on the patched build. Record output. Steps: examples + verify.
Finally:
verdict — pursue-as-rfc / reframe / kill.
rfc-scaffold — required iff verdict is pursue-as-rfc; skip with status skipped otherwise. A scaffold of notes the user can copy into the wiki.php.net RFC. This is explicitly not the RFC — no proposed-PHP-Version line authoring, no formal vote section, no full prose. Just the structured stub the user pastes in and fleshes out. Required subsections:
### RFC scaffold (notes for wiki.php.net)-**Proposed title**: <oneline>-**Target PHP version**: <e.g.8.6, 9.0 — picktheconservativechoice>-**Selling points** (3–5): pull from Context's Pro list + measured prototype evidence. Each one references the specific Measurement step that supports it.
-**Hardest objections to pre-empt** (2–3): pull from Context's Con list + Research-surfaced objector names. For each, the prototype's evidence-stance — "addressed by `delta` step showing X", or "not addressed; needs separate discussion".
-**Alternative names / namespacings considered**: pull from Survey & Plan's `design-space` "Deferred to RFC" column.
-**Open dimensions for RFC discussion**: every row in `design-space` whose Prototype-takes column is a free choice (not forced by measurement). The RFC author needs to argue for each.
-**References**:
- The internals@ thread(s) found in Research (URLs verbatim).
- The prototype commit SHA(s).
- The cross-language precedents named in Context.
Boundary: rfc-scaffold records notes; it does not generate the wiki page text, does not write a "Proposed PHP Version" header, does not draft a vote section. If the user wants RFC drafting, that's the human's job — see Out of scope.
Verdict guidance:
pursue-as-rfc — the metric held up cleanly; the idea is worth a wiki.php.net RFC and internals@ discussion. Drafting the RFC itself is out of scope for this skill (but rfc-scaffold notes are required to bridge to the wiki).
reframe — the metric held up partially or the metric itself was off; record what the next version of the hypothesis should be. rfc-scaffold is skipped.
kill — the metric failed and you don't see a path to make it hold. Negative results are valuable. rfc-scaffold is skipped.
Be honest. A "kill" verdict after a clean Measurement is a success of the skill.
Post-guardrail: the verdict is one of the three values; the evidence (numbers or examples) is recorded in SPEC.md.
Commit benchmark + DX artifacts (only if there is anything stageable under php-src — bench scripts under $PHP_SRC_DIR/benchmark/ or example .phpts under the relevant tests dir):
git -C "$PHP_SRC_DIR" add <bench-script-paths> <example-phpt-paths>
# Skip the commit if nothing is staged:
git -C "$PHP_SRC_DIR" diff --cached --quiet || git -C "$PHP_SRC_DIR" commit -m "$(cat <<'EOF'
idea(<slug>): bench + verdict <pursue-as-rfc|reframe|kill>
<one-paragraph: hypothesis, metric, observed delta or example outcome,
verdict reasoning>
Verdict: <pursue-as-rfc|reframe|kill>
Prototype-of: <slug>
EOF
)"
Record the commit SHA (if a commit happened) in the delta / verify step body. If nothing was stageable (all evidence lives only in the gitignored SPEC.md), record "no commit — evidence in SPEC.md only" and proceed.
Mark Measurement & Verdict done. Flip SPEC.md top-level status: done.
Boundaries
Throwaway prototype, not production code. Don't refactor surrounding code, don't add tests, don't worry about BC. Document this in plan steps.
No silent modification of existing .phpt files. Throwaway status does NOT exempt prototypes from the test-modification policy. Any change to a pre-existing test goes through _shared/test-modification-policy.md and requires a justification block. Most prototypes should not need to touch existing tests; if a prototype does, that is itself a signal worth recording in the verdict.
No RFC drafting. Wiki.php.net RFC writing is explicitly out of scope for v1. If the verdict is pursue-as-rfc, the user takes the SPEC.md to the wiki themselves.
No remote git mutations. No git push, no git remote add/remove/set-url/rename/prune/update, no git pull, no git request-pull. Local commit is used per phase (Prototype, Measurement) on whatever branch is currently checked out — never creates new branches.
No benchmark beyond Zend/bench.php complexity unless the user provides one. Don't fabricate elaborate benchmark harnesses; reuse what php-src already has.
Edge cases
Situation
Handling
Hypothesis is too vague to restate ("PHP should be faster")
Block Context with: Hypothesis must be specific enough to identify one subsystem and one mechanism.
Prior-art search shows a declined RFC
Record it in prior-art; raise Open Question: should this iteration try a different angle?
Prototype doesn't compile at all
Mark build step blocked with the error; stop. Don't paper over with #ifdefs.
Benchmark noise exceeds the claimed delta
Verdict is kill or reframe (smaller real effect than claimed). Be honest.
Idea requires JIT/opcache changes the user doesn't have built
Note in subsystem-map; if unavoidable, raise Open Question for the user to enable.
Hypothesis is really a bug, not an idea
Refuse during Context; suggest /bug-context instead.
Multiple competing prototypes
One SPEC.md per prototype. Cross-link via a sibling EPIC.md if the user wants to manage them together.
Quality checklist
Context has all 8 required subsections (Restate, Who benefits, Who pushes back, Userland today, Cross-language precedent, Pros vs Cons, Subsystems, Out of scope)
Pros vs Cons table has 3–5 entries on each side; AGAINST list is honest
Cross-language precedent names 2–3 runtimes (or explicitly records "none")
Metric is falsifiable (could fail), not aspirational
Workload is named (specific script + inputs)
Prior art was searched (git log + gh issue/pr list + internals ML)
Survey & Plan has design-space table covering ≥3 dimensions
Survey & Plan has counter-position-binding for every AGAINST argument + every Research-surfaced objection
Prototype is the smallest change that exercises the hypothesis
make -j$(nproc) exits 0 with prototype applied
Baseline and patched numbers (or examples) are recorded verbatim
Verdict is one of pursue-as-rfc / reframe / kill and matches the evidence
If verdict is pursue-as-rfc, rfc-scaffold step is done with all required subsections
If verdict is reframe or kill, rfc-scaffold step is skipped
SPEC.md top-level status: done when Measurement & Verdict is done
Out of scope
Writing a wiki.php.net RFC document (the rfc-scaffold step produces notes, not the wiki page text — no "Proposed PHP Version" header, no vote section, no full prose)