Skip to documentation

PhET simulation pilot

This is a local feasibility pilot, not a deployed service or catalog-wide compatibility claim. Density 1.2.9, Pendulum Lab 1.0.34, and Circuit Construction Kit: DC 1.5.1 compile from pinned public source with one shared collector. The selected manifests match the latest publicly retained numeric release-branch tips checked on November 9, 2026. They do not represent current private PhET releases.

Author a simulation node

The SPARC authoring catalog includes an atomic simulation node. It requires an accessible label and a simulation object containing an operator-approved buildId and one to 32 declarative bindings:

In the visual editor, select Simulation from the palette, enter its label, choose an approved build, and edit Observation bindings (JSON). Changes apply when the field loses focus; invalid changes show an inline error and are not applied. The starter address is an explicit placeholder and must be replaced with a validated catalog address before launch. Authoring preview displays the label without starting a simulation or collecting telemetry.

{
  "id": "simulation",
  "nodeType": "atomic",
  "atomType": "simulation",
  "label": "Density: mass and volume",
  "simulation": {
    "buildId": "replace-with-the-validated-pinned-build-id",
    "bindings": [
      {
        "name": "mass",
        "address": { "path": "screens.1.model.blockA.massProperty" },
        "projection": "value"
      }
    ]
  }
}

Paths are first-discovery addresses, not continuously updated array positions. Each address has exactly one structural path or supplied tandem name. Validate it against the pinned build's catalog. Object identity is a session-local ID; array positions are descriptive paths and must not identify dynamic components. Missing, ambiguous, unsupported, or incomplete required evidence stops the attempt. Required values must have observed notification coverage; initial-only fields are rejected.

The supported projections are value for scalar properties, count for collection size, and any-nonzero for an explicitly named numeric memberProperty of collection members. any-nonzero uses an absolute numerical tolerance of 1e-8. The circuit activity uses currentProperty. These are declarative projections over the common catalog and snapshots; there is no circuit-specific collector.

Bindings become slots in an interface-event fact with eventType: "simulation-observation". Author conditions and effects in the existing productionRules DSL. The host evaluates settled frame checkpoints in order after ordered browser receipt, independently of research storage. It does not turn observations into answer submissions or KC/model updates. Those require explicitly authored effects. This pilot uses static authored rules and does not support an AutoTutor/LLM-driven simulation policy.

Guard completed tasks with persistent SPARC state. The example activities set simulation.completed and require it before the learner selects Done. Repeated observations cannot repeatedly award task completion. Completed work and prior instructional history survive reload; the physical simulation state does not.

Local builds and activities

The application repository owns packages/phet-monitor/ and tools/phet-pilot/. releases.json pins the original dependency manifests and supplemental locale metadata. Acquisition and builds reject unexpected source edits. Density and CCK have the explicitly approved supportedBrands metadata addition for adapted-from-phet; Pendulum's baseline is unmodified. Shared changes are confined to Axon, Joist, Scenery, Tandem, and the Chipper preload list.

After building all three monitored artifacts with the current shared sources:

node tools/phet-pilot/create-activities.mjs 'C:/dev/mofacts_config/SPARC PhET Pilot'
node tools/phet-pilot/verify-activities.cjs 'C:/dev/mofacts_config/SPARC PhET Pilot'
node tools/phet-pilot/serve-pilot.mjs

The canonical lesson directory contains three TDF/stimulus pairs. The upload package is C:/dev/mofacts_config/SPARC PhET Pilot.zip. The generator binds each activity to the current verified build identity; regenerate and update the local lessons when shared sources change.

The simulation server binds only 127.0.0.1:3212, verifies artifact hashes, and serves only the three approved HTML artifacts. It writes an ignored tmp/phet-pilot/settings-fragment.json containing public.simulationBuilds. Merge that public catalog into an isolated ignored local settings file and restart the canonical Docker watcher at http://localhost:3200 through deploy/hotfix-local.ps1. Do not edit production settings. The pilot bridge permits only that host origin. No second MoFaCTS application server is used.

Import the ZIP under the intended author/admin account through Content → Upload MoFaCTS Package, then launch the new activities from Practice. They demonstrate:

Activity Instructional condition
Density Increase wood volume above 5 L while mass/volume remains 400 kg/m³
Pendulum Lab Change length to at least 0.75 m, then observe left–right–left motion
Circuit Construction Kit: DC Create and remove components, then form a circuit with nonzero current

The pendulum task detects angular crossings from observed property changes at settled checkpoints; it is not an exact period estimate. Separate browser probes passed representative native pointer/keyboard controls, reset, and screen switching for all three simulations, with supported-state reconstruction checked at each stage. The local Pendulum URL includes its documented ?accessibility flag; the ten-minute timing comparison used the default upstream launch instead. CCK keyboard deletion in the embedded test uses the native focusable wire control; a canvas-only selection with BODY focus did not respond to Delete in this browser. Closed-circuit setup in the reproducible host test uses public model APIs. These probes do not constitute a complete accessibility audit.

Trace integrity and research access

Protocol 2 separates reactive instruction from research persistence. Validated ordered batches update SPARC working memory and rules directly. Browser receipt acknowledgments release the child to deliver further events while an independent ordered writer saves research batches. The shared collector remains object-event based, with no model polling. Required bindings and sequence/coverage checks still apply before instructional effects.

The host retains accepted batches in a bounded 32 MiB queue until storage completes. Research failure or overflow stops further dependent instruction and marks the attempt incomplete. Decisions already made keep their attempt/sequence references and are not retroactively erased; their raw evidence can still be pending. Normal departure drains accepted writes before closing; abrupt browser loss can interrupt unsaved research. Restart preserves completed work and marks the previous attempt interrupted. This pilot does not provide a crash-proof offline outbox.

Use verify-host.mjs <activity-ids.json> --hold-research to hold the first research write until actual instructional feedback appears, then release storage and verify normal persistence/reload. The failure and bounded-queue contracts are tested with synthetic streams.

The iframe receives a random attempt identity, protocol/build metadata, and acknowledgments. Authentication and learner identity remain in MoFaCTS. Messages require the exact origin, iframe source window, protocol version, build, and attempt. Traces are split into ordered chunks of at most 4,000 records and 1 MiB of UTF-8 JSON; record notifications schedule a flush in the next event-loop task. Idle streams have no recurring timer; outstanding batches have a one-second retry and a ten-second deadline. Identical retries share the pending transaction. Conflicting retries, sequence gaps, timeouts, queue overflow, individually oversized records, and unsupported required bindings stop dependent instruction.

simulation_telemetry is a server-only collection separate from scored history. It stores authenticated attempts and deduplicated ordered batches. Mongo transactions preserve the attempt cursor and batch atomically. Indexes support exact user/scope interruption and bounded per-attempt export. Reload interrupts the old attempt before enabling the explicit Start button. Host failures mark the attempt incomplete and retain a bounded reason such as host-queue-overflow or transport-timeout; ordinary departure records interrupted with left-node. Existing histories retain instructional decisions and their observationEvidence references.

There is no broad reactive publication or automatic ordinary history export. exportSimulationResearchTrace(attemptId, afterSequence) requires an administrator and returns attempt metadata, at most four batches, and a nextSequence cursor. Start at zero and pass the returned cursor until the batch list is empty. Attempt status and reason must accompany research interpretation. Raw traces must not be interpreted as complete physics replay or automatic correctness observations.

The existing administrator-authorized account deletion removes that user's research attempts and batches through the telemetry owner. Deleting lesson content or assets does not erase research traces. No account deletion was performed while implementing the pilot.

The shared collector uses object-level events after one initial model discovery. Property notifications, emitter events, and Axon observable-array API mutations are observed directly. Frame callbacks settle already-captured records and affected cached identity edges; they do not inspect model values. No model-step hook, polling mode, full-model rescan, or full-frame reference collector remains.

Plain fields, ordinary arrays/maps/sets, typed-array physics buffers, and vector/matrix direct writes are initial-only, unsupported for continuous observation. Objects explicitly carried by events may receive labeled event-point snapshots. Getters, closures, and rendering/browser objects remain unsupported. Direct backing-array or defineProperty writes bypassing Axon are unsupported. The trace contains object and field-level coverage inventories; required bindings cannot use initial-only data.

Startup capture begins before the first model factory. Early primitive notification values and order are retained until ownership is known; pre-discovery mutable references resolve later and cannot establish their earlier field values. Earlier module-loading activity is outside coverage. Dynamic identity uses IDs and collection references, while structural paths describe first discovery. Event-driven trial deletion releases affected disposed/detached subgraphs without model rescans. Silent ordinary-array membership changes cannot establish detachment.

Verification and release boundary

The current registration-contract builds passed the local functional checks described below. Full milestone acceptance remains incomplete because the current builds have not received controlled ten-minute baseline/monitored performance comparisons. Older collector timing measurements do not qualify these builds.

Build logs, source/license inventories, artifact hashes, compressed model traces, coverage inventories, screenshots, and measurements are retained under the ignored application tmp/phet-pilot/ directory. The source and build instructions remain reviewable in the repository. Upstream notices and license files are retained. A distribution/asset-license review is not established by the inventory alone.

Research-only coalescing starts a 100 ms window for each pending storage group. Contiguous receipts are combined into bounded storage batches without changing record sequences. Further writes follow completion of the preceding write and receive their own coalescing window, limiting sustained requests to at most ten per second. No timer runs on an idle queue. This storage scheduling never gates SPARC observation delivery.

The shared monitor now uses an explicit version-1 registration contract for properties, emitters and observable collections. Shared framework constructors supply kind, exact notification source, public value reader and collection child handles. Rendering/instrumentation exclusions use registered constructor identities. Private-field lookalikes never gain observed coverage. The declaration in packages/phet-monitor/registration-contract.d.ts is identical across the three adapted builds; release manifests record its hash. Framework adapters preserve startup capture, collection identity, disposal and the independent reactive SPARC/research paths. No simulation model is patched and no polling mode is introduced.

Registration-contract verification passed on all three final build identities: 43 contracts, full application typecheck/lint, reconstruction and startup checks, zero inspections over 1,000 idle frame callbacks, representative native controls/reset/screen switching, and a 100-cycle CCK retention probe with zero retained references among 200 tested component/property references. Real SPARC feedback preceded held research writes, eventual storage reached the supporting observations, and reload preserved completed tasks. Exact source/build hashes and results are in ignored tmp/phet-pilot/registration-verification.json. No full Meteor CI invocation or deployment was performed for this revision. Controlled ten-minute overhead qualification on these final builds remains outstanding. Feedback timing is a diagnostic, not a monitor correctness requirement.