Contradiction 1: the engine-global wrappers
AGENTS.md ("Public APIs never leak CData") states that Core::$executor / Core::$compiler / Core::$modules are "core-layer state and not a consumer API". The README, however, documents them as the public way to do things:
- "The object store" →
Core::$executor->objectStore[spl_object_id($instance)]
- "Abstract Syntax Tree" / "Debugger-grade introspection" →
Core::$compiler->parseString(...), Core::$compiler->setOptions(...)
One of the two is wrong. Either:
- (a) the README routes through named public methods (
Core::parseString(), Core::objectStore() or similar facades), and the statics get de-emphasized, or
- (b) AGENTS.md carves out the specific read paths it blesses for consumers (parse, object-store read) while keeping mutation core-only.
A related structural note: PHP 8.4 asymmetric visibility does not apply to static properties, so public private(set) static is not an available fix — hardening the statics means accessor methods (BC-affecting) or documentation.
Contradiction 2: module globals
src/EngineExtension/AbstractModule.php:233 — final public function getGlobals(): ?object with @return \FFI\CData|null. AGENTS.md: modules "must expose pure PHP-native interfaces: no public method of a module may return CData". The README's extension example ($globals = $module->getGlobals(); // FFI-backed) is the documented entry point for the whole subsystem, so this is the contract's flagship counter-example. Resolution options: a typed globals-wrapper object, or a documented, explicit exception in AGENTS.md.
Also in scope
@internal marking on the CData escape hatches is inconsistent (tracked separately, but the resolution here should set the rule): StringEntry::getRawValue(), ObjectEntry::getRawValue(), ReflectionValue::getRawValue(), ReflectionClassConstant::getRawValue(), ClosureEntry::getRawFunction(), ResourceEntry::getRawData(), OpLine::getHandler() and ReflectionExtension::getGlobals() all hand out raw CData/pointers without the @internal marker that HashTable::getRawValue() / ReflectionClass::getRawValue() carry.
This is a design decision for the maintainer, not a mechanical fix — filing for discussion. Found during the 2026-08 modernization review.
Contradiction 1: the engine-global wrappers
AGENTS.md("Public APIs never leak CData") states thatCore::$executor/Core::$compiler/Core::$modulesare "core-layer state and not a consumer API". The README, however, documents them as the public way to do things:Core::$executor->objectStore[spl_object_id($instance)]Core::$compiler->parseString(...),Core::$compiler->setOptions(...)One of the two is wrong. Either:
Core::parseString(),Core::objectStore()or similar facades), and the statics get de-emphasized, orA related structural note: PHP 8.4 asymmetric visibility does not apply to static properties, so
public private(set) staticis not an available fix — hardening the statics means accessor methods (BC-affecting) or documentation.Contradiction 2: module globals
src/EngineExtension/AbstractModule.php:233—final public function getGlobals(): ?objectwith@return \FFI\CData|null. AGENTS.md: modules "must expose pure PHP-native interfaces: no public method of a module may returnCData". The README's extension example ($globals = $module->getGlobals(); // FFI-backed) is the documented entry point for the whole subsystem, so this is the contract's flagship counter-example. Resolution options: a typed globals-wrapper object, or a documented, explicit exception in AGENTS.md.Also in scope
@internalmarking on the CData escape hatches is inconsistent (tracked separately, but the resolution here should set the rule):StringEntry::getRawValue(),ObjectEntry::getRawValue(),ReflectionValue::getRawValue(),ReflectionClassConstant::getRawValue(),ClosureEntry::getRawFunction(),ResourceEntry::getRawData(),OpLine::getHandler()andReflectionExtension::getGlobals()all hand out rawCData/pointers without the@internalmarker thatHashTable::getRawValue()/ReflectionClass::getRawValue()carry.This is a design decision for the maintainer, not a mechanical fix — filing for discussion. Found during the 2026-08 modernization review.