On this page
- Freshening an existing app for aligned lifecycle
- Available chapters
- Purpose and audience
- Scope and source material
- Part I: Getting started
- Part II: Programming guide
- Part III: API reference
- Part IV: Appendices
- Documentation quality and writing order
- Source-linked defect inventory
- Compatibility-first stabilization
Guide development and migration record
Reader contents | Current compatibility advice
This maintainer record preserves the original chapter plan, investigations, repair evidence, and migration history. Status statements and proposed work below are historical, not the current reader-facing table of contents. The first-edition scope supersedes the original recipe wishlist. App migrations remain deferred unless selected.
For model authors: this guide assumes lifecycle: "aligned" in the
top-level Project configuration. Start with the first-model tutorial
and the aligned lifecycle contract.
You do not need to understand runtime messages or underscore-prefixed built-in
Stores to write your first model.
Suggested reading path: Fire → Walkers → Main → Turtles → Grazers. Use state management, neighborhoods, and utilities as references along the way. Troubleshooting and the quick reference / NetLogo bridge are ready to consult. Use the alphabetical API index to find a method or property by name. Consult compatibility and known limitations when reading older models or checking which behavior the aligned flag actually changes.
Compatibility: the runtime still uses legacy behavior when the flag is absent. Templates and unconverted apps are not automatically aligned. Freshening the remaining apps is deferred while we develop this guide; do not bulk-add the flag. The process below is for a future, explicitly selected app, not a request to resume migrations.
Freshening an existing app for aligned lifecycle
Freshening means reviewing initialization and reset ownership, not just changing Project. Preserve the model's scientific rules, parameter meanings, random draws, visit mode, and step/shift timing unless a repair is explicitly intended.
- Capture the existing behavior. Identify both the standalone app and its Studio library copy, if present. Record startup state, a few controlled steps, displayed outputs, parameter changes, births/deaths, and reset behavior. Compare each copy with its own prior behavior; the copies can legitimately differ. Record known failures rather than silently treating them as targets.
- Separate the four jobs. Constructors allocate stable fields/Stores and
structural wiring; never call
init()orsetup()there.init()runs once per object after runtime context/defaults. Localreset()restores that object's state. Runtime-invokedsetup()rebuilds relationships and derived state after populations are ready. Keep base calls where required. Main and patches survive world reset; turtles are recreated. - Check early property dependencies. Configured patch colors are assigned
before patch
init(). Allocate anything needed by an overriddencolorsetter in the constructor (EGOL3's level Store is an example). Maininit()cannot assume patches, turtles, or agent configuration exist. - Give custom state an explicit reset policy. Retain Store/listener identity on surviving objects. Do not accumulate listeners in setup, reconstruct populations in local reset, or assume synchronization resets custom Stores. Dispose registrations owned by turtles when those turtles are discarded.
- Leave orchestration to the runtime. Remove manual population/child
init()andsetup()calls. Move population-dependent Main work to setup. UseresetWorld()for a programmatic whole-world reset, notreset(). Check customization callbacks and deferred births as well as startup. - Use public built-in properties. For example, replace
this._color.setUpdater(fn)withthis.colorUpdater = fn. Alignedsuper.patchStep()evaluates color;super.turtleStep()schedules movement and then evaluates heading/color/size/shape updaters. Remove duplicate explicit evaluation of those built-ins, without changing their intended timing. Custom Store updaters still need explicit evaluation. - Opt in and verify. Add top-level
lifecycle: "aligned"to each reviewed Project, including the embedded Studio Project. Compare controlled startup and trajectories; test multiple resets, changed inputs, zero agents, births/deaths, stable registrations, and initial display/agent rendering. If old reset was broken, test the intended repair separately from trajectory parity. Flagging the app alone is not evidence that it is freshened. - Record evidence and limits. Note which copies were changed, which checks passed, any deliberate behavior repairs, and what was not exercised. Keep unrelated bugs out of the migration.
Migration status and checks
The canonical Agents, Life, Antz, Diffuse, Dla, and EGOL3 apps and their embedded model-library copies are migrated; standalone Ani2 is also migrated. Flock's first-run agent rendering was fixed in shared view initialization for both lifecycle modes; Flock itself has not been migrated. Templates and remaining apps stay as they are until migrations resume. Keep the compatibility flag until legacy and saved/external model compatibility has been addressed; this guide's aligned default is not a runtime-default change.
The opt-in regression suite is
testing/nlapi-aligned-lifecycle.test.mjs.
It exercises Project/message/dispatcher propagation, browser handshake,
cartesian and hex initialization, patch-only/agent-only/empty-agent worlds,
local reset versus coordinated world reset, stable Store identities and
one-time registrations, constructor failures, and births without changing the
existing phase boundaries. Existing legacy characterization tests remain intact.
Representative shipped-model coverage includes Life, Agents, Follower, Fng,
Forest, Diffuse, Dla, EGOL3, WvMach, Antz, Ani2, and WolfSheep. Agents now runs through the
dispatcher with aligned lifecycle; its deterministic trajectories, output,
population, speed changes, repeated resets, and hook counts are compared with
a source-derived legacy version. Life and Antz have equivalent source-derived
comparisons for canonical and embedded copies. Diffuse and Dla also have
source-derived legacy comparisons and repaired-reset regressions; EGOL3 has
independent weighted-rule checks for each copy. Forest, WvMach, and WolfSheep
are exercised through the actual legacy dispatcher. Dla's old library
Status is not defined reset failure remains explicitly characterized in the
source-derived legacy copy, while the shipped copy is repaired. These tests do not certify every
saved/external model, DOM-dependent model integration, or browser rendering.
Status: work in progress. Three tutorials, practical Main/Patch/Turtle chapters, lifecycle/state chapters, utility/neighborhood references, troubleshooting, the Project configuration reference, and a quick reference/glossary are available. The broader chapter plan below also lists material still to be written; it is not a claim that the whole API has been documented or repaired.
Available chapters
- First model: a spreading fire: a complete patch-only model, small steps, expected results, and experiments.
- Second model: wandering turtles: a complete moving-agent model with live speed/turn controls, a count chart, reset, and an exact one-turtle experiment.
- Project configuration reference: world and turtle settings, defaults/starts, execution, inputs, charts/loggers, units, and a live-change versus Reset/reinitialization/relaunch table.
- NLSimMain API reference: declared methods/accessors, runtime fields, supporting exports, exact phase/return/side-effect contracts, and classified internal/legacy members.
- NLPatch and HexPatch API reference: lifecycle, color properties and notifications, coordinates, neighborhoods, occupancy, static serialization, and hex overrides.
- NLTurtle API reference: state, movement/turn timing, velocity, appearance, sensing, queries, population/index operations, notifications, serialization, and classified legacy limitations.
- NLRoot and inherited utilities reference: shared context/reset, coordinates, collection adapters, sampling, extrema, text/statistics, and inherited APIRoot geometry/random utilities.
- Supporting types: Store/Property contracts, coordinates, collection shapes, callback arguments, enums, and runtime choices.
- Alphabetical API index: name-to-owner links for declared members, inherited utilities, model-facing fields, aliases, and supporting types.
- Compatibility and known limitations: legacy/aligned differences, shared query repairs, remaining caveats, safer alternatives, configuration/reset surprises, and controlled-migration guidance.
- 3. Execution and lifecycle: initialization, visit modes, commitment, population changes, and reset. Read the aligned contract first; older behavior is retained in clearly marked compatibility sections.
- 4. Managing model state: Stores, current/next/initial values, registration, reset ownership, and programming examples.
- 5. Programming Main: populations, input timing, coordination, activation, aggregation, displays/logs, stops, and reset ownership; includes a hook-by-hook map and a worked population/peak monitor with an exact check.
- 6. Programming patches and their environment: patch hooks, coordinates, colors, synchronous custom state, activation, neighborhoods, and residents; includes a complete heat-spreading model with exact values, conservation checks, live mixing input, and repeated reset.
- 7. Programming turtles: public state, movement and heading timing, sensing, appearance, births/deaths, and verified helper caveats; includes a hook map and a worked obstacle-sensing turtle with an exact route.
- Third model: grazers and food: regrowing patches, turtle energy, bounded ordered resource claims, charts, custom Store reset, and deterministic stationary/moving experiments with resource-budget checks.
- Patch neighborhoods and local agent queries:
ring,block,blockX, visitors,nhood4,nhood8, and turtle neighborhood queries. - Tools and shared utilities:
Enum,EnumSym,Property, random draws, sampling, summaries, and supporting tools. - Checking and troubleshooting a model: a verification routine, first-failing-stage workflow, symptom table, worked food/reset diagnoses, and small reproducible checks.
- Quick reference, glossary, and NetLogo bridge: rule ownership, lifecycle/step/state/query tables, movement, inputs/outputs, worked-check links, units, and familiar concepts with their important differences.
The guide example tests load the tutorials' actual fenced Project/Main/Patch/Turtle sources and check published counts, chart values, movement/turn ordering, slider changes, resource competition, energy caps, empty populations, and repeated reset through the dispatcher. Selected Main/Turtle chapter fragments also execute, including customization, display shapes, pending movement, and birth/death visibility timing. The Patch chapter's complete heat model checks synchronous spreading, conservation, boundary behavior, color timing, live inputs, and stable Store reset. Project-reference checks cover nested input registration, defaults/bindings, explicit starts, population reinitialization, output shapes, and dt versus movement. Main-reference checks inventory declared prototype members and exercise its Store example, callback/queue timing, activation/clear policies, parameters, query side effects, logger payloads, and consuming status/stopping behavior. Patch-reference checks inventory NLPatch/HexPatch declarations and exercise both alternating-rule examples, color notifications/baselines, updater timing, occupancy snapshots, raw coordinate distance, and static serialization. Turtle-reference checks inventory declared members, execute the straight-motion and query/turn examples, and characterize velocity/local-reset behavior, visibility-cache differences, serialization, notifications, and known defective legacy entry points without changing runtime behavior. Root-reference checks inventory NLRoot and inherited APIRoot declarations, execute static/collection/selection examples, and check local reset, sampled paths, extrema policies, geometry, and weighted-selection behavior. Supporting-type checks inventory state/coordinate/agent collection declarations, execute Store/Property/coordinate/enum examples, and check factory reset, Store.create ownership, visibility-filtered lists, and sorting mutation. Index checks cover alphabetical order, unique names, and owner-specific instance/static declaration coverage. They also check neighborhood boundaries/duplicates and square/hex center inclusion, key tool semantics, the aligned state examples, and relative file links. These are headless runtime checks, not browser layout/launch certification. Run from the repository root:
node --test testing/nlapi-guide.test.mjs testing/nlroot-utilities.test.mjs testing/nlapi-aligned-lifecycle.test.mjs
Browser spot checks also loaded the exact Walkers and Grazers sources through the local Studio runtime POST path: 40/30 initial rendered turtles, advancing Step controls, Reset to time zero, and Grazers charts returning to food 960 / mean energy 4. Control events were dispatched programmatically; Studio popup launch and physical mouse hit-testing were not certified. Project examples use the supported format version 0.2, avoiding an unnecessary newer-version warning.
Neighborhood API stabilization
The shared neighborhood API now has consistent contracts in both lifecycle
modes: modulo wrapping, unique collection/visitor results, center-including
block, center-excluding ring/blockX, explicit zero-distance and validation
rules, boundary-aware square nhood4, and topology-neutral neighbors.
Turtle counts match unique-other-turtle queries; standard coordinate objects
work with nhoodCoords. Radius/annulus queries use Euclidean distance with
shortest wrapped displacement, and object-form APIRoot.distance includes
both coordinate axes. See the reference.
These are intentional API behavior repairs, not additional app lifecycle migrations. Both Flock copies explicitly round their continuous Vision input up to an integer grid distance, preserving the previous rounding for positive values. All other app lifecycle migrations remain deferred. Both River copies also use public heading/speed assignments for radius-selected source flows; the previously ineffective query had hidden obsolete Store-style calls and a nonexistent parameter getter. The neighborhood tests cover the contracts and affected Flock, River, and RPS callers; existing model comparisons continue to check lifecycle behavior, not parity with the former neighborhood defects. Browser checks also exercised standalone Flock (1000 visible agents, step, reset) and River (eight steps, 16 flows, reset), without page exceptions. The broad testing baseline still has four pre-existing Studio/Designer catalog/selection/resource failures; neighborhood repairs do not address them.
node --test testing/nlapi-neighborhoods.test.mjs testing/nlapi-guide.test.mjs testing/nlapi-compatibility.test.mjs testing/nlapi-apps.test.mjs testing/ani2-aligned-lifecycle.test.mjs testing/nlapi-aligned-lifecycle.test.mjs testing/nlroot-utilities.test.mjs
Purpose and audience
Help model authors build, understand, and extend NuWebKit patch and agent simulations using JavaScript. Assume basic JavaScript knowledge, but no prior knowledge of the NL API or its execution model. Aim at the average NetLogo model author: explain the modeling idea before JavaScript machinery, introduce one concept at a time, and give observable results before advanced variations. Do not assume knowledge of inheritance, callbacks, Stores, or runtime protocols.
The manual should provide three reading paths:
- First model: a complete example from Project configuration to running code.
- Programming guide: explanations and recipes organized by modeling task.
- API reference: precise method and property contracts organized by class.
Scope and source material
The four principal classes are:
| Class | Role | Source |
|---|---|---|
NLSimMain |
Model-wide initialization, scheduling, parameters, populations, and output | nlsimmain.js |
NLPatch |
A cell in the spatial environment, with state and neighborhood operations | nlpatch.js |
NLTurtle |
An individual agent, with state, movement, sensing, and lifecycle operations | nlturtle.js |
NLRoot |
Shared base functionality inherited by Main, Patch, and Turtle | nlroot.js |
Explain supporting types where they are needed: Store, coordinates, patch and
agent collections, Enum, EnumSym, Shape, Dir, and VisitMode. Include inherited APIRoot
utilities because model authors call methods such as NLRoot.random() even
though they are implemented in apiroot.js.
Keep system-dynamics stocks, flows, and integration methods outside the main
scope, with a scope note rather than a second API manual.
Existing Studio help is useful supporting material, not a substitute for checking implementation: Main, Patch, Turtle, and Project.
Part I: Getting started
1. The model at a glance
- How Project, Main, Patch, and Turtle fit together.
- Class inheritance:
APIRoot->NLRoot-> the three model classes. - Which code the author writes and which objects the runtime creates.
- Imports, subclasses, default exports, and access through
this.main. - Units and conventions: columns/rows, x/y, radians/degrees, palette indices.
2. Build and run a first model
- Begin with the complete patch-only tutorial, then the moving-agent tutorial.
- Fixed population, live speed/turn controls, corresponding Main getters, and a count chart; distinguish population creation from live resizing.
- Agent initialization, movement, and a simple probabilistic state transition.
- Run, pause, step, and reset; expected observations and checks.
- Change a parameter and distinguish immediate effects from reset-time effects.
- Explain every API call used, with links to its reference entry.
Part II: Programming guide
3. Understand execution and lifecycle
- Construction versus runtime initialization; when defaults become available.
- Main hooks:
init(),initCells(),initAgents(),setup(),coordinate(),update(), andreset(). - Patch hooks:
setup(),patchStep(),patchShift(), andreset(). - Turtle hooks:
init(),setup(),turtleStep(),turtleShift(), andreset(). - A sequence diagram for an ordinary step, including pending-state commitment.
- How
visitModechanges patch/agent order and intermediate shift phases. - Aligned one-time init, repeatable setup, and local reset; keep the still-legacy supplied templates out of aligned examples unless explicitly adapted.
- Which base calls must be preserved and what each one does.
4. Manage model state correctly
- Current state, pending next state, and the reset baseline.
- Initialization setters versus
current...setters versusnext...setters. Store: construction, reading, initialization, next-state writes, and listeners.- Registering custom state with Main for synchronization and display reporting.
- Separate responsibilities: synchronization, visitation, reporting, and reset.
- Persistent objects versus repeatable setup; avoiding duplicate listeners.
- A worked synchronous update and a worked custom-state reset.
5. Program Main
- Initialize patches and turtles; supply custom initialization callbacks.
- Read numeric and Boolean inputs with
getParam()andgetBParam(). - Use
coordinate()for global work andupdate()for outputs. - Visit patches and turtles; aggregate counts and values.
- Configure all-patch, active-patch, and persistent-patch visitation.
- Queue births and deaths; understand when population changes take effect.
- Publish charts with
show()andshowList(); write and clear logs. - Use time, stopping conditions, and run controls without creating a second loop.
6. Program patches and their environment
Practical chapter available, including a complete conservative heat-spreading example with deterministic checks.
- Coordinates, patch centers, color state, and palette interpretation.
- Patch lookup and boundary behavior.
- Rings, blocks, visitor callbacks, and collection-returning methods.
- Identify whether a neighborhood includes its center and whether it deduplicates.
- Find agents on a patch; explain spatial-index prerequisites.
- Build a local interaction rule and a synchronous cellular update.
- Introduce field diffusion only with verified boundary and state semantics.
7. Program turtles
Practical chapter available. Legacy/specialized helpers are identified rather than presented as working introductory shortcuts.
- Built-in state: location, unwrapped location, appearance, speed, and heading.
- Trajectory movement versus model-controlled next locations.
- Immediate versus scheduled movement; initialization of location and velocity.
- Ordering a heading change before or after the base step.
- Boundary handling, continuous/discrete motion, and coordinate units.
- Distances, directions, patch-ahead queries, and neighborhood candidates.
- Filter out self and apply the intended distance/boundary policy.
- Births, customization callbacks, death, visibility, and drawing trails.
- State and appearance listeners.
- Advanced sensing with
sniffer()andsniffCone(), subject to verification.
8. Use shared utilities
- Tools and utilities reference, including named states with
Enum, symbolic choices withEnumSym, and change notifications withProperty. - Static utilities versus instance methods and inherited methods.
- Random draws, integer selection, probabilities, and distributions.
- Filtering, mapping, visiting, counting, summing, and selecting extrema.
- Callback signatures and supported collection types.
- Coordinate creation and path sampling.
- Randomness and reproducibility: document actual generator behavior rather than promising a seeded, repeatable run.
9. Modeling recipes
- Regrowing food and turtle energy is available, including explicit claim-order bias, conservation checks, and reset ownership.
- A probabilistic agent state machine with population counts by state.
- Neighbor-to-neighbor infection and recovery.
- A patch-based cellular automaton.
- A sensed environmental field that influences movement.
- Parent/offspring creation and population removal.
- Multiple output series and a text logger.
Each recipe should include its required Project settings, complete model code, expected behavior, reset behavior, and relevant API reference links.
10. Troubleshooting and checking a model
Troubleshooting chapter available.
- Objects do not step: initialization,
visitMode, and patch activation. - State changes too early or too late: current versus next-state writes.
- Turning takes effect one move later: base-step call ordering.
- Reset differs from the first run: initial values, custom state, and setup.
- Empty neighbor queries: spatial indexing, coordinates, and self filtering.
- Missing output: exact display codes, existing slots, and value shapes.
- Incorrect colors or shapes: palette indices versus configuration strings/enums.
- Small deterministic checks for ordering, boundaries, and state transitions.
Part III: API reference
Reference chapter files and status:
| Proposed file | Contents |
|---|---|
| nlsimmain.md (available) | Lifecycle hooks; initialization; scheduling; patch/agent access; population operations; parameters; displays; logging |
| nlpatch.md (available) | State properties; lifecycle hooks; neighborhoods; distances; occupancy; listeners; HexPatch overrides |
| nlturtle.md (available) | State properties; lifecycle hooks; movement; direction/distance; sensing; populations; appearance; listeners |
| nlroot.md (available) | Shared state/reset behavior; coordinates; collection operations; selection and aggregation; inherited APIRoot utility inventory |
| supporting-types.md (available) | Stores, coordinates, collection shapes, callbacks, and enums required by the four classes |
Standard reference entry
For each method or property, document:
- Exact name/signature and declaring class; static or instance.
- Role: model-author operation, overridable hook, or runtime/internal helper.
- Parameters, defaults, accepted types, units, and return type.
- Preconditions, required configuration, and supported collection shapes.
- Side effects, mutation, timing, and reset implications.
- Boundary behavior, self inclusion, and duplicates where relevant.
- A minimal example and a link to a programming-guide use case.
- Limitations, failure behavior, verification status, and source location.
Inventory every declared method and property, including aliases and inherited members. Give runtime helpers a brief classification rather than presenting them as ordinary modeling operations.
Part IV: Appendices
- Quick reference, glossary, and NetLogo concepts table and the alphabetical API index are available, along with compatibility and known limitations.
- Project configuration reference for settings used in the guide.
- Alphabetical method/property index, including inherited utilities.
- Lifecycle and current/next/initial-state quick-reference tables.
- Glossary of patches, turtles, Stores, visits, shifts, and palette values.
- Compatibility notes, implementation limitations, and recommended alternatives.
Documentation quality and writing order
- Inventory the API and classify model-facing operations, hooks, and internals.
- Verify lifecycle, state, and reset behavior before drafting recipes.
- Write the first complete model and the execution/state chapters.
- Draft each class reference alongside its programming-guide chapter.
- Run examples in the actual runtime; check boundaries, resets, callback arguments, return shapes, and required Project settings.
- Finish recipes, troubleshooting, cross-links, and the alphabetical index.
Do not infer behavior from familiar method names or describe legacy code as a
working feature without checking it. Initial source inspection shows apparent
Java-port remnants in some NLRoot helpers and duplicate method definitions
elsewhere. Record such cases as needing verification; distinguish confirmed
limitations from supported behavior. Fixes to the API itself are a separate
task, not part of writing this manual.
Source-linked defect inventory
These are concrete implementation findings, not claims that all older methods are defective. The links identify the current declarations; line numbers may move as fixes are made. The initial findings below record the pre-repair failures; see the stabilization status for fixes and current regression coverage.
Reproduced defects
Isolated Node.js probes evaluated the actual NLRoot class body with its imports
replaced by minimal stand-ins. A separate probe evaluated both forward
declarations together. These checks reproduce local JavaScript failures; they
are not full simulation tests. The table retains those initial observations;
seven helper defects have since been repaired as described below.
| Method and source | Reproduced failure and source evidence | Repair direction |
|---|---|---|
NLRoot.n_of(size, elements) |
An array input throws ReferenceError: Collections is not defined. The method also uses Java's subList, which JavaScript arrays do not provide. |
Use the existing NLRoot.shuffle() on a copy and a JavaScript slice. Define size validation and sampling semantics before fixing the slice bounds. |
NLRoot.one_of(elements) |
An array input throws because elements.size() is not a function. The following unqualified random() is also not defined in this module. |
Define accepted collection types; use the corresponding length and the inherited random utility. Specify empty-collection behavior. |
NLRoot.unquotify(s) |
A string input throws because s.length() calls a numeric property as a function. |
Use the string's length property; check empty, quoted, and unquoted strings. |
NLRoot.parse(s) |
String input throws at s0.length(). Additional source defects include s1.length(), Java-style toArray(new String[0]), failure to advance s1 inside the quoted-token loop, and omission of the first token's content when assembling a multiword quote. |
Define the intended quoting/tokenization contract and replace the Java-port logic. Test empty input, repeated spaces, multiword quotes, and unterminated quotes. Do not repair only the first exception. |
NLRoot.meanPMStd(vals) |
[1, 2, 3] throws ReferenceError: mean is not defined: the result array references mean, but the local variable is mean0. |
Use the computed mean; confirm result order and population-versus-sample standard deviation, and define empty-input behavior. |
NLRoot.max_one_of_value(fn, elements) |
fn = x => x.score with [{score: 2}] throws because fn.apply(this, val) treats the element as an argument list rather than passing it as one argument. This differs from min_one_of_value, which calls fn(val). |
Align the callback contract with the minimum-value counterpart; test object and numeric collections. |
NLRoot.max_of(objs, fun) |
Nonempty input throws ReferenceError: best_of is not defined; the instance method is called without this. |
Call this.best_of(...); check object selection, ties, and empty input. |
NLRoot.min_of(objs, fun) |
The same unqualified best_of(...) call throws ReferenceError. |
Call this.best_of(...) and cover the same cases as max_of. |
NLTurtle.forward(speed) and the following forward() |
JavaScript does not overload methods by arity. The second definition replaces the first: forward(2) returns the heading without calling fdNext(2). |
Decide whether forward is a movement operation or heading query; preserve intentional compatibility explicitly and test argument handling. |
Additional source findings requiring targeted runtime checks
| Method and source | Concern to investigate |
|---|---|
NLTurtle.fd(dist) |
Passes the _theta Store object, rather than a numeric heading, to go(). It then calls move(coords), although move() ignores arguments and schedules a speed-based move. Verify and repair immediate-movement semantics together rather than changing only one argument. |
NLSimMain.initAgents() and the later initAgents(msg, ws, mainapp) |
The later definition replaces the earlier lifecycle hook in the base class. Its body also resembles an older message-handling path. Check callers and subclass overrides before removing or renaming it. |
NLTurtle.inBounds() and the following duplicate |
The implementations are identical, so this is redundant code, not a demonstrated behavior defect. Remove duplication separately from functional repairs. |
This is the initial inventory behind the outline's warning, not an exhaustive audit of the four classes. Each repair should add focused regression coverage and update the eventual reference entry with the verified contract.
Compatibility-first stabilization
Policy
- Work in small batches with regression tests before repairs.
- Preserve existing app code, configuration defaults, lifecycle ordering, and random-number sources unless a behavioral change is explicitly approved.
- Characterization tests record observed behavior, including known defects. They do not make those defects desirable permanent contracts.
- Require a compatibility decision before changing movement, reset ownership, visit-mode semantics, or previously usable callback forms.
- Do not describe representative app tests as certification of every app.
First batch: narrow helper repairs
The following methods in NLRoot are repaired and covered by API regression tests:
| Method | Verified contract |
|---|---|
unquotify(s) |
Removes a matching pair of outer double quotes; otherwise preserves the string. |
meanPMStd(vals) |
Returns [mean, mean + populationSD, mean - populationSD]. Empty input retains the calculation's NaN results rather than introducing a new policy. |
max_of(objs, fun) |
Calls the instance selector and returns an object, not its score. Keeps the first tied result; returns null for empty input. |
min_of(objs, fun) |
Same object-selection, tie, and empty-input conventions as max_of. |
A repository usage search found no calls to these four methods outside their
declarations before this batch. That does not rule out externally saved apps.
max_one_of_value is deliberately not changed: its existing argument-list
application works for tuples and preserves callback this, even though it fails
for ordinary object-element callbacks. A characterization test protects that
usable legacy form pending a compatibility decision.
No signatures were renamed. Scheduling, reset, movement, configuration defaults, and random utilities are unchanged.
Characterized behavior retained for compatibility
Runtime characterization tests exercise actual API modules, covering:
- Current, pending, and reset-baseline state distinctions.
- All seven patch/agent visit modes, including intermediate state commitment.
- Clock ordering and the time observed by
coordinate()andupdate(). - Deferred population creation/removal and newborn visibility bookkeeping.
- Patch identity retention versus turtle recreation during reset.
- The effect of scheduling a turn after the base trajectory step.
- The current heading-query behavior of
forward(), even with an argument. - Tuple expansion and callback
thisinmax_one_of_value.
Two slash modes, "C/A" and "A/C", currently have no shift-hook mapping:
their aliases are assigned before the target entries in initVisitModeMap()
exist. Store commitment still occurs. This is a newly characterized defect,
not repaired here because adding hook calls can change existing simulations.
Shipped-app tests load actual model code and configuration from the library:
- Life: every cell is compared with an independent synchronous Life rule; counts, display output, and reset state are checked. Canonical and library aligned runs are also compared with source-derived legacy behavior.
- Agents: the migrated aligned app is exercised through the dispatcher; deterministic trajectory/output/population and parameter behavior are compared against an in-test source-derived legacy version.
- Antz: canonical and library mixed patch/agent worlds are compared with deterministic legacy runs, including sensing, food/chemical Stocks, births, deaths, live parameter changes, occupancy, and repeated reset.
The model comparisons are headless. A local browser smoke test also loaded the canonical Agents page, connected the runtime, stepped, reset, changed Speed, and stepped again. Life and Antz browser evidence is recorded below. Seeded Agents/Antz comparisons are deterministic and exact for their controlled scenarios; this does not certify arbitrary stochastic runs, every library, or externally saved apps.
Validation and next gates
Run the new checks from the repository root:
node --test testing/ani2-aligned-lifecycle.test.mjs testing/nlapi-compatibility.test.mjs testing/nlapi-apps.test.mjs
The existing baseline command is:
node --test testing/*.test.mjs common/js/test/*.test.mjs scripts/*.test.mjs
The current focused NL API suite passes all 111 tests (exit 0), using:
node --test testing/ani2-aligned-lifecycle.test.mjs testing/nlapi-aligned-lifecycle.test.mjs testing/nlapi-apps.test.mjs testing/nlapi-compatibility.test.mjs testing/nlroot-utilities.test.mjs
Before Ani2, the recorded repository baseline ran 155 tests: 150 passed and five pre-existing failures remained. The recorded Ani2 run had 158 tests: 153 passed. After EGOL3 coverage the broad command above runs 178 tests: 173 pass, with five unrelated failures (exit 1; the Designer redirect assertion and four model-library catalog/selection/resource checks). No unrelated failing checks were repaired.
Historical: second batch of expanded compatibility checks
The earlier compatibility batch changed only tests and documentation. Its history is retained here; the first app migration below is the current state. Five consecutive focused runs had passed while exercising stochastic app behavior without imposing an exact random trajectory.
Additional shipped-app coverage:
- Follower: three cycles of twelve steps and reset at its configured
population. Checks reciprocal leader/follower links, absence of cycles, live
population membership, bounded positions, patch indexing, the
MODparameter's pending colors, and replacement of turtles on reset. - Fng: three controlled two-step branch/reset cycles. The actual model code
runs with a uniform moisture field, diffusion and consumption set to zero,
and branching every tick after the first. Checks sensing of passable/impassable
patches, occupancy state, moisture consumption, agent water uptake, branch
water splitting, deferred insertion, newborn query exclusion, and reset.
The child stays at the parent's pre-step location because legacy
forward(1)does not schedule movement. This is intentionally characterized, not repaired.
Earlier batch: Agents aligned-lifecycle migration
The canonical app under agents/ and the embedded
common/studio/model_library/models/Agents.html both set
Project.lifecycle: "aligned". Their no-op Patch and Turtle constructors no
longer call init(); runtime initialization calls those hooks after patch
allocation or turtle defaults, respectively. The model's movement and
stochastic rules are unchanged. Life and Antz were migrated subsequently, as
described below. Templates and remaining shipped models stay legacy.
The dispatcher-path test compares a deterministic 5×7 world with seven agents for initial output, four motion steps, a live speed change, three more steps, and two consecutive resets. It also checks constructor/init/setup counts and turtle replacement on reset. A minimal test transform removes the Project opt-in and restores the old constructor hook calls to form the legacy comparison; it does not duplicate full app fixtures. Results are limited to this model/scenario and do not certify external saved projects or every Agents configuration. Do not remove the lifecycle flag until compatibility of the remaining bundled and saved models has been assessed.
Current batch: Life, then Antz
Life was migrated and verified before changing Antz. Both canonical Project
files now select lifecycle: "aligned", as do the actual embedded files
common/studio/model_library/models/LIfe.html (case intentional) and
common/studio/model_library/models/Antz.html. Standalone pages still import
their canonical modules; the all-app build includes those changes. No
additional app or template was opted in.
Life: patch constructors no longer call init. The canonical custom state Store is created from the preset color after allocation, synchronized once, and reinitialized locally on reset. Setup seeds the active neighborhood for initial live cells; Main setup restores the original initial-count formula. The embedded version retains its color Store and installs its listener once. Tests compare every cell, counts/output, and active-site membership (not Set insertion order) over 45 ticks and three resets for each copy. An independent synchronous rule also checks 36 ticks and three resets. The embedded legacy reset retained stale active sites from the previous run; aligned reset clears these sites, so its active set is a subset of the legacy set after reset, while cells/counts/output remain exactly equal. This is an intentional aligned reset cleanup, not a change to the Life rule.
Antz: constructors no longer invoke init. FoodPatch and Nest initialization
now extend their parent's init explicitly, preserving the factory-selected
class, food/chemical Stocks, scents, and color updaters. Turtle init respects
the newborn flag so post-default initialization does not overwrite the
parent-derived birth location. Death unlinks the retiring turtle and releases
its food Stock; Antz's resetWorld() also retires current and queued newborn
turtles before delegating world orchestration. Its local reset() remains
local and does not remove child Stocks. This avoids orphan Stock closures and
agent-index references when instances are replaced. No general Stock disposal
API or runtime scheduler change was introduced.
For each copy, seeded seven-ant runs compare all cells, food/chemical values, agent state, patch occupants, time, and display data exactly across 15 ticks, three resets, speed changes, forced births, and forced extinction. Additional tests check food sensing/uptake, parent-position inheritance, local reset, queued-birth reset cleanup, zero population, stable patch/Stock identity, and registration counts. Constructor/init multiplicity differs intentionally; legacy's duplicate turtle-food registrations are not retained. Cartesian and hex mixed-class factory tests prove one allocation/init and no retry on error.
Validation (Node v22.18.0):
- Before Life edits: three Life regressions passed; after Life and before Antz: the combined app/lifecycle gate passed 28/28 (exit 0).
- Before Antz edits: both deterministic Antz regressions passed.
- At this stage, the combined app/lifecycle gate passed 37/37 and focused API
checks passed 86/86 (exit 0). The full repository command passed 150/155,
with the same five baseline failures (exit 1).
git diff --checkpassed. npm run build -- --mode all: exit 0, 45/45 apps included.- Browser, fresh uncached local modules: Life connected, stepped to time 3, and reset to 0; Antz connected, changed birth/death/speed inputs, produced 360 ants after forced births, then zero after forced deaths, reset to time 0, and stepped with 180 restored ants. Both flows had no page errors. Existing spy refresh timing is preserved: after extinction, Antz's visible population field remains zero at Reset and refreshes to 180 on the next update/Step. No display-timing repair is claimed.
These are controlled headless comparisons and canonical-page browser checks, not exhaustive certification of external saved apps, arbitrary topologies, physics settings, or every Studio import/UI path. The five unrelated full-suite failures remain open.
Next batch: Ani2 aligned lifecycle
The standalone Ani2 Project now selects lifecycle: "aligned". Patch and
turtle constructors no longer call init() directly; the runtime invokes those
hooks after allocation and turtle defaults. Ani2's legacy explicit setup()
call is retained only for legacy mode, preventing a duplicate when the aligned
dispatcher completes world setup. Its kernel/resource initialization remains
in Main setup, after patch and turtle setup, without changing movement,
resource, chooser, or stochastic rules.
Ani2 now uses public built-in state accessors (color, nextColor, and
nextSize) rather than reaching into the base classes' private Stores. The
upload default remains a 1000×1000 world. The deterministic dispatcher test
uses a valid custom 12×12 uploaded configuration instead, and compares initial
state, Next_Location trajectories, resources, cells, output, live parameters,
and repeated-reset results with an in-test source-derived legacy version. It
also checks hook counts, stable patch/Store identity, and a single
ani2config listener after resets. This is headless model evidence only; no
browser rendering or million-cell default-world run is claimed.
The depletion path also contained a pre-existing runtime exception: it passed
an undefined id to Main when energy reached zero. It now reaps the turtle by
this.id; a focused test forces the zero-energy path and confirms the
Chrono state changes to Reaped. No other Ani2 rule or known defect was altered.
Diffuse and Dla aligned lifecycle
Both standalone Projects and the Studio library copies opt into aligned
lifecycle ownership. Patch/turtle constructors no longer call init. Existing
Studio Dla edits are retained: class-scoped DlaPatch.Status, public pending
color/reporting in its listener, and turtle Status binding in init. Standalone
Dla keeps its exported Status enum for compatibility.
Diffuse: heat aliases use public built-in accessors. One-time patch init
registers heat's Store with synchReportAndVisitNext; this internal Store
reference is required by that registration API. No new updater evaluation is
added. The ineffective main.patchShift flag is removed (doPatchShift is the
runtime's actual property). Main setup now owns the original pre-tick turtle
step, preserving startup's pending heat/motion/heading and replaying it on reset.
That replay intentionally repairs the old reset, which cleared the field but
did not seed it as startup did. Population remains captured at initialization;
an NTU parameter message does not resize it on reset.
The scientific timing is unchanged: coordinate diffuses current heat, patches
dissipate next heat, then turtles schedule movement and overwrite the old-location
patch's next heat with current heat + THT. Multiple colocated turtles overwrite
rather than accumulate. The next heading is committed with movement at shift.
The numerical regression checks this order, nonzero diffusion/loss, and wrapping.
This migration does not make the solver an additive simultaneous heat model.
Dla: Main reset restores radius 0/count 1, and setup publishes CNT after
initialization and reset. Patch reset uses its correct enum scope and restores
empty current/pending colors before reseeding the center. This also handles a
nonseed patch whose built-in color reset baseline was changed. Its status
Store/listener is retained rather than reallocated. Public nextTheta replaces
direct heading Store access. Birth, movement, neighborhood, and sticking rules
are unchanged, including existing distance/collision/index behavior.
Evidence in nlapi-apps.test.mjs:
- Source-derived legacy transforms reverse lifecycle changes; canonical and
embedded rules run through the actual dispatcher under controlled
APIRoot.random/irandom. Initial state, trajectories, current/pending field, occupancy observations, births/deaths, counts/radius, and live parameter changes match exactly before reset. Separate tests establish repaired reset semantics rather than claiming parity with broken reset behavior. - Canonical and library snapshots match through repeated resets. Patch
Store/listener identity, initialization multiplicity, startup seeding, Dla's
complete reset redraw and count output are checked. The old Studio unqualified
Statusexception is retained only in the legacy fixture. - The existing
nlDiffuserectangular limitation is explicitly characterized: a taller-than-wide grid throws; a wider-than-tall grid produces nonfinite heat. Square worlds are supported by this model. NeithernlDiffusenor the knownNLRoot.distancex-distance defect was changed in that migration. The distance defect is subsequently repaired by neighborhood API stabilization above. - The initial scoped run (
node --test testing/*.test.mjs) had 140 tests: 135 passed, four catalog/resource checks failed, and the stale Dla assertion failed because it called the aligned local reset hook instead of world reset. That assertion was replaced with legacy characterization plus repaired-world regressions. Final focused results: 103/103; broad results: 165/170 (five unrelated Designer redirect/catalog/selection/resource failures). npm run build:all: 45/45 applications built.- A new local browser page loaded each standalone default-sized app and connected
successfully. Diffuse stepped to 3, changed diffusion to 0.6/loss to 0.2, and
reset/stepped twice (
0 → 1). Dla changed whole-world/max-particle inputs, stepped to 12 and reset twice; its chart returned from counts 23 and 19 to(time 0, count 1). No page errors were observed during these flows. Dla controls were exercised via DOM events after visibility/stability checks prevented ordinary clicks; this is runtime/chart evidence, not a UI-layout certification. Studio copies were dispatcher-tested, not browser-rendered.
EGOL3 aligned lifecycle
Both standalone egol3/js and Studio Egol3.html opt into aligned lifecycle.
Studio retains its class-scoped Egol3.ELState and per-patch enum binding;
standalone retains its exported ELState. Neither patch constructor invokes
init. Each constructor allocates the level Store, installs its positive-level
scheduling callback, and synchronizes it once, so pre-init configured color
assignment is safe. Patch init only enables shifting. Studio's unused turtle
constructor no longer invokes its init hook.
Intentional lifecycle repairs:
- Main setup clears and recounts state totals only after RAM color assignment at startup or patch restoration at reset. Previously startup counted the pre-RAM state and could retain previous enum counts.
- The Dead enum count now agrees with the existing full-world Empty display after each step; formerly the enum counted only visited dead cells.
- Patch reset restores public color first, then converts it to level using current NBI/MAT weights. This preserves the initial pattern after weight changes, rather than restoring incompatible old numeric weights.
- Main reset remains local: no replacement RAM, duplicate parameter listener, or Store allocation. Positive-level initialization repopulates live cells and their rings after scheduling is cleared.
Evidence and limits:
- An in-memory check of each original
HEADpatch source, suppressing the init hook before configured color assignment, reproducesCannot read properties of undefined (reading 'initialize'). The fixed constructor regression runs that same pre-init setter path successfully. - Eight new regressions in
testing/nlapi-apps.test.mjscover each copy independently: pre-init color assignment (with init explicitly prohibited during construction), the shipped nine-cell pattern (6 newborn, 3 adult, 9991 dead), Store/callback/RAM/listener identity, three resets, changed NBI/MAT weights, and Random/Reg 6/Reg Custom RAMs with deterministic seeding. - A separate full-grid weighted-neighbor oracle checks colors, levels, state totals, and emitted display values across repeated steps and resets, including wraparound and rectangular grids. This is not a duplicated model fixture or a forced standalone/Studio equality target. It covers the tested positive, distinct weights and default birth interval; it does not certify every parameter combination (for example sparse scheduling with birth at zero).
- Focused suite: 111/111. Documented broad baseline: 173/178, with the same five pre-existing Designer/catalog/resource failures. No runtime, scheduler, weighted transition rule, or unrelated failing check was changed.
- A fresh browser page loaded standalone source and a separately generated
Studio-library session through the existing local servers. Each reported
(dead,newborn,adult) = (9991,6,3)initially,(9984,8,8)at step 5, and the initial counts after reset. Changing NBI/MAT to 2/4, resetting and stepping gave(9999,0,1)at time 1, then restored initial counts on reset. No page exceptions occurred during these flows. Studio needed DOM-dispatched controls after normal clicks failed visibility/stability checks; two 404 console resource messages appeared during session navigation. This is runtime/count evidence, not UI-layout or asset-completeness certification. - Validation used source modules directly; the distribution build was not
rerun because its build script deletes the existing shared
disttree.
Additional API-level coverage:
- Template-style turtle construction calls
init()before defaults, then the population initializer calls it after defaults. Initial population creation currently callssetup()twice; reset recreation calls it once. - A child created by a patch in
"C_A"does not act until the next step. In"C/A", the intermediate shift inserts it before the agent phase, so it acts during the same step. - Death immediately hides an agent from patch queries; population and spatial index removal follow during shift.
- Patch-ahead sensing distinguishes current and pending headings and respects the cell world's wrapping/clamped-boundary configuration.
- Neighbor queries and spatial-index updates before and after commitment.
- Immediate and pending pen trails: sampled patch colors, movement timing, untouched cells, and restoration of the patch-color reset baseline.
The Follower test also records an existing app-level defect:
Follower.init()
registers a new MOD listener on each reset. Listener counts grow rather than
remaining constant. The regression test captures that behavior to expose a
future change, not to designate listener accumulation as a supported contract.
One-time registration and repeatable initialization must be separated before
claiming reset is free from duplicate listeners.
The headless loader evaluates embedded model classes against real API modules and supplies initialization messages locally. It is not a browser deployment or transport test. The tests cover Cartesian worlds; hex topology, collision accuracy, wrapped trail geometry, nonzero Fng diffusion, and all saved apps remain outside this batch's evidence.
The full regression run after the second batch has 93 passing tests and the same five pre-existing failures. No new failures were introduced.
Third batch: approved sampling and parsing contracts
The user approved these contracts before implementation. This batch repairs
n_of, one_of, and parse in NLRoot, with
22 checks in utility tests.
The new tests were run before the fixes: 20 failed and two already passed.
All 22 pass after the repairs, together with the 31 earlier compatibility checks.
The full suite now has 115 passes and the same five pre-existing failures,
with no new failures. The focused command including the new utility checks is:
node --test testing/nlroot-utilities.test.mjs testing/nlapi-compatibility.test.mjs testing/nlapi-apps.test.mjs
Sampling
- Both methods accept JavaScript arrays. Other collection types throw
TypeError; they are not silently converted. n_of(size, elements)samples array positions without replacement, shuffling a shallow copy with the existingNLRoot.shuffle()and returning up tosizeentries. Object references and duplicate values are preserved.- Size must be a nonnegative integer; invalid sizes throw
RangeError. Zero returns an empty array, and a size greater than the array length returns all entries in shuffled order. one_of(elements)returns one randomly selected entry usingNLRoot.random(), orundefinedfor an empty array.- Neither method mutates its input. No new random-number source or seed policy is introduced.
const candidates = [firstAgent, secondAgent, thirdAgent];
const pair = NLRoot.n_of(2, candidates);
const selected = NLRoot.one_of(candidates);
Token parsing
parse(text) accepts a string and returns an array of strings:
- Whitespace separates tokens; repeated or leading/trailing whitespace is
ignored. Empty or whitespace-only input returns
[]. - Double quotes group one token and are removed. Interior whitespace is preserved. A quoted empty string produces an empty token.
- Quoted tokens must be separated from other tokens by whitespace.
- Unterminated quotes, embedded quotes in unquoted tokens, and text adjoining
a closing quote throw
SyntaxError. Non-string input throwsTypeError. - There are no escape sequences or single-quote grouping rules. Backslashes and single quotes are ordinary characters.
NLRoot.parse('alpha "beta gamma" ""');
// ["alpha", "beta gamma", ""]
These are utility repairs, not changes to the execution model. forward, fd,
slash-mode shift hooks, tuple callbacks, configuration defaults, and reset
semantics remain unchanged. The user explicitly chose to preserve that legacy
behavior for now. A targeted search found no tracked NLRoot/APIRoot calls to
these three utilities outside their declarations before this batch; external
saved apps remain outside that evidence.
Next gates, before broader architectural changes:
- Sampling and parsing contracts are now approved, implemented, and tested. Use them in the reference chapters rather than the original Java-port logic.
- The second batch covers representative mixed patch/agent behavior, births, sensing, and trails. Add browser checks before any user-visible change, and extend coverage to the specific topology/physics affected by each repair.
- Decide a compatibility mechanism before repairing
max_one_of_value,forward,fd, slash-mode shift hooks, or initialization/reset semantics.Fngalready callsforward(1.0), so fixing its movement behavior is not a neutral cleanup. - Agree on custom-state registration and public/internal API boundaries before refactoring their implementation or publishing them as stable guarantees.