G-16: Server-derived Semantic registration omission (NOTICE-level)
G-16: Server-derived Semantic registration omission (NOTICE-level)
Context
Discovered during Pilot 5 (doCheckout) post-implementation review of the EC-CUBE -> Be Framework migration. After the Final ran and tests passed, a follow-up commit (3ad9821) registered three Semantic classes that had been silently missing: OrderNo, OrderDate, PaymentDate. The test suite went from “passing with N notices” to “passing with 0 notices”.
Problem
The Be Framework Semantic validator iterates over every property and constructor parameter and looks up a Semantic class by parameter name. When a parameter is server-derived (generated inside a Reason, not arriving from client input), engineers reflexively register the client-input Semantics (those reachable from #[Input] forms) and skip the server-derived ones. The validator then emits a NOTICE per missing Semantic — easily lost in test output noise.
The result is a silently incomplete semantic vocabulary:
- Server-side audit info (
orderNo,orderDate,paymentDate) flows through Being / Final without a Semantic class to validate or document it. var/log/bemart.json(the DevBecoming dump) shows these properties as bare strings, breaking the “every value has a Semantic” invariant.- Future code that consumes the Final has no Semantic to lean on; a typo of
orderdatevsorderDatewould slip through.
Solution / Convention
Every public property exposed by a Being or Final must have a Semantic class — including those generated server-side. Use the MergedCart pattern (empty #[Validate] body) for values that are valid by construction:
// be/src/Semantic/OrderNo.php
final class OrderNo
{
#[Validate]
public function validate(string $orderNo): void
{
// Server-generated by OrderNoProvider; format is
// bin2hex(random_bytes(16)) — always 32-hex. No external validation
// needed because the generator is the only producer.
}
}
The empty body is intentional. The Semantic still serves three purposes even without a constraint:
- Documents that the value has been “named” by the domain.
- Lets the Be validator stop emitting NOTICE for this parameter name.
- Gives consumers a type-system anchor — refactoring renames are now mechanical.
Workflow check: when a Pilot/Wave is done, run the test suite and grep for NOTICE (or your project’s equivalent). Zero NOTICE is the target. The acceptance criterion for “Pilot complete” should include this.
Code example
// be/src/Semantic/OrderNo.php
final class OrderNo
{
#[Validate]
public function validate(string $orderNo): void
{
// server-derived, valid by construction
}
}
// be/src/Semantic/OrderDate.php
final class OrderDate
{
#[Validate]
public function validate(string $orderDate): void
{
// server-derived ISO-8601 timestamp
}
}
// be/src/Semantic/PaymentDate.php
final class PaymentDate
{
#[Validate]
public function validate(string $paymentDate): void
{
// server-derived ISO-8601 timestamp
}
}
After registering these, the test suite runs cleanly:
OK (52 tests, 149 assertions, 0 notices)
Anti-pattern
// WRONG — only registering client-input Semantics.
//
// CheckoutInput has PreOrderId / PaymentMethodId — both registered.
// CheckoutCompleted exposes OrderNo / OrderDate / PaymentDate as public
// properties (audit info for the response body), but no Semantic exists.
//
// Validator emits NOTICE for each:
// "No Semantic class found for parameter name 'orderNo'"
// Engineer dismisses it as noise; the vocabulary stays incomplete.
The fix is mechanical once you see the rule: every public name in the chain needs a Semantic, server-derived or not. The cost of a 9-line empty-body class is trivial; the value is a complete vocabulary.
Where this matters
- Be Framework Final design — Finals that expose audit fields (timestamps, generated IDs, derived totals).
- Beings that aggregate server-fetched data — joined fields from queries, computed totals, status enums.
- Any project where
var/log/bemart.json-style semantic logging is enabled — bare strings break the structural invariant.
Add a “zero NOTICE” gate to the Pilot/Wave completion checklist alongside “all tests pass”. This makes the omission impossible to miss.
Related
- G-22 — Semantic naming convention for context-specific bounds (
LimitvsOrderLimitvsHistoryLimit). G-16 is about whether to register a Semantic; G-22 is about which name to use.