On this page

Compatibility and known limitations

Manual contents | Lifecycle | Troubleshooting | Alphabetical API index

Use this appendix when reading an older model, deciding whether an old helper is suitable, or planning a deliberately selected lifecycle migration. It consolidates compatibility advice; the linked references retain the exact signatures, examples, and detailed contracts. No runtime behavior is changed by this appendix.

1. Which contract is my model using?

Only the exact top-level Project setting lifecycle: "aligned" selects the aligned patch/agent lifecycle. Omission, "legacy", and any other value retain legacy behavior. Project format version is a separate setting; increasing it does not opt into aligned lifecycle.

The tutorials and practical chapters teach aligned models. Existing templates, unconverted apps, saved models, and external clients must not be assumed aligned. Do not copy constructor-driven initialization from an older example into a new aligned model, or add the flag to an old model without reviewing its hooks. Changing lifecycle requires reinitializing the application, not changing a live runtime field or pressing ordinary Reset.

The flag changes initialization, reset ownership, birth setup, and built-in updater evaluation. It does not redesign visit modes, movement, spatial indexing, or step/shift boundaries, and does not change SD initialization. Some shared API repairs apply in both modes; see shared neighborhood changes.

For the full contract, read aligned lifecycle. The earlier lifecycle sections explicitly describe legacy behavior.

2. Lifecycle differences at a glance

These are base/runtime contracts. A model's own overrides can add calls or change behavior; the flag does not remove those calls for you.

Surface Legacy path Aligned path / migration rule
Constructor Older templates call init from subclass constructors, sometimes before defaults exist Allocate stable fields/Stores only; do not call init/setup
Main init Runs during runtime initialization and again from base world reset Runs once after cell context/parameters, before populations and agent configuration; preserve super.init
Patch init Base constructor does not invoke it; template/model code arranges it Runtime applies initial colors before calling each patch's polymorphic init once
First setup Patch setup is not automatically coordinated; initial turtle setup can repeat; Main setup is not automatically coordinated After configured populations exist: patch setup, turtle setup, Main setup, once each
Main reset Base reset orchestrates the world and calls Main init/setup Main reset is local; resetWorld orchestrates the world and coordinated setup
Turtle local reset Restores its reset-list Stores, then calls setup Restores its reset-list Stores without setup
World Reset identities Main and patches survive; turtles are recreated Same identity policy; rebuild references to replacement turtles in setup
Base patchStep Empty Evaluates assigned colorUpdater
Base turtleStep Schedules trajectory movement Movement first, then assigned theta/color/size/shape updaters
Birth helpers Do not assume the aligned init/customize/deferred-setup sequence Constructor, inherited/default built-ins, init, customization; setup after batch insertion/indexing

Preserve base step calls and remove duplicate manual built-in updater evaluation when migrating. Custom Store updaters still need explicit evaluation. Do not reorder movement and heading updates accidentally.

Calling Main reset locally is not a safe substitute for a world reset in aligned mode: it clears local queues/time but does not recreate populations, restore patches, or halt the whole world. Let browser/dispatcher Reset orchestrate it, or deliberately use resetWorld. See reset ownership.

What aligned mode does not infer

See state ownership and Store contracts.

External clients

Clients opting in through initCells messages must also supply awaitAgents: true when agents will follow. For aligned agent worlds, the first cellinit is a request handshake, not final display data. After setup, agentinit supplies agents and worldinit supplies final patch/display state; do not request the agents again on worldinit. Patch-only initialization uses cellinit after setup. Legacy message ordering is unchanged.

Use the browser/runtime protocol; do not implement a reset-before-first-run workaround.

3. Shared neighborhood changes are not a lifecycle switch

The repaired query contracts apply in both aligned and legacy modes. Removing the aligned flag does not restore older duplicate, wrapping, or validation behavior.

Current contract Consequence for older model rules
Wrapped grid queries use modulo normalization and unique patch results Large/tiny-world queries no longer count repeated images as separate neighbors
Visitors call once per distinct patch, matching collection forms Do not expect duplicate callback effects at seams
block includes the center; ring/blockX exclude it Choose explicitly whether the focal patch belongs in a calculation
Grid distance is a nonnegative safe integer Fractional, negative, nonfinite, and other invalid distances throw RangeError; decide the model's rounding policy before calling
ring(0)/blockX(0) are empty; block(0) is the focal patch A zero-radius input has defined behavior, not an implicit first ring
nhood4 respects boundaries and requires square topology Use topology-neutral neighbors for hex; do not assume four hex side neighbors
Local turtle queries deduplicate, hide newborn/dying entries, and exclude the caller Count results agree with the corresponding unique-other-turtle query
Radius/annulus queries use Euclidean distance with shortest wrapped displacement Do not substitute square/hex graph rings for physical-radius selection
Object-form APIRoot.distance reads both axes Results are raw coordinate distances, not automatically wrapped or hex-scaled

These are intentional shared repairs, not proof that an entire legacy app has been migrated. See query contracts and neighborhood stabilization evidence. In particular, choose a scientific meaning for a fractional grid-distance input rather than silently truncating it to make an exception disappear.

4. Scheduling and population caveats

These limitations remain relevant in aligned mode as well.

Caveat Recommended practice
C/A and A/C currently omit mapped patch/turtle shift hooks, although Store commitment still occurs Do not depend on those hooks in slash modes; test a design with another visit mode or put proposal work in the appropriate step rule
doTurtleShift is not consulted by current scheduler maps Do not use it as an on/off switch for turtle hooks
Persistent and transient patch passes can both visit a patch; visitAll can make multiple passes scan everything Choose one activation policy for a simple model; do not assume visitAll means exactly once per tick
Intermediate slash-mode shifts can insert births and remove deaths Test same-tick opportunities explicitly; C/A is not equivalent to C_A
A dying turtle remains in the population until shift but disappears from local queries earlier Return after die when the current rule should stop; distinguish population size from query visibility
step and shift are internal phases, not browser Step Use browser controls; update is the normal post-commit reporting hook
Main statusToJson consumes output/reporting queues Do not call it as a harmless diagnostic snapshot

See shift-hook limitations, activation controls, and birth/death timing.

5. Older entry points and safer choices

This is a triage table, not a promise that every unlisted helper is certified. Some names are merely internal; others are defective or have surprising contracts. Consult the reference before replacing one: a similar name does not imply equivalent semantics.

Entry point / issue Prefer / consult
Main base initAgents has an effective legacy overload, not a usable empty hook Override it and call initializeTurtles with your Turtle class; do not call super.initAgents as a no-op. Population initialization
Main dt numeric field shadows the prototype dt method Read this.dt, do not call it. Time/control
Main nlDiffuse has unresolved boundary/loop limitations Use the explicit synchronous Heat model; preserve its conservation and boundary checks
askTurtles/filterBreed depend on legacy breed/filter machinery Explicitly filter an array of turtles with instanceof and iterate it. Collection scans
Turtle forward returns a heading; fd is defective; initDynamics uses obsolete Store-style assumptions Use pending movement and explicit built-in/custom initialization, not these as movement or cloning APIs. Movement reference
nextX_val/nextY_val and XVal/YVal listeners refer to absent Stores Use joint velocity operations and XVel/YVel interfaces. State, notifications
Turtle link lacks the coordinates required by Main poke Leave insertion/index changes to runtime-managed births and movement. Index operations
patchAtAngle contains boundary/geometry defects Use checked patch_ahead or explicit patch queries with a known geometry policy. Sensing
Trails, sniffer, and sniffCone have sampled-path and swallowed-error limitations Use explicit checked queries/rules; these helpers are not general collision or physical sensing APIs. Legacy sampling
Coords.equals calls numeric internals as functions; hexify mutates and is not idempotent Compare public numeric coordinates explicitly; distinguish raw addresses from physical coordinates. Coordinate contracts
NLRoot.filter matrix overload passes columns, not patches; class/iterator dispatch has caveats Use patches.toList().filter(predicate) or an explicit array conversion. Filter overloads
max_one_of_value spreads tuple elements, unlike min_one_of_value Use an explicit scalar aggregation with a chosen empty policy. Extrema
Random interval truthy-defaults mishandle zero bounds; random and irandom have separate generators For ordered real bounds use low + (high - low) * NLRoot.random(); do not assume Reset rewinds a common seed. Random helpers
normalize does not validate finite input; negative Infinity can loop forever Validate finiteness before calling; do not use nonfinite headings. Geometry
APIRoot.shuffle mutates and returns undefined; NLRoot.shuffle returns the mutated array Choose the declaring class deliberately. Sampling
AgentSet constructor is not an iterable initializer; toString returns an array; sorting policies differ Use supplied query results, explicit iteration, and the documented collection shape. Agent collections

Immediate built-in assignments replace reset baselines; current setters retain them; next setters propose for later commitment. Do not repair a legacy helper by manually shifting private Stores. See read/set/propose.

6. Project, Reset, and saved settings

Surprise Deliberate policy
Numeric default speed 0 (and size 0) falls back to 1 Apply explicit public state in setup/step for a stoppable model; use visibility controls rather than zero size
A speed/theta input binding updates existing turtles, but Reset can restore captured starts If today's input must govern Reset, read it explicitly in setup and in the intended live rule
Changing an input used for initial population count does not resize on ordinary Reset Reinitialize with current inputs, or design births/deaths; do not mistake a count binding for a live resize command
Nonempty explicit turtle starts override count; IDs are not durable identities Verify starts and population, and rebuild references after world reset
dt advances simulation time, not automatic trajectory distance scaling Scale the model rule explicitly when that is the intended unit policy
Agent boundary and cell boundary are independent Test motion and sensing at edges separately
Saved/shared presets may override Project starts or inputs Inspect the loaded preset before diagnosing a failed code change
hardReset or a model-specific Reset override changes ordinary Reset behavior Check the actual Project and override; ordinary reset advice is not a promise about custom run controls

Format version 0.2 is used by the guide; a newer value can produce a compatibility warning. Save/relaunch for code changes. Ordinary Reset is not recompilation, full configuration reload, or random-stream replay. Use the Project reference, especially its live change / Reset / relaunch table.

7. A controlled migration, not a flag-only edit

App migrations remain deferred unless explicitly selected. For current app/copy status and the detailed process, use the migration record and freshening checklist. Canonical apps and embedded Studio copies can differ; compare each with its own previous behavior.

For a selected model:

  1. Capture startup state, a small controlled trajectory, outputs, input effects, births/deaths, and repeated reset. Record existing failures separately.
  2. Move work to constructor/init/local reset/setup according to ownership. Preserve scientific rules, draw order, visit mode, and phase boundaries.
  3. Keep stable registrations on surviving owners; dispose turtle-owned resources on replacement/death. Review early overridden setters before moving fields.
  4. Opt in only after removing manual lifecycle orchestration and duplicate built-in updater calls. Review initial and birth customization callbacks.
  5. Compare time zero, steps, zero-agent worlds, parameter changes, two resets, and initial rendering. Distinguish intended reset repairs from trajectory parity; do not silently accept new behavior.

8. Evidence and limits

The existing focused suites provide executable evidence:

Run these from the repository root:

node --test testing/nlapi-guide.test.mjs testing/nlapi-aligned-lifecycle.test.mjs testing/nlapi-compatibility.test.mjs testing/nlapi-neighborhoods.test.mjs testing/nlroot-utilities.test.mjs

These headless checks do not certify every saved/external model, statistical distribution, host iterator API, SD/Stock/collision branch, or browser layout/launch flow. A characterized defect is not a supported modeling feature, and documenting it is not repairing it. Use a small, measurable model check before relying on a behavior outside the verified guide path.