# Development, validation and operations

[Documentation index](README.md) · [Source audit](source-audit.md)

This guide describes the checked-in 2.8.0 tools, not a claim that a particular
machine, deployment or physical device has passed them. The authoritative
publication requirements remain [TEST-PLAN.md](../TEST-PLAN.md). The
[HARNESS-IMPLEMENTATION-PLAN.md](../HARNESS-IMPLEMENTATION-PLAN.md) contains planned
and staged work; its existence does not make every proposed feature a production
API or an implemented test command.

## Repository layout

| Path | Role |
| --- | --- |
| `kieran-epk-device-orientation.php` | Plugin identity, page configuration, editor UI, REST capture, signed downloads and content transforms. |
| `assets/` | Production timing, catalogue, orientation, shared effects, paper-plane sprites, Clay/Stars and admin assets. |
| `docs/` | User guide and source-audited API reference. |
| `tests/` | Node contracts, PHP transform checks, integration fixtures and mobile capture/conformance utilities. |
| `tests/harness/plugin-core/` | Shared local harness assets/contracts. |
| `tests/harness/clay-stars/`, `tests/harness/california-screamin/` | Release-specific harness configurations. |
| `tools/` | Validation, preview building, read-only audits and packaging utilities. |
| `migration/`, `qa/` | Staged migration data and dated evidence. Do not interpret a historical result as approval for a new artifact. |
| `dist/` | Versioned WordPress ZIPs and manifests. These are not automatically regenerated by documentation edits. |

## Normal unit validation

The existing coordinator is [tools/validate.ps1](../tools/validate.ps1), with:

| Argument | Meaning |
| --- | --- |
| `-Mode Unit` | Default. Lint, contracts, encoding, transforms and scaffold checks, without rebuilding migration/package artifacts. |
| `-Mode Package` | Explicitly rebuild migration and distribution artifacts and check package identity/layout. |
| `-Mode All` | Run Unit, then Package. This is not a read-only documentation check. |
| `-ReleasesRoot` | Optional release-workspace root forwarded to the migration builder in Package/All mode. Unit mode does not need it. |
| `-IncludeFailureFixture` | Deliberately exercise a syntax failure; valid only when Unit runs. Not a normal passing test run. |

The scripts assume the repository's Windows/PowerShell workflow, with `php`,
`node` and `python` on PATH. The source does not declare a definitive minimum
version matrix for these tools or for WordPress/PHP. Record actual versions used
rather than inventing a support guarantee. In particular, Package mode invokes
`powershell` explicitly and some paths/exclusions are Windows-specific.

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\validate.ps1 -Mode Unit
```

The process-scoped execution-policy argument does not change machine-wide
policy. Inspect scripts before running them. Unit mode performs PHP lint,
JavaScript syntax checks, sequential direct execution of each top-level
`tests/*.test.cjs`, PHP Clay transform/collision and download-path checks, shared/Clay harness
scaffold validation, UTF-8/null/replacement-character checks and plugin
name/version checks. It deliberately excludes the failing fixture from the
normal syntax scan.

**California Screamin' caveat:** the coordinator treats its full-page scaffold
audit failure as a warning because the page retains known width/background-position
blockers. Passing Unit is therefore not a statement that every release's browser,
visual, accessibility or performance gate passed.

The coordinator runs each Node contract as `node path/to/file.test.cjs`, not one
parallel `node --test` invocation. That is also the appropriate focused pattern:

```sh
node tests/dance-moves-core.test.cjs
node tests/dance-moves-diagnostics.test.cjs
node tests/timed-lyric-popover.test.cjs
node tests/exact-cue-seek.test.cjs
node tests/cue-timeline.test.cjs
node tests/effects-primitives.test.cjs
node tests/epk-download-contract.test.cjs
node tests/paper-dreams-flight.test.cjs
node tests/catalogue-timing.test.cjs
node tests/clay-adapter.test.cjs
node tests/sensor-raf-scheduler.test.cjs
node tests/runtime.test.cjs
node tests/california-adapter.test.cjs
php tests/validate-clay-transform.php
php tests/validate-clay-legacy-collision.php
php tests/validate-epk-download-paths.php
```

These focused commands do **not** replace the coordinator. Other checked-in
contract files cover WordPress metadata/uploads, shared component timing,
orientation maths, mobile API parity, capture duration and harness foundations.
See [tests/](../tests/) for the inventory. Use
[tools/test-validator-failure.ps1](../tools/test-validator-failure.ps1) when
verifying that the deliberate failure is correctly attributed and returns a
nonzero exit status.

## Local harness workflow

Serve the repository rather than opening timing-fetch fixtures through `file:`:

```sh
python -m http.server 8765 --bind 127.0.0.1
```

From the resulting local site, open the appropriate checked-in fixture under
`tests/`, such as `dance-moves-integration.html`, `shared-component-timing.html`
or `mobile-harness.html`, plus `effects-primitives-preview.html` and
`paper-dreams-flight-preview.html`, or follow the release harness's own files. Supply only
approved, locally available media/manifests; repository fixtures are not proof
that an external canonical master exists on your machine. A local HTTP server
does not make a LAN-served page a secure physical-phone sensor origin.

The shared harness validation commands used by the coordinator are:

```sh
python -X utf8 tools/validate-effect-harness.py tests/harness/plugin-core --mode scaffold
python -X utf8 tools/validate-effect-harness.py tests/harness/clay-stars --mode scaffold --manifest-file tests/harness/clay-stars/clay-stars.json --shared-root tests/harness/plugin-core
python -X utf8 tools/validate-effect-harness.py tests/harness/california-screamin --mode scaffold --manifest-file tests/harness/california-screamin/california-screamin.json --shared-root tests/harness/plugin-core
```

The [validator source](../tools/validate-effect-harness.py), release manifests and
[test plan](../TEST-PLAN.md) define their expected evidence. Manifest-driven
master/timing/effect controls, deterministic input/cue simulation, attributed
handler timing, CSS-property audits and release snapshots are development
facilities. They are not an additional public production control plane.
`EPKEffectHarnessProbe`, simulated orientation events and capture/conformance
utilities under `tests/` must not be shipped as front-end dependencies.

When instrumenting the core, set `danceMovesConfig.diagnostics = true` **before**
the core asset executes, then install a bounded `setDiagnosticsSink()` receiver.
Give every cue/lyric/interval effect a stable attribution ID. The normal
WordPress configuration does not enable diagnostics. See
[diagnostics API](api/javascript.md#diagnostics) for the payloads and limits.

## Release selection before changing documentation

Do not infer the latest plugin from the default branch. At this audit, `main`
remains 2.4.0 while the latest published tag is **v2.8.0**. The release commit is
`4cb6a71f60459b5579be87d7e55ac8b1426c578e`; the current staging branch is one
publication-evidence commit ahead, with identical production source and README.
Compare tags, release assets and staging before selecting a baseline.

This patch replaces the earlier 2.4.0 documentation patch; it is not incremental.
Apply to a clean 2.8.0/staging checkout without the previous patch's added `docs/`.
For example, after saving local work and placing the patch outside the checkout:

```sh
git fetch origin timed-lyrics-staging-2026-09-20 --tags
git switch -c docs/complete-api-reference-v2.8.0 origin/timed-lyrics-staging-2026-09-20
git apply --check /path/to/DanceMoves-2.8.0-documentation.patch
git apply /path/to/DanceMoves-2.8.0-documentation.patch
```

For an immutable release-based branch, use `v2.8.0` as the start point instead.
Review any newly fetched changes before applying. The commands do not push,
merge, rebuild a ZIP or write to WordPress. No PHP/JavaScript version bump is
required for a source-documentation change.

## Acceptance checks for an integration

Exercise ordinary playback, pause/resume, end/replay, playback-rate changes,
forward/backward seeks, more than one player, absent/invalid timing files,
blank/repeated/long lyric lines and a dynamically inserted audio element. Check
that only master-length players bind and call `discoverAudio()` after deliberate
dynamic insertion. Recreate interval subscriptions around backward seeks when
required by the documented scheduling caveat.

For shared timelines, test zero-time cues, default/opt-in playing and paused
landings, duplicate IDs, overlapping intervals, gap discontinuities, rate changes,
callback failures and repeated teardown. Confirm full interval reconstruction,
not merely a transient class firing. The core landing policy is different from
the timeline's opt-in policy. Verify every `next*` and `nextVisible*` field across
blank cues and the final line.

For downloads, test real GET/HEAD and invalid signatures, local-file availability,
Unicode filenames, cached page links and the original player's seek behaviour.
For paper planes, test hero clipping/stacking, compact counts, offscreen pause,
reduced-motion hiding and the forced-colour CSS-versus-loop distinction. For the
shared quality controller, test a preference already active at initial mount.

Test at desktop, tablet and narrow-mobile widths, signed out, with reduced motion
and forced colours. Preserve complete readable lyrics, keyboard focus, controls,
safe areas and static artwork. Test both permissions granted and denied on actual
supported devices, plus hidden-tab/page lifecycle changes. Confirm the adapter's
root identity before interpreting diagnostic attributes.

Performance acceptance requires sustained-load measurements and recovery trials,
not merely a snapshot of `profile: "full"`. Record the exact plugin/source or
package hash, browser/device, viewport, time and evidence location. Synthetic
orientation events verify contracts, not native trust, physical-device smoothness
or input-to-display latency. The motion-capture schema stores orientation/parity
data; it does not contain all the physical performance telemetry required by the
test plan.

The plugin release gate and the EPK pre-live gate are independent. `BLOCKED` and
`NOT RUN` are not `PASS`. No WordPress upload, page-meta write, timing-media upload
or legacy-plugin deactivation is authorised merely by running a local test.

## Packaging and upgrade identity

Only run the following for an intentional package/migration rebuild:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\validate.ps1 -Mode Package
```

[tools/package.ps1](../tools/package.ps1) reads the version from the PHP header,
uses the root `kieran-epk-device-orientation/`, and includes **only** the entrypoint,
root `README.md` and `assets/`. It writes `dist/DanceMoves-<version>.zip` and the
matching package manifest, including ZIP/file hashes and layout checks. The
manifest builder is a separate step invoked by Package mode.

`docs/`, tests, tools, QA, migration evidence and master audio are not copied into
the WordPress ZIP by the current allowlist. The complete API reference lives in
the source repository; the root README links to it online for packaged readers.
This documentation patch deliberately does not alter that packaging policy,
regenerate immutable evidence, overwrite existing ZIPs or bump runtime versions.
A later intentional package rebuild will include the updated root README.

Do not rename the internal folder or PHP entrypoint to `DanceMoves`: WordPress
must recognise the same installed plugin on upgrade. Check both the artifact
identity and package layout before deployment. Preserve an exact rollback ZIP
and its checksum. Reactivating the old Clay/Stars effects plugin suppresses the
new page-252 takeover when its `KS_CLAY_STARS_EFFECTS_VERSION` constant is defined;
it is not a general rollback for the shared timing core.

## Troubleshooting

| Symptom | Check / documented limitation |
| --- | --- |
| `DanceMoves` undefined | Script dependency/order, whether the request is a WordPress Page, footer output and browser console. The core has no public ready event or load promise; `dance-moves-effects-ready` belongs to the separate shared-effects layer. |
| Page shows fallback BPM | Metadata must be finite and within 20–400; blank/invalid values use 120 without claiming a known tempo. |
| Cue file has no entries | Use bracketed LRC-style timestamps, not CD `TRACK`/`INDEX` syntax. Blank cue labels are ignored. |
| Fetch failure looks like status `none` | Final initialisation overwrites the temporary `unavailable` marker. Inspect network/console and entry counts, not status alone. |
| Cue behaviour differs between integrations | Core LRC playback arms one cue within 50 ms, including at zero; the shared timeline fires crossings and has opt-in playing seek landings. Initialize persistent timeline state in render. |
| New audio does not follow cues | Insert it before discovery or call `discoverAudio()` after insertion; confirm metadata/duration and then normal playback events. |
| Repeating interval waits after backward seek | A retained interval boundary is not fully rebased; remove and recreate the subscription for that seek. |
| Custom lyric output remains visible on pause | `onLyric()` is not a complete playback-state stream. Handle pause/end for your custom renderer. |
| Lyric CSS does not match | The generated layer is under `body`, outside `.ks-epk`; component-local defaults also outrank merely inherited palette variables. |
| Phone UI absent | Production mobile/secure-context/sensor gate, root selector, adapter mapping, reduced motion and permission support. |
| Performance never reaches a fixed 60 FPS | Identify the controller: Clay uses median/p95 and probation; shared quality uses frame-count windows and fixed cooldowns. Neither proves physical refresh rate. |
| Paper snapshot reports 2.6.3 | This is the adapter-local marker; plugin `DanceMoves.version` is 2.8.0. |
| MP3 opens inline | Confirm the anchor has `download`, passed through main Page `the_content`, and matched the upload base. Direct player URLs intentionally stay inline. |
| Untouched assets fail byte hashes | Check `.gitattributes`, checkout line-ending conversion and the exact locked production bytes; never update locks merely to silence a mismatch. |
| A gate passes on a simulator but not a phone | Keep synthetic contracts and real physical-device evidence separate. |

For additional read-only inspection and preview utilities, inspect the commands
and assumptions in [tools/](../tools/): `audit-live-epk-motion.mjs`,
`capture-public-viewports.mjs`, `inspect-phone-epk.mjs`,
`build-clay-stars-preview.ps1` and `build-california-screamin-preview.ps1`.
No generic install command or undocumented command-line flags are implied here.

## Documentation maintenance

Keep troubleshooting limited to reproducible current plugin or integration problems,
with checks and links to the relevant API contract. Put test prerequisites beside
the commands they affect. Do not accumulate one-time shell/automation mistakes,
resolved fixture bugs, private workspace notes or dated success reports in the
README. Retain release provenance in existing version-control and migration/QA
records; changing documentation is not a new deployment or validation result.
