Handwritten model and command contracts
_addStringProperty registers storage; it does not synthesize a setter. A handwritten model must define its accessor with the matching property index/type. Generated models already contain that code. Inspect the current ViewModel implementation rather than copying a generated setter name into an unrelated model.
Supply complete initial state in the model constructor. Publish later changes after frontend readiness using model.transaction(), whose Python wrapper requests hold/commit and attempts rollback for ordinary body or commit exceptions. See failure boundaries before relying on rollback. Subscribe to data changes and render the current value once; the initial value may predate subscription.
A Wulf command receives one optional argument payload, rather than arbitrary positional arguments. Define property types/defaults, command names, payload keys and decoder together. Keep a no-argument ready action distinct from one carrying structured data, and validate frontend numbers, ranges, identity and current owner in Python.
Identity fields can be relational keys
A field displayed as a name can also key a native dictionary. Changing only that label can break another presenter's account/vehicle lookup even while the visible row renders. Current result-model investigations found distinct incomplete-fallback and strict-assertion consumers across modes.
Trace every producer and consumer before changing a model identity field. Prefer a presentation-only label change where the identity contract must stay intact; if a key truly changes, synchronize all dependent lookups and test the active mode. A repair verified in one result presenter does not establish another mode's schema or fallback behavior.
For ordinary state publication rather than native projection repair, follow Communication and Updates; it owns initial delivery, command payloads, revision handling and callback cleanup.
Transaction wrappers have different failure boundaries
The name transaction does not establish one universal rollback contract. The public Python wrappers select these proxy calls:
| Wrapper | Normal path | Failure path |
|---|---|---|
| ViewModel.transaction() | Hold, body, commit | A body or commit Exception attempts rollback. Hold runs outside that guard. |
with model | Hold, body, commit | A body exception selects rollback; a clean-exit commit failure has no automatic rollback attempt. |
| Array.transaction() | Body, invalidate | A body failure skips invalidation. This wrapper requests neither hold nor rollback. |
The model transaction catches Exception, rather than every BaseException. A failing rollback can replace the original surfaced error. Neither model wrapper snapshots ordinary Python attributes, external caches or other side effects. Validate values before mutation and keep non-model state under its own failure policy.
These are inspected Python call-routing rules. A rollback request does not prove native restoration, atomicity, nesting behavior or frontend publication. An enclosing model transaction may also affect array changes; the standalone array wrapper does not establish those native effects.
Python array slices defer their value reads
A slice of the Python Wulf Array wrapper returns a single-use generator. Its index range is formed when the slice is created, but each proxy value is fetched when that generator is consumed. Ordinary iteration starts its size/range calculation on first advance. Keeping either iterator does not freeze element values or establish native-object validity.
A client tooltip path passes a tail slice through view settings and consumes it while filling the tooltip model. That routing does not prove a measured delay or a stale UI defect. For mod-owned data that must be repeated or retained, materialize at an explicit acceptance boundary and copy the required fields when detached values are needed; a list of model references is not a deep snapshot. The readable wrapper builds slice ranges directly, so do not assume every Python-list slice normalization or native bounds behavior. Only Python wrapper and recording-proxy behavior was checked.
Projected range endpoints can differ from membership bounds
A model field named to need not contain the source range's final member. The Python Interval helper includes both numeric endpoints. A concrete progression model builder publishes from=begin and to=end + 1, while its current-item selection tests membership in the original inclusive interval after its mode-specific gate.
For an illustrative source range 100 through 199, the model receives 100 and 200; the original membership test accepts 199 and rejects 200. Keep those representations distinct when reading a model or adapting a projection. The page caller uses both helpers. The frontend reader passes to - from as the progress span and currentScore - from as its value. This arithmetic does not turn the model endpoint into an original interval member or establish a universal interval convention or server rating boundary.
Selected readable helper controls with inert division/configuration data and recording model setters confirmed these values and boundary tests under a Python 3 host fixture. Frontend arithmetic was inspected statically. No native model, frontend rendering, live configuration, account state or Python 2.7 client runtime was exercised.
Selection IDs can omit traversed nodes
A selection list can be a filtered projection of a route. For ordinary targets, the inspected tree-selection presenter builds directed edges whose cost is the destination's XP price, or zero if that node is already received. It uses the weighted path helper, then removes received nodes from the returned IDs. Those nodes can still have been traversed as zero-cost intermediates. The selection therefore need not describe every edge or be the route with the fewest steps.
Keep the full graph, chosen route, pending IDs and local cost separate when adapting a projection. Removing a received node from the output does not remove it from the graph. This is the ordinary-target branch's policy: the final-target branch collects pending parents separately. Local selection and affordability calculations also remain separate from the later purchase action and server acceptance.
Selected readable algorithm and presenter methods with an inert connected graph confirmed a longer route with lower remaining XP and received intermediates filtered after traversal. These controls used nonnegative costs; they did not establish unreachable-target behavior, equal-cost ordering, arbitrary graph support or live configuration validity. No model, purchase, account or native/client operation was exercised.