Files
XZBT-NGN/test-fixtures/reference-exhibits/aquarium/contract-adapter.js
T
Labyricorn 745912e451 Steps 6.4-6.7B — Local surfaces, reference-exhibit validation, SciFi Observation surface
One commit for the work accumulated in the working tree since Step 6.3,
which had never been split into per-step commits:

- src/local-surfaces.js + src/surface-url.js (new); src/ui.js,
  src/validation.js, src/connection.js and public/index.html updated for
  local-surface hosting and generic surface rendering
- tests: local-surfaces (20), scifi-surfaces (24) and postmessage-interop (7)
  new; connection/museum-gallery/surface-validation suites updated
- reference exhibits: shared/contract-core.js defaults to Contract 5.3
  (major 5, minor 3, xzbt 5.3); museum-gallery advertises its surface
  catalog; aquarium/haunted-house/planetarium adapters updated
- SciFi-XZBT (Step 6.7A/6.7B): surface-mode.js + surface-bus.js,
  Observation-surface boot branch, local-change hooks, view.pillars /
  view.warp-flight targets; fixture byte-identical to G:/.vibe/SciFi-XZBT
- SciFi-XZBT contract adapter handshake fix: the inbound bridge filter no
  longer gates on an exact advisory xzbt value (Contract 5.3 §6.5), only on
  its presence/type, matching the host's own envelope validation; the
  adapter now advertises contract minor 3 / version 5.3.0, which it already
  implemented via the 5.3 surfaces field. Root cause of the five failing
  postmessage-interop tests (host hello was silently dropped).
- docs: architecture 6.4 and 6.7A, reference 6.6 and 6.7; evidence logs;
  test-fixtures/PROVENANCE.md resync record

Test results: NGN 154/154 (was 149/154); postmessage-interop 7/7 (was 2/7);
SciFi contract harness 21/21, real-adapter suite 32/32. git diff --check
clean for changed files; two pre-existing trailing-whitespace lines remain
in test-fixtures/reference-exhibits/scifi/index.html, copied verbatim from
the authoritative SciFi source.

Step 6.7 live verification (browser Observation, packaged standalone) is
still pending and is not claimed here.
2026-09-14 19:45:27 -07:00

218 lines
7.0 KiB
JavaScript

/*
* Aquarium — XZBT Exhibit Contract 5.3 adapter.
*
* This file is the only place in the exhibit that knows the contract exists.
* It does three things and nothing else:
*
* 1. declares the public target catalog against the exhibit's real model
* 2. binds each target to a real exhibit service (setter / reader / action)
* 3. routes the native UI through the canonical mutation path
*
* It holds no state of its own. Every value it reports is read live from the
* exhibit, so there is no shadow copy that can drift (Authoring Guide §W).
*/
(function () {
'use strict';
var Core = window.XZBTContractCore;
var IDENTITY = {
product: 'Aquarium',
version: '1.0.0',
build: 'reference-exhibit'
};
/* ------------------------------------------------------------------ *
* Public target catalog.
*
* IDs are canonical and domain-shaped. They are not DOM ids, CSS
* selectors, or widget names (Authoring Guide §W).
* ------------------------------------------------------------------ */
var DESCRIPTORS = [
{
id: 'tank.species',
kind: 'selection',
label: 'Species',
description: 'The species currently stocked in the tank.',
options: [
{ value: 'clownfish', label: 'Clownfish' },
{ value: 'blue-tang', label: 'Blue Tang' },
{ value: 'neon-tetra', label: 'Neon Tetra' },
{ value: 'angelfish', label: 'Angelfish' },
{ value: 'guppy', label: 'Guppy' }
],
readable: true, writable: true, restorable: true,
category: 'tank', requires: []
},
{
id: 'tank.population',
kind: 'range',
label: 'Population',
description: 'Number of fish in the tank.',
min: 1, max: 40, step: 1, unit: 'fish',
readable: true, writable: true, restorable: true,
category: 'tank', requires: []
},
{
id: 'tank.lighting',
kind: 'selection',
label: 'Lighting',
description: 'Lighting programme for the tank.',
options: [
{ value: 'daylight', label: 'Daylight' },
{ value: 'twilight', label: 'Twilight' },
{ value: 'moonlight', label: 'Moonlight' },
{ value: 'blackwater', label: 'Blackwater' }
],
readable: true, writable: true, restorable: true,
category: 'tank', requires: []
},
{
id: 'tank.current',
kind: 'range',
label: 'Water current',
description: 'Strength of the circulation pump, 0 (still) to 1 (strong).',
min: 0, max: 1, step: 0.01,
readable: true, writable: true, restorable: true,
category: 'tank', requires: []
},
{
id: 'tank.temperature',
kind: 'range',
label: 'Temperature',
description: 'Heater setting, 0 (18 C) to 1 (28 C).',
min: 0, max: 1, step: 0.01,
readable: true, writable: true, restorable: true,
category: 'tank', requires: []
},
{
id: 'tank.paused',
kind: 'state',
valueType: 'boolean',
label: 'Paused',
description: 'Whether the simulation is frozen.',
readable: true, writable: true, restorable: true,
category: 'tank', requires: []
},
{
id: 'action.feed',
kind: 'impulse',
label: 'Feed',
description: 'Scatter food across the surface.',
readable: false, writable: false, restorable: false,
category: 'action', requires: []
},
{
id: 'action.startle',
kind: 'impulse',
label: 'Startle',
description: 'Send a pressure wave through the tank.',
readable: false, writable: false, restorable: false,
category: 'action', requires: []
},
{
id: 'action.clean-glass',
kind: 'impulse',
label: 'Clean glass',
description: 'Run the glass-cleaning cycle.',
readable: false, writable: false, restorable: false,
category: 'action', requires: []
},
{
id: 'telemetry.fish-count',
kind: 'state',
valueType: 'number',
label: 'Fish count',
description: 'Live number of fish currently rendered.',
readable: true, writable: false, restorable: false,
category: 'telemetry', requires: []
},
{
id: 'telemetry.food-level',
kind: 'state',
valueType: 'number',
label: 'Food level',
description: 'Remaining food in the water column, 0 to 1.',
readable: true, writable: false, restorable: false,
category: 'telemetry', requires: []
},
{
id: 'telemetry.water-temp',
kind: 'state',
valueType: 'number',
unit: 'C',
label: 'Water temperature',
description: 'Actual water temperature in degrees Celsius.',
readable: true, writable: false, restorable: false,
category: 'telemetry', requires: []
}
];
/* ------------------------------------------------------------------ *
* Wiring
* ------------------------------------------------------------------ */
function createAquariumContract(exhibit, ui) {
var catalog = new Core.Catalog(DESCRIPTORS);
/* The exhibit has no capability that can fail: it is pure canvas and
* needs no device, no network, and no permission. Reporting `ready`
* once is honest; inventing loading/error transitions would not be
* (Authoring Guide §M). */
var capabilities = new Core.CapabilityRegistry();
capabilities.declare('render', 'ready');
var setters = {
'tank.species': function (v) { return exhibit.setSpecies(v); },
'tank.population': function (v) { return exhibit.setPopulation(v); },
'tank.lighting': function (v) { return exhibit.setLighting(v); },
'tank.current': function (v) { return exhibit.setCurrent(v); },
'tank.temperature': function (v) { return exhibit.setTemperature(v); },
'tank.paused': function (v) { return exhibit.setPaused(v); }
};
var readers = {};
var ids = catalog.ids();
for (var i = 0; i < ids.length; i++) {
(function (id) {
readers[id] = function () { return exhibit.read(id); };
})(ids[i]);
}
var actions = {
'action.feed': function () { return exhibit.feed(); },
'action.startle': function () { return exhibit.startle(); },
'action.clean-glass': function () { return exhibit.cleanGlass(); }
};
var core = new Core.ContractCore({
identity: IDENTITY,
catalog: catalog,
capabilities: capabilities,
setters: setters,
readers: readers,
actions: actions
});
/* Route the native UI through the canonical path. From here on, a
* slider drag and a host `set` are the same transaction, and both are
* reported with the same source semantics (Authoring Guide §H). */
ui.dispatchOverride = function (targetId, value) {
core.applyMutation(targetId, value, 'ui');
ui.sync();
};
ui.invokeOverride = function (targetId) {
core.invokeAction(targetId, {}, 'ui');
ui.sync();
};
/* Keep the controls honest when the exhibit changes for any reason. */
exhibit.onChange = function () { ui.sync(); };
return core;
}
window.AquariumContract = { create: createAquariumContract, IDENTITY: IDENTITY };
})();