test: record Phase 0 feasibility probe and manual evidence
This commit is contained in:
@@ -11,7 +11,8 @@ XZBT is a self-contained browser runtime for declarative procedural audiovisual
|
||||
| [MVP Product Requirements Document](XZBT_0-1_MVP_Product_Requirements_Document.md) | Product scope, user behavior, delivery requirements, and release acceptance | Document revision 0.2; format version remains 0.1 |
|
||||
| [Format Specification 0.1](docs/XZBT_0-1_Format_Specification.md) | Runtime semantics, contract inventory, and required authoring examples | Partial specification; identified contracts still require completion |
|
||||
| [Gap Closure Decisions](docs/XZBT_0-1_Gap_Closure_Decisions.md) | Decisions and rationale for the seven pre-implementation gaps | Decisions incorporated for planning; feasibility is not yet verified |
|
||||
| [Verification Gates](docs/XZBT_0-1_Verification_Gates.md) | Evidence required before architecture commitment, subsystem work, and release | All checks pending; no implementation results recorded |
|
||||
| [Verification Gates](docs/XZBT_0-1_Verification_Gates.md) | Evidence required before architecture commitment, subsystem work, and release | Worklet, audio, and user-performed restart persistence passed; GC1 incomplete |
|
||||
| [Implementation status](docs/IMPLEMENTATION_STATUS.md) | Current phase, stop reason, saved work, and resume prerequisites | Phase 0: worklet loading passed; remaining manual checks pending |
|
||||
|
||||
The PRD is authoritative for product requirements. The format specification is authoritative for runtime semantics where a contract is explicitly defined. The decision record explains those choices; the verification gates define how to check them. These documents must be updated together when a decision changes. An unresolved conflict is a specification defect, not permission for an implementation to choose silently.
|
||||
|
||||
@@ -23,10 +24,12 @@ Begin the implementation plan with Phase 0 from the verification gates. Complete
|
||||
|
||||
The full PRD completion criteria remain the 0.1 release target. Early integrated demonstrations are milestones, not completed MVPs. Reference exhibits develop alongside the engine; Phase 9 completes and audits the suite.
|
||||
|
||||
No runtime, schema implementation, benchmark, or completed feasibility test is supplied by this documentation revision.
|
||||
Phase 0 has begun with a separate [feasibility probe and fixtures](prototypes/phase0/README.md). The [version 3 user-run result](docs/evidence/phase0/2026-09-04-user-run-v3.md) verifies embedded data-URL worklet loading. The [subsequent user-performed restart test](docs/evidence/phase0/2026-09-04-user-restart.md) restored edited activity and volume. The directory-permission/unavailability fallback check remains pending; the agent cannot perform direct-file navigation through its browser tool. No production runtime, completed schema implementation, benchmark result, or completed GC1 gate is supplied yet.
|
||||
|
||||
## Repository configuration
|
||||
|
||||
The [storage-failure evidence](docs/evidence/phase0/2026-09-04-user-storage-failure.md) additionally confirms session settings changes, directory import, and heard native playback during injected write failures. The user also confirmed the visible session-only warning; the injected-storage-failure check passed.
|
||||
|
||||
Copy `.env.example` to `.env` for local repository configuration. `.env` and its variants are ignored by Git; never commit real credentials. The example contains placeholders only.
|
||||
|
||||
The repository's existing [LICENSE](LICENSE) is preserved.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# XZBT implementation status
|
||||
|
||||
**Updated:** September 4, 2026\
|
||||
**State:** Stopped in Phase 0 at GC1; no later phase started\
|
||||
**Baseline commit:** `05fe2b4e021ba86e4a290d05b63c7cae0e386128`
|
||||
|
||||
The user requested sequential implementation with a stop on problems. The [manual version 3 evidence](evidence/phase0/2026-09-04-user-run-v3.md) verifies embedded data-URL worklet loading in direct-file Chrome. The subsequent [user-performed restart test](evidence/phase0/2026-09-04-user-restart.md) restored Blue Study activity 0.37 and master volume 0.19 immediately on reopening. Native tone output and AudioContext suspend/resume are also observed. GC1 still awaits the directory-permission/unavailability fallback check. Ordinary file import, selection of both exhibits, and regular Chrome mode have been confirmed. The agent's direct-file browser-navigation restriction remains in effect.
|
||||
|
||||
| Phase | Status | Evidence or next prerequisite |
|
||||
| --- | --- | --- |
|
||||
| 0 — Contracts and feasibility | Awaiting remaining manual evidence; incomplete | [Restart/settings pass and outstanding checks](evidence/phase0/2026-09-04-user-restart.md) |
|
||||
| 1 — Runtime skeleton | Not started | Complete Phase 0 |
|
||||
| 2 — Common grammar | Not started | Complete Phase 1 and shared contracts |
|
||||
| 3 — Audio engine | Not started | Earlier phases and audio contracts |
|
||||
| 4 — Visual engine | Not started | Earlier phases and visual contracts |
|
||||
| 5 — Events and cadence | Not started | Earlier phases and event/cadence contracts |
|
||||
| 6 — Scenario director | Not started | Earlier phases and scenario contracts |
|
||||
| 7 — Dynamic UI | Not started | Earlier phases and UI contracts |
|
||||
| 8 — Library and persistence hardening | Not started | Earlier phases and library contracts |
|
||||
| 9 — Reference suite completion/audit | Not started | Earlier phases, challenge coverage, benchmark and soak evidence |
|
||||
|
||||
## Work saved
|
||||
|
||||
- A self-contained [Phase 0 probe](../prototypes/phase0/XZBT-phase0-probe.html) for import, isolated probe caching, settings restoration, user-initiated audio, embedded engine worklet loading, suspend/resume, and injected storage failure.
|
||||
- Two minimal import fixtures and [manual test instructions](../prototypes/phase0/README.md).
|
||||
- Syntax checks passed for the probe's JavaScript and both JSON fixtures. These checks do not establish browser behavior or complete GC1.
|
||||
- Preserved the user's version 1 and version 2 evidence, including the failed Blob-module attempts. Version 2 separated tone/worklet tests and added failure diagnostics.
|
||||
- Version 3 changes the fixed processor module from a Blob URL to an embedded data URL and adjusts the page script-source allowlist while continuing to block network connections. Three stubbed regression tests passed; the user's subsequent version 3 run now verifies real loading and connection in direct-file Chrome.
|
||||
|
||||
## Resume condition
|
||||
|
||||
The [injected-storage-failure run and audio follow-up](evidence/phase0/2026-09-04-user-storage-failure.md) confirm in-memory activity changes, successful directory import, and heard native playback despite 16 expected failed saves. The user subsequently confirmed the visible session-only warning. The injected-storage-failure check passed; this does not establish real browser permission-denial behavior.
|
||||
|
||||
Worklet loading and the user-performed restart/settings test have passed and need not be repeated without a relevant code change. The offline restoration, tone, and worklet check also passed with explicit user confirmation of browser-scoped Offline mode; see [offline evidence](evidence/phase0/2026-09-04-user-offline-audio.md). The [user confirmed regular Chrome mode and ordinary import/activation](evidence/phase0/2026-09-04-user-import-environment.md). Directory-permission/unavailability fallback remains unverified. The earlier reload results remain distinct from the subsequent manual restart procedure.
|
||||
|
||||
The browser rejection explicitly forbids agent workarounds through alternate browser surfaces, raw commands, or indirect execution. No workaround was attempted. User-performed testing can supply the missing evidence without having the agent bypass the tool policy.
|
||||
|
||||
Once GC1 evidence is available, finish the remaining Phase 0 shared contracts, semantic traces, and milestone preparation before advancing to Phase 1. A manual GC1 pass alone does not mark all of Phase 0 complete. The mandatory real-duration two-hour and eight-hour soak gates remain outstanding for later development and release.
|
||||
@@ -1,7 +1,7 @@
|
||||
# XZBT 0.1 Gap Closure Decisions
|
||||
|
||||
**Record version:** 0.1
|
||||
**Status:** Incorporated into the planning baseline; implementation verification pending
|
||||
**Record version:** 0.1\
|
||||
**Status:** Incorporated into the planning baseline; implementation verification pending\
|
||||
**Related resources:** [PRD](../XZBT_0-1_MVP_Product_Requirements_Document.md), [format specification](XZBT_0-1_Format_Specification.md), [verification gates](XZBT_0-1_Verification_Gates.md)
|
||||
|
||||
Numbering preserves the seven gaps discussed during PRD preparation. Recording a decision closes a design question; it does not establish that a browser feature, performance target, or lifecycle guarantee has passed testing.
|
||||
@@ -28,6 +28,8 @@ Moving or renaming the HTML is a separate compatibility case. Do not promise tha
|
||||
|
||||
**Gate:** GC1 in the verification checklist. A failure of direct-file persistence in the chosen supported environment reopens the deployment decision; do not silently replace the distribution promise with hosted-only operation.
|
||||
|
||||
**Verified packaging refinement:** The [version 3 manual probe](evidence/phase0/2026-09-04-user-run-v3.md) loads and connects the engine-owned worklet from an embedded data URL in direct-file Chrome 152; the earlier Blob URL approach failed in the same user's testing. Use the verified data-URL approach for this packaging path, with an explicit page script-source allowlist and network connections disabled. This does not permit exhibit-authored executable code or declare the remaining GC1 checks complete.
|
||||
|
||||
**Basis:** [MDN file-origin behavior](https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Same-origin_policy#file_origins), [Chrome File System Access API](https://developer.chrome.com/docs/capabilities/web-apis/file-system-access), and [AudioWorklet requirements](https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet). These describe platform constraints, not evidence that the XZBT prototype has passed.
|
||||
|
||||
## 2. One authoritative format specification
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# XZBT 0.1 Verification Gates
|
||||
|
||||
**Status:** Planning checklist; every check is pending
|
||||
**Status:** Phase 0 awaiting remaining manual checks; worklet loading and user-performed restart persistence passed; GC1 incomplete
|
||||
**Related resources:** [PRD](../XZBT_0-1_MVP_Product_Requirements_Document.md), [decisions](XZBT_0-1_Gap_Closure_Decisions.md), [format specification](XZBT_0-1_Format_Specification.md)
|
||||
|
||||
No tests, browser prototypes, benchmark results, or soak results have been run as part of the gap-closure documentation. Mark a check complete only with linked evidence. A recorded design decision is not a passing test.
|
||||
A separate Phase 0 probe has been created and its JavaScript/JSON syntax checked. The agent's direct-file navigation was blocked by browser tool policy; see the [attempt record](evidence/phase0/2026-09-04-feasibility.md). The [user's manual run](evidence/phase0/2026-09-04-user-run.md) reports successful import, saves, and context unlock, but worklet loading failed. Version 2 separates the tests and records better diagnostics; its two stubbed regression tests pass, not its browser gate. GC1, benchmark, and soak acceptance remain incomplete. Mark a check complete only with linked evidence; a partial result does not pass a compound acceptance check.
|
||||
|
||||
The [second user run](evidence/phase0/2026-09-04-user-run-v2.md) confirmed cache restoration between page loads, a heard tone, and audio suspend/resume, but Blob worklet loading failed. The [version 3 user run](evidence/phase0/2026-09-04-user-run-v3.md) verified the embedded-data-URL fix. The subsequent [manual restart test](evidence/phase0/2026-09-04-user-restart.md), supplied after the explicit full-exit procedure, restored edited activity and volume with zero new saves. The directory-permission/unavailability fallback observation remains pending.
|
||||
|
||||
## Phase 0 and later gates
|
||||
|
||||
@@ -13,7 +15,7 @@ Subsystem-specific format details can be completed in dependency order as planne
|
||||
|
||||
| Gap | Decision | Evidence status | Gate placement |
|
||||
| --- | --- | --- | --- |
|
||||
| GC1 | Recorded | Not run | Phase 0, before deployment architecture commitment |
|
||||
| GC1 | Recorded | Data-URL worklet load and user-performed restart/settings restoration passed; remaining manual checks pending | Phase 0, before deployment architecture commitment |
|
||||
| GC2 | Recorded; format draft partial | Contracts and fixtures incomplete | Shared contracts in Phase 0; subsystem contracts before their implementation |
|
||||
| GC3 | Recorded; transition details open | Traces and tests pending | Shared resolver contract before Phase 2; tests with implementation |
|
||||
| GC4 | Recorded; audio mapping details open | Prototype and tests pending | Clock contract in Phase 0; synchronization tests in audio integration |
|
||||
@@ -23,16 +25,16 @@ Subsystem-specific format details can be completed in dependency order as planne
|
||||
|
||||
## 1. GC1: Direct-file feasibility
|
||||
|
||||
- [ ] Record OS, browser version/profile, launch path, and storage mode.
|
||||
- [ ] Open the artifact directly from disk with network unavailable.
|
||||
- [ ] Import two exhibits with the ordinary file picker and activate each.
|
||||
- [ ] Save exhibit definitions, per-exhibit parameters, selection, and master volume to IndexedDB.
|
||||
- [ ] Close the browser fully, reopen the same HTML, and restore the cached library and settings without selecting source files again.
|
||||
- [ ] Verify user-initiated audio unlock and resumed playback.
|
||||
- [ ] If needed by the audio design, load engine-owned AudioWorklet code from the single-file artifact. Otherwise document why this check is not applicable.
|
||||
- [ ] Test renamed and moved HTML files; record observed storage behavior and the support boundary.
|
||||
- [ ] Test unavailable/failed storage; session playback works and the UI explains lack of persistence.
|
||||
- [ ] Test directory import/remembered handles where available and ordinary-picker fallback where unavailable or permission is denied.
|
||||
- [x] Record OS, browser version/profile, launch path, and storage mode. The preceding environment records and [regular-Chrome confirmation](evidence/phase0/2026-09-04-user-import-environment.md) establish Windows, Chrome 152, direct-file launch, and IndexedDB in a regular non-Incognito profile.
|
||||
- [x] Open the artifact directly from disk with network unavailable. The [follow-up capture and user confirmation](evidence/phase0/2026-09-04-user-offline-audio.md) verify restoration, tone, and worklet with browser-scoped Offline mode enabled. The changed mobile user agent on Win32 does not establish physical Android support.
|
||||
- [x] Import two exhibits with the ordinary file picker and activate each. [The user confirmed ordinary import and selection of both fixtures worked](evidence/phase0/2026-09-04-user-import-environment.md).
|
||||
- [x] Save exhibit definitions, per-exhibit parameters, selection, and master volume to IndexedDB. See the version 1 saves and subsequent restored snapshots, including edited values in the restart record.
|
||||
- [x] Close the browser fully, reopen the same HTML, and restore the cached library and settings without selecting source files again. Passed as user-performed evidence following the explicit full-exit procedure; see the restart record for provenance and limits.
|
||||
- [x] Verify user-initiated audio unlock and resumed playback. Native tone heard in versions 2/3; AudioContext suspend/resume states observed in version 2. This is not full audio-engine conformance.
|
||||
- [x] If needed by the audio design, load engine-owned AudioWorklet code from the single-file artifact. Verified with the data-URL module in the version 3 user run.
|
||||
- [x] Test renamed and moved HTML files; record observed storage behavior and the support boundary. The user confirmed restoration from both copies; see [file-location evidence and limits](evidence/phase0/2026-09-04-user-file-locations.md).
|
||||
- [x] Test unavailable/failed storage; session playback works and the UI explains lack of persistence. [Injected-failure evidence](evidence/phase0/2026-09-04-user-storage-failure.md) confirms settings changes, import, and heard native playback remain operational with zero successful writes; the user subsequently confirmed the visible session-only warning. Passed using application fault injection, not real browser permission denial.
|
||||
- [ ] Test directory import/remembered handles where available and ordinary-picker fallback where unavailable or permission is denied. Directory import completed in the injected-failure run and ordinary import/activation passed separately. Actual denial/unavailability fallback remains unverified. Remembered handles are optional and not implemented in this probe; verification is required if implemented in the runtime.
|
||||
|
||||
**Evidence:** prototype artifact, reproducible steps, environment record, observed results. If ordinary-mode persistence fails, reopen decision 1 before dependent architecture commitment.
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Phase 0 feasibility attempt — September 4, 2026
|
||||
|
||||
**Gate:** GC1 / direct-file launch\
|
||||
**Result:** BLOCKED by browser tool policy; browser behavior NOT TESTED\
|
||||
**PRD revision:** 0.2 (XZBT format 0.1)\
|
||||
**Git baseline:** `05fe2b4e021ba86e4a290d05b63c7cae0e386128`\
|
||||
**Probe SHA-256:** `01EBF91DBD1257C8099DCCC80FE3937EEFBF39819568DD7FB4B5482C2756CE5C`
|
||||
|
||||
## Environment observed
|
||||
|
||||
Recorded at `2026-09-04T18:47:45.9013932-07:00`:
|
||||
|
||||
- Windows 11 Pro, version `10.0.26200`, build `26200`.
|
||||
- Intel Core i7-13700F.
|
||||
- NVIDIA GeForce RTX 4060 Ti, driver `32.0.16.1656`; Parsec Virtual Display Adapter also present.
|
||||
- Installed Chrome `152.0.7977.77` and Edge `152.0.4191.62`, identified from executable version metadata only. Neither was launched or tested by this attempt.
|
||||
- Node.js `v24.11.0` and npm `11.12.1`.
|
||||
- Browser tooling selected the Codex in-app browser. Its page engine version, viewport, storage mode, and profile persistence were not observed because navigation was denied.
|
||||
|
||||
## Artifact and static check
|
||||
|
||||
Artifact: [XZBT-phase0-probe.html](../../../prototypes/phase0/XZBT-phase0-probe.html). Fixtures: [Amber Study](../../../prototypes/phase0/fixtures/amber-study.xzbt) and [Blue Study](../../../prototypes/phase0/fixtures/blue-study.xzbt).
|
||||
|
||||
The probe is intentionally separate from the production runtime. It uses the isolated database name `xzbt-phase0-probe-v1`, performs no authored network requests, and does not read local credentials. A Content Security Policy disallows network connections and external resources. These source-level properties are not a completed browser offline test.
|
||||
|
||||
Node's `vm.Script` successfully parsed the HTML's inline script. `JSON.parse` successfully parsed both fixture files. The static check reported:
|
||||
|
||||
```text
|
||||
Probe JavaScript and both JSON fixtures parse successfully.
|
||||
```
|
||||
|
||||
## Browser attempt
|
||||
|
||||
Requested navigation:
|
||||
|
||||
```text
|
||||
file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html
|
||||
```
|
||||
|
||||
Expected: the probe loads from disk, reports environment and IndexedDB availability, and permits user-initiated fixture import.
|
||||
|
||||
Actual: the browser tool rejected navigation before a page snapshot or probe result was available. Its reason was: "The browser URL policy blocks this action." The response also explicitly prohibited workarounds through indirect execution, raw CDP/browser commands, or alternate browser surfaces.
|
||||
|
||||
This is not an IndexedDB error, AudioWorklet error, failed prototype assertion, or evidence that direct-file Chromium operation is impossible. No browser acceptance result can be inferred from this rejection.
|
||||
|
||||
## Checks not performed
|
||||
|
||||
- Actual offline loading and browser environment capture.
|
||||
- File-picker and directory import.
|
||||
- Cache writes, settings changes, and restoration after a full browser restart.
|
||||
- Audio unlock, Blob worklet loading, and suspend/resume.
|
||||
- Rename/move storage behavior.
|
||||
- Injected storage-failure behavior or real browser storage-denial behavior.
|
||||
- Any performance, conformance, or soak testing.
|
||||
|
||||
No GC1 acceptance box is marked passed based on the static checks. Phase 0 remains incomplete and Phase 1 was not started.
|
||||
|
||||
## Required next evidence
|
||||
|
||||
The user can open the artifact directly in desktop Chrome or Edge and follow the [manual procedure](../../../prototypes/phase0/README.md). Capture the on-page evidence after import/settings changes and again after fully closing and reopening the browser. Record failures and cancelled permissions distinctly from passes. On receipt of that evidence, review GC1 before resuming the remaining Phase 0 work.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Phase 0 renamed and relocated copies
|
||||
|
||||
**Result:** Passed as user-reported manual evidence.
|
||||
|
||||
The requested procedure was to open a renamed copy of probe version 3 in the same folder, then another copy in a different folder, and report restoration before importing or changing anything. The user replied: “yes, each restored exhibit and settings”.
|
||||
|
||||
Both copies therefore recovered the cached exhibits and settings in the tested browser environment. This observation does not support assuming storage is isolated by individual HTML filename or containing folder. Keep the runtime database namespaced and treat same-profile copies as potentially sharing cached state.
|
||||
|
||||
The exact alternate paths and separate JSON captures were not supplied. The browser environment is inherited from the ongoing manual Chrome 152 test; no cross-profile, cross-browser, or cross-device behavior was tested. Restoration after renaming or relocating is observed here, not guaranteed for every browser configuration. The supported baseline remains the tested same-file restart behavior, with file import and session operation available when persistence is unavailable.
|
||||
|
||||
The GC1 rename/move observation check is complete. Offline, remaining import/fallback, and browser profile/storage-mode observations remain pending. Phase 0 shared contracts also remain unfinished.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Phase 0 browser mode and ordinary file import
|
||||
|
||||
**Result:** Regular-browser environment and ordinary import/activation checks passed as user-reported manual evidence.
|
||||
|
||||
The assistant asked whether the user was using a regular Chrome window rather than Incognito, and whether importing through the ordinary file input and selecting each exhibit worked. The user answered: “yes, regular chrome, yes everything worked”. This confirms both requested observations; it is not a claim that every outstanding project test passed.
|
||||
|
||||
The preceding records identify Windows, Chrome 152, the original direct-file launch path, and IndexedDB storage. Combined with this confirmation, the tested storage mode is a regular, non-Incognito Chrome profile. A profile name or personal account identifier is not required. The mobile user agent in the offline run remains an environment variation, not evidence from a physical Android device.
|
||||
|
||||
Ordinary file-input import and activation of both Amber Study and Blue Study passed. Directory import separately succeeded in the storage-failure run. Neither confirmation demonstrates actual directory permission denial or API unavailability; that conditional fallback portion remains unverified. Remembered source handles are optional and are not implemented in this disposable probe.
|
||||
|
||||
Phase 0 remains incomplete: the conditional directory fallback observation and shared contracts, semantic traces, and milestone preparation are still outstanding. No Phase 1 implementation is established by these manual probe results.
|
||||
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"probeVersion": 3,
|
||||
"startedAt": "2026-09-05T03:01:55.614Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 15; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Mobile Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": {
|
||||
"width": 1186,
|
||||
"height": 872,
|
||||
"dpr": 2
|
||||
},
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": true,
|
||||
"saves": 0,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "not tested",
|
||||
"audio": "unlocked through user interaction",
|
||||
"tone": "ended and nodes disconnected",
|
||||
"toneHeard": true,
|
||||
"worklet": "loaded from engine-owned data URL and connected",
|
||||
"workletAttempts": [
|
||||
{
|
||||
"at": "2026-09-05T03:02:40.836Z",
|
||||
"stage": "connect node",
|
||||
"status": "loaded and connected",
|
||||
"contextState": "running",
|
||||
"moduleURLScheme": "data:",
|
||||
"moduleSourceBytes": 226,
|
||||
"finalContextState": "running"
|
||||
}
|
||||
],
|
||||
"securityPolicyViolations": [],
|
||||
"storageFaultInjected": false,
|
||||
"errors": [],
|
||||
"restoredAtLoad": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"sampleRate": 48000,
|
||||
"audioState": "running"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# Phase 0 audio follow-up with changed user agent
|
||||
|
||||
**Result:** Offline restoration, native tone, and data-URL worklet passed as user-performed manual evidence.
|
||||
|
||||
The [user-supplied evidence](2026-09-04-user-offline-audio.json) records a page start at `2026-09-05T03:01:55.614Z`, successful cache restoration, a heard tone with completed node cleanup, and successful worklet loading and connection at `2026-09-05T03:02:40.836Z`. The audio context runs at 48,000 Hz. No errors or policy violations were reported.
|
||||
|
||||
The user agent now identifies Android 15 / Pixel 9 / Mobile Chrome 152, while platform remains Win32 and the URL remains the original Windows file path. Viewport is 1186 × 872 with DPR 2. This is consistent with desktop device emulation, but its activation was not explicitly confirmed. It does not establish behavior on a physical Android device.
|
||||
|
||||
After being asked whether the browser network setting was explicitly Offline throughout this run, the user confirmed: “Yes, it was set to offline”. This manual confirmation establishes the test condition and completes the offline probe check. The raw JSON remains unchanged: `onlineFlag` is true and authored requests are zero. Neither field alone establishes offline operation; the result relies on the user's confirmation together with the observed restoration and audio/worklet results. This was browser-scoped offline testing, not a change to system networking.
|
||||
|
||||
Both fixture definitions are restored, but activation of each is not captured. Remaining import/fallback and browser profile/storage-mode observations remain separate checks. This result does not complete Phase 0 or establish offline conformance of a production runtime.
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"probeVersion": 3,
|
||||
"startedAt": "2026-09-05T03:00:54.788Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": {
|
||||
"width": 1920,
|
||||
"height": 959,
|
||||
"dpr": 1
|
||||
},
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": true,
|
||||
"saves": 0,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "not tested",
|
||||
"audio": "not tested",
|
||||
"tone": "not tested",
|
||||
"toneHeard": false,
|
||||
"worklet": "not tested",
|
||||
"workletAttempts": [],
|
||||
"securityPolicyViolations": [],
|
||||
"storageFaultInjected": false,
|
||||
"errors": [],
|
||||
"restoredAtLoad": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Phase 0 initial capture after offline-test instructions
|
||||
|
||||
**Result:** Partial; offline acceptance remains open.
|
||||
|
||||
The [supplied capture](2026-09-04-user-offline-initial.json) starts at `2026-09-05T03:00:54.788Z` on the original direct-file probe version 3. Both exhibits and saved settings restored, including Blue activity 0.37 and master volume 0.19. There were zero new saves and no errors.
|
||||
|
||||
The capture was supplied after instructions to disable networking within the test browser, reload, select each exhibit, and test the tone and worklet. It contains no explicit confirmation of the developer-tool offline setting. `onlineFlag` remains true and `networkRequestsAuthored` is zero; these fields do not establish that networking was disabled. Audio, tone, and worklet are all marked not tested. Activation of each exhibit is not demonstrated by this capture.
|
||||
|
||||
Keep the offline gate unchecked pending confirmation of browser-scoped offline mode and the requested interaction results. No runtime defect is established by this initial capture. Earlier successful audio and worklet evidence remains valid for its original test conditions.
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"probeVersion": 3,
|
||||
"startedAt": "2026-09-05T02:43:23.096Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": {
|
||||
"width": 1920,
|
||||
"height": 959,
|
||||
"dpr": 1
|
||||
},
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": true,
|
||||
"saves": 0,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "not tested",
|
||||
"audio": "not tested",
|
||||
"tone": "not tested",
|
||||
"toneHeard": false,
|
||||
"worklet": "not tested",
|
||||
"workletAttempts": [],
|
||||
"securityPolicyViolations": [],
|
||||
"storageFaultInjected": false,
|
||||
"errors": [],
|
||||
"restoredAtLoad": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# Phase 0 user-performed restart and edited-settings restoration
|
||||
|
||||
**Result:** Passed as a user-performed manual restart test following the requested procedure\
|
||||
**Evidence:** [Supplied JSON](2026-09-04-user-restart.json)\
|
||||
**Page started:** `2026-09-05T02:43:23.096Z`\
|
||||
**State saved:** `2026-09-05T02:42:54.030Z`\
|
||||
**Probe:** Version 3, same artifact as the successful worklet test
|
||||
|
||||
## Procedure provenance
|
||||
|
||||
The user clarified that the earlier tests were reloads. The assistant then explicitly requested: set activity to 0.37 and volume to 0.19, wait for the save, fully exit Chrome, reopen the same file, and provide evidence before making any changes. The user supplied this record in response. The full-restart action is accepted from that manual-test context; the JSON itself and the agent do not independently attest to browser-process termination. Earlier reload-only records are not retroactively classified as restart tests.
|
||||
|
||||
## Observed result
|
||||
|
||||
Both exhibits, Blue Study selection, and saved settings were present immediately after loading the same `file:` URL. `restored` is true and `saves` is zero. `restoredAtLoad` and `current` agree:
|
||||
|
||||
| Setting | Saved/restored value |
|
||||
| --- | --- |
|
||||
| Amber Study activity | 0.25 |
|
||||
| Blue Study activity | 0.37 |
|
||||
| Master volume | 0.19 |
|
||||
| Selected exhibit | blue-study |
|
||||
|
||||
The Blue activity and master-volume values differ from their declared/initial defaults, demonstrating recovery of edited values rather than initialization from defaults. No error was reported. There was no need to re-import either fixture.
|
||||
|
||||
Audio and worklet are correctly marked `not tested` on this new page load. The earlier successful audio/worklet evidence remains valid; these per-load statuses are not regressions and do not require another audio test for this persistence check.
|
||||
|
||||
## Gate effect and remaining work
|
||||
|
||||
Mark the GC1 full-browser-reopen/cache-and-settings check passed as user-performed manual evidence. The underlying worklet-loading blocker is already resolved. GC1 still needs offline, storage-failure, rename/move, and remaining import/fallback/environment observations. Phase 0 shared contracts and semantic traces also remain unfinished, and Phase 1 has not begun.
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"probeVersion": 2,
|
||||
"startedAt": "2026-09-05T02:26:04.668Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": {
|
||||
"width": 1920,
|
||||
"height": 959,
|
||||
"dpr": 1
|
||||
},
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": true,
|
||||
"saves": 0,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "not tested",
|
||||
"audio": "unlocked through user interaction",
|
||||
"tone": "ended and nodes disconnected",
|
||||
"toneHeard": true,
|
||||
"worklet": "failed",
|
||||
"workletAttempts": [
|
||||
{
|
||||
"at": "2026-09-05T02:26:26.305Z",
|
||||
"stage": "addModule",
|
||||
"status": "failed",
|
||||
"contextState": "running",
|
||||
"moduleURL": "blob:null/afe55472-8afa-4bd8-a767-f68dce237b70",
|
||||
"error": {
|
||||
"name": "AbortError",
|
||||
"message": "Unable to load a worklet's module."
|
||||
},
|
||||
"finalContextState": "running"
|
||||
}
|
||||
],
|
||||
"securityPolicyViolations": [],
|
||||
"storageFaultInjected": false,
|
||||
"errors": [
|
||||
{
|
||||
"stage": "worklet/addModule",
|
||||
"name": "AbortError",
|
||||
"message": "Unable to load a worklet's module."
|
||||
}
|
||||
],
|
||||
"restoredAtLoad": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.75
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.25,
|
||||
"savedAt": "2026-09-05T02:14:24.015Z"
|
||||
},
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.75
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.25,
|
||||
"savedAt": "2026-09-05T02:14:24.015Z"
|
||||
},
|
||||
"sampleRate": 48000,
|
||||
"audioState": "running",
|
||||
"suspendObserved": "suspended",
|
||||
"resumeObserved": "running"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Phase 0 version 2 results and version 3 candidate fix
|
||||
|
||||
**Result:** Cache restored between loads; tone heard; suspend/resume observed; Blob worklet loading failed\
|
||||
**Source:** User-supplied [version 2 JSON](2026-09-04-user-run-v2.json)\
|
||||
**Run started:** `2026-09-05T02:26:04.668Z` (September 4 in America/Los_Angeles)\
|
||||
**Gate:** GC1 remains incomplete; no advancement to Phase 1
|
||||
|
||||
## Findings
|
||||
|
||||
| Check | Evidence | Conclusion |
|
||||
| --- | --- | --- |
|
||||
| Direct-file load | `protocol: "file:"`, secure context, Chrome 152 | Observed in the user's browser |
|
||||
| Restore on a later load | `restored: true`, `saves: 0`, populated `restoredAtLoad` | Both exhibits, Blue selection, and saved default parameter/volume values restored without a write in this run |
|
||||
| Link to previous saved state | `savedAt: 2026-09-05T02:14:24.015Z` | Matches the preceding user-run save timestamp |
|
||||
| Full browser restart | No independent confirmation of process exit | Still unverified; a later page load alone is insufficient |
|
||||
| Distinctive edited settings | Values remain at defaults | Edited-value reconciliation/restoration is not demonstrated by this run |
|
||||
| Native tone | `toneHeard: true`, tone ended and nodes disconnected | User confirms audible output |
|
||||
| Suspend/resume | `suspendObserved: "suspended"`, `resumeObserved: "running"` | AudioContext state changes observed |
|
||||
| Worklet | `addModule` rejected a `blob:null/...` URL while the context remained running | Failure isolated to module loading; node construction was not reached |
|
||||
| CSP | Empty `securityPolicyViolations` list | No page violation event captured; this does not prove absence of every browser restriction |
|
||||
| Offline/directory/fault injection | Online flag true; directory untested; injection false | These checks remain pending |
|
||||
|
||||
This run resolves the uncertainty about native audio output and demonstrates actual cache restoration between loads. It does not establish that all of GC1 passed.
|
||||
|
||||
## Candidate module-loading correction
|
||||
|
||||
The combination of a local `file:` page, an opaque `blob:null` URL, and an `addModule` failure is consistent with a local Blob-module loading limitation. This remains a diagnosis to verify, not a proven universal restriction in Chrome 152.
|
||||
|
||||
The [Recorder project's own implementation](https://raw.githubusercontent.com/xiangyuecn/Recorder/master/src/recorder-core.js) constructs a `data:text/javascript;base64,...` worklet module URL, with an explicit source comment explaining that some browsers reject local object URLs. This primary implementation informed the candidate fix; no Recorder dependency or code was imported into XZBT.
|
||||
|
||||
Probe version 3 encodes the same fixed ASCII processor source into a `data:` URL and passes it to the same `audioWorklet.addModule()` API. It replaces `blob:` with `data:` in the page's `script-src` allowlist. It does not disable CSP, add network sources, enable eval, change browser flags, or accept exhibit-authored executable modules. `connect-src 'none'` remains in effect. This changes application packaging, not the agent browser tool's URL policy; no blocked browser automation was attempted.
|
||||
|
||||
The probe's database and path remain unchanged. Attempt evidence now reports `moduleURLScheme: "data:"` and the fixed source size. Success would be reported as `loaded from engine-owned data URL and connected`; it must not be recorded until an actual user browser run reports it.
|
||||
|
||||
**Version 3 artifact SHA-256:** `97D6909953F1F22E5BF0CA8AD653605D57A078AB3CCDEDFA509C5E9B0C1A4B2A`
|
||||
|
||||
## Verification performed
|
||||
|
||||
Three Node control-flow regression tests passed using `node --test prototypes/phase0/audio-probe.test.mjs`. These use stubbed APIs. They cover rejection isolation, diagnostics, the embedded processor source's syntax, data URL construction, and avoiding duplicate registration after simulated success. They do not verify real browser module loading or CSP enforcement.
|
||||
|
||||
## Next evidence
|
||||
|
||||
Reopen the same [probe file](../../../prototypes/phase0/XZBT-phase0-probe.html), verify `probeVersion: 3`, enable audio, and test the worklet module. Share the evidence and any additional browser console error if it still fails. Confirm separately whether Chrome was fully exited between the earlier user runs. Preserve the remaining offline, edited-settings, directory/fallback, and failure-injection checks in the [manual procedure](../../../prototypes/phase0/README.md).
|
||||
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"probeVersion": 3,
|
||||
"startedAt": "2026-09-05T02:37:48.163Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": {
|
||||
"width": 1920,
|
||||
"height": 959,
|
||||
"dpr": 1
|
||||
},
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": true,
|
||||
"saves": 0,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "not tested",
|
||||
"audio": "unlocked through user interaction",
|
||||
"tone": "ended and nodes disconnected",
|
||||
"toneHeard": true,
|
||||
"worklet": "loaded from engine-owned data URL and connected",
|
||||
"workletAttempts": [
|
||||
{
|
||||
"at": "2026-09-05T02:38:39.380Z",
|
||||
"stage": "connect node",
|
||||
"status": "loaded and connected",
|
||||
"contextState": "running",
|
||||
"moduleURLScheme": "data:",
|
||||
"moduleSourceBytes": 226,
|
||||
"finalContextState": "running"
|
||||
}
|
||||
],
|
||||
"securityPolicyViolations": [],
|
||||
"storageFaultInjected": false,
|
||||
"errors": [],
|
||||
"restoredAtLoad": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.75
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.25,
|
||||
"savedAt": "2026-09-05T02:14:24.015Z"
|
||||
},
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.75
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.25,
|
||||
"savedAt": "2026-09-05T02:14:24.015Z"
|
||||
},
|
||||
"sampleRate": 48000,
|
||||
"audioState": "running"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# Phase 0 version 3 — worklet loading verified
|
||||
|
||||
**Result:** Engine-owned data-URL worklet loaded and connected in the user's direct-file Chrome session\
|
||||
**Source:** [User-supplied version 3 evidence](2026-09-04-user-run-v3.json)\
|
||||
**Page started:** `2026-09-05T02:37:48.163Z`\
|
||||
**Worklet attempt:** `2026-09-05T02:38:39.380Z`\
|
||||
**Artifact SHA-256:** `97D6909953F1F22E5BF0CA8AD653605D57A078AB3CCDEDFA509C5E9B0C1A4B2A`
|
||||
|
||||
Timestamps are UTC; both fall on September 4 in America/Los_Angeles. This record uses the user's final version 3 snapshot after the worklet button was pressed. The earlier snapshot from the same page load contained no worklet attempt and is not used as a worklet pass.
|
||||
|
||||
## Verified observations
|
||||
|
||||
- The page ran at the expected `file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html` URL in Chrome 152, with `secureContext: true`.
|
||||
- `addModule` and node construction completed. The attempt reached `connect node`, returned `loaded and connected`, and recorded `moduleURLScheme: "data:"` and `moduleSourceBytes: 226`.
|
||||
- AudioContext state was running before and after the attempt at 48000 Hz. The user marked the native test tone as heard; the tone's nodes were disconnected when it ended.
|
||||
- No errors or page security-policy violations were recorded in this run.
|
||||
- Both exhibits, Blue Study selection, parameters, and master volume restored on page load, with zero new saves. The saved timestamp matches the earlier version 1 run.
|
||||
|
||||
The version 3 change resolves the observed Blob-module loading blocker in this tested environment. Use an engine-owned embedded data URL for this direct-file packaging path. Do not infer universal browser support or a complete audio-engine acceptance result from this small probe.
|
||||
|
||||
The silent processor's render callbacks are not independently measured; this gate verifies loading, construction, and connection. Native tone output and worklet loading are separate observations. Full synthesis correctness, output protection, performance, and long-running behavior remain later tests.
|
||||
|
||||
## Gate accounting
|
||||
|
||||
GC1's engine-owned AudioWorklet loading check is passed using user-run browser evidence. The cache save check is supported by the earlier save transactions plus actual restoration in versions 2 and 3. Native audio unlock/tone and AudioContext suspend/resume are supported across the version 2 and version 3 records.
|
||||
|
||||
GC1 is not complete. Remaining evidence includes:
|
||||
|
||||
1. Confirmation of a full browser exit/restart, rather than only a new page load, and restoration of distinctive edited parameter/volume values.
|
||||
2. Offline operation in an isolated test browser; the latest evidence still reports the browser online.
|
||||
3. Rename/move storage behavior and the documented support boundary.
|
||||
4. Session playback with storage failure injected, including the user-facing persistence message.
|
||||
5. Browser profile/storage mode details and the remaining import/fallback checks. Directory APIs are optional enhancements; capability presence alone is not an import test.
|
||||
|
||||
The [manual procedure](../../../prototypes/phase0/README.md) covers these checks. No further worklet retest is needed unless its source, loader, or relevant policy changes. The agent's direct-file navigation restriction remains; no alternate browser automation was used.
|
||||
|
||||
Phase 0 still requires the remaining shared contracts and semantic traces after feasibility verification. Phase 1 has not started.
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"probeVersion": 1,
|
||||
"startedAt": "2026-09-05T02:13:55.036Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": { "width": 1920, "height": 959, "dpr": 1 },
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": false,
|
||||
"saves": 2,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "not tested",
|
||||
"audio": "unlocked through user interaction",
|
||||
"worklet": "not tested",
|
||||
"storageFaultInjected": false,
|
||||
"errors": [
|
||||
{ "stage": "audio/worklet", "name": "AbortError", "message": "Unable to load a worklet's module." },
|
||||
{ "stage": "audio/worklet", "name": "AbortError", "message": "Unable to load a worklet's module." },
|
||||
{ "stage": "audio/worklet", "name": "AbortError", "message": "Unable to load a worklet's module." }
|
||||
],
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": { "id": "amber-study", "name": "Amber Study" },
|
||||
"parameters": {
|
||||
"activity": { "type": "number", "default": 0.25, "min": 0, "max": 1 },
|
||||
"color": { "type": "color", "default": "#e8ad57" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": { "id": "blue-study", "name": "Blue Study" },
|
||||
"parameters": {
|
||||
"activity": { "type": "number", "default": 0.75, "min": 0, "max": 1 },
|
||||
"color": { "type": "color", "default": "#6baee8" }
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": { "amber-study": { "activity": 0.25 }, "blue-study": { "activity": 0.75 } },
|
||||
"masterVolume": 0.25,
|
||||
"savedAt": "2026-09-05T02:14:24.015Z"
|
||||
},
|
||||
"importedNames": ["Amber Study", "Blue Study"],
|
||||
"sampleRate": 48000,
|
||||
"audioState": "running"
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Phase 0 user-run evidence and probe correction
|
||||
|
||||
**Result:** Partial feasibility demonstrated; worklet load failed; GC1 remains incomplete\
|
||||
**Source:** JSON supplied by the user from probe version 1\
|
||||
**Run started:** `2026-09-05T02:13:55.036Z` (September 4 in America/Los_Angeles)\
|
||||
**Raw evidence:** [user-run JSON](2026-09-04-user-run.json)
|
||||
|
||||
The raw evidence is preserved with its original field values, including the misleading `worklet: "not tested"` status. No agent browser navigation or workaround was used to obtain this result.
|
||||
|
||||
## What this run establishes
|
||||
|
||||
| Observation | Assessment |
|
||||
| --- | --- |
|
||||
| `protocol: "file:"`, Chrome 152 user agent, `secureContext: true` | The user loaded the probe directly from disk in a secure context |
|
||||
| Both exhibits present; Blue Study selected | File import and selection produced the shown library state |
|
||||
| `indexedDB: "opened and read"`, `saves: 2` | The probe reports an opened database and two completed save transactions |
|
||||
| `audioState: "running"`, sample rate 48000 | The audio context was unlocked and running |
|
||||
| Three `AbortError` entries for module loading | Worklet loading failed; it was attempted despite the stale status label |
|
||||
| `restored: false` | This document load did not restore cached state; it does not establish a full-restart test or prove persistence failure |
|
||||
| Both activity values and master volume still at defaults | Distinctive edited preference restoration has not been demonstrated |
|
||||
| `onlineFlag: true`, authored-network count 0 | No offline test is established; the count is a source-level declaration, not a network trace |
|
||||
| Directory import not tested; storage injection false | These checks remain pending |
|
||||
|
||||
The v1 worklet attempt occurs before oscillator creation and before enabling suspend/resume. Each shown exception therefore prevented that invocation from scheduling its tone and enabling those controls. A running context is not evidence of audible output.
|
||||
|
||||
## Root cause status
|
||||
|
||||
The cause of module-load failure is not established. Chromium uses the reported AbortError wording for worklet module-load failures; it does not uniquely identify CSP, origin behavior, or a processor-code defect. See the [Chromium diagnostic change](https://chromium.googlesource.com/chromium/src/+/963f877152d08f60e3f8bd60f1958ddcebce3b0f).
|
||||
|
||||
The [Fetch Standard](https://fetch.spec.whatwg.org/#concept-request-destination) associates `audioworklet` requests with `script-src`. This probe already permits `blob:` in `script-src`. That is insufficient to diagnose this run, but does not justify assuming `connect-src 'none'` is the cause or weakening the policy without evidence. Console details and captured policy violations are the next diagnostic input.
|
||||
|
||||
## Probe version 2
|
||||
|
||||
The corrected [probe](../../../prototypes/phase0/XZBT-phase0-probe.html) separates native tone playback from worklet loading, enables suspend/resume after context unlock, and records explicit worklet attempt stages and failure state. It adds an explicit user-reported audible-tone checkbox and page CSP diagnostics. The database name, file path, loader strategy, and CSP remain unchanged.
|
||||
|
||||
**Version 2 artifact SHA-256:** `52DD9137A7584236D00A7BF5BBBDB8159B25D8F98605C88A849892835B8D0FE5`
|
||||
|
||||
Two stubbed Node control-flow regression tests passed using:
|
||||
|
||||
```text
|
||||
node --test prototypes/phase0/audio-probe.test.mjs
|
||||
```
|
||||
|
||||
They verify that worklet rejection cannot prevent tone scheduling or suspend/resume controls and that supplied CSP diagnostics are retained. The tests use fake APIs and do not verify browser loading, CSP enforcement, audio output, or persistence. Browser testing of version 2 is pending.
|
||||
|
||||
## Next manual check
|
||||
|
||||
Follow the [updated instructions](../../../prototypes/phase0/README.md). Test the native tone and worklet independently; include `workletAttempts`, `securityPolicyViolations`, and any additional console error in the next evidence. Test distinctive values and capture the next launch before importing again, confirming separately whether the browser was fully closed.
|
||||
|
||||
Phase 0 remains stopped at the unresolved worklet/loading and restart-persistence checks. No Phase 1 implementation has begun.
|
||||
@@ -0,0 +1,222 @@
|
||||
{
|
||||
"probeVersion": 3,
|
||||
"startedAt": "2026-09-05T02:48:47.748Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": {
|
||||
"width": 1920,
|
||||
"height": 959,
|
||||
"dpr": 1
|
||||
},
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": true,
|
||||
"saves": 0,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "completed",
|
||||
"audio": "unlocked through user interaction",
|
||||
"tone": "ended and nodes disconnected",
|
||||
"toneHeard": true,
|
||||
"worklet": "not tested",
|
||||
"workletAttempts": [],
|
||||
"securityPolicyViolations": [],
|
||||
"storageFaultInjected": true,
|
||||
"errors": [
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
}
|
||||
],
|
||||
"restoredAtLoad": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.5
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"importedNames": [
|
||||
"Blue Study",
|
||||
"Amber Study"
|
||||
],
|
||||
"sampleRate": 48000,
|
||||
"audioState": "running"
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"probeVersion": 3,
|
||||
"startedAt": "2026-09-05T02:48:47.748Z",
|
||||
"url": "file:///G:/.vibe/XZBT/prototypes/phase0/XZBT-phase0-probe.html",
|
||||
"protocol": "file:",
|
||||
"secureContext": true,
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
|
||||
"platform": "Win32",
|
||||
"viewport": {
|
||||
"width": 1920,
|
||||
"height": 959,
|
||||
"dpr": 1
|
||||
},
|
||||
"onlineFlag": true,
|
||||
"networkRequestsAuthored": 0,
|
||||
"indexedDB": "opened and read",
|
||||
"restored": true,
|
||||
"saves": 0,
|
||||
"directoryPickerAvailable": true,
|
||||
"directoryImport": "completed",
|
||||
"audio": "not tested",
|
||||
"tone": "not tested",
|
||||
"toneHeard": false,
|
||||
"worklet": "not tested",
|
||||
"workletAttempts": [],
|
||||
"securityPolicyViolations": [],
|
||||
"storageFaultInjected": true,
|
||||
"errors": [
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
},
|
||||
{
|
||||
"stage": "save",
|
||||
"name": "QuotaExceededError",
|
||||
"message": "Injected write failure."
|
||||
}
|
||||
],
|
||||
"restoredAtLoad": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.37
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"current": {
|
||||
"exhibits": [
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "amber-study",
|
||||
"name": "Amber Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.25,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#e8ad57"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": {
|
||||
"id": "blue-study",
|
||||
"name": "Blue Study"
|
||||
},
|
||||
"parameters": {
|
||||
"activity": {
|
||||
"type": "number",
|
||||
"default": 0.75,
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"color": {
|
||||
"type": "color",
|
||||
"default": "#6baee8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"selected": "blue-study",
|
||||
"parameters": {
|
||||
"amber-study": {
|
||||
"activity": 0.25
|
||||
},
|
||||
"blue-study": {
|
||||
"activity": 0.5
|
||||
}
|
||||
},
|
||||
"masterVolume": 0.19,
|
||||
"savedAt": "2026-09-05T02:42:54.030Z"
|
||||
},
|
||||
"importedNames": [
|
||||
"Blue Study",
|
||||
"Amber Study"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# Phase 0 injected storage failure and directory import
|
||||
|
||||
**Result:** Passed for injected write failure: session settings, import, playback, and the visible session-only warning.\
|
||||
**Evidence:** [User-supplied JSON](2026-09-04-user-storage-failure.json)\
|
||||
**Page started:** `2026-09-05T02:48:47.748Z`\
|
||||
**Environment:** Probe 3, direct-file Chrome 152 on Win32, same original path and 1920 × 959 viewport as preceding runs.
|
||||
|
||||
The user supplied this capture after instructions to enable storage failure, change activity, and import the fixtures again. It records 16 save errors, all `QuotaExceededError` with message `Injected write failure.` These are expected application fault-injection results, not a new worklet or browser failure.
|
||||
|
||||
Blue Study activity changed from the restored 0.37 to 0.5 in memory. Both fixtures remain present, Blue remains selected, and master volume remains 0.19. Successful saves stayed at zero and the saved timestamp stayed at `2026-09-05T02:42:54.030Z`. Directory import completed and the imported names include both fixtures. This supports continued settings and import operation despite failed writes.
|
||||
|
||||
The initial JSON does not capture the visible session-only warning or demonstrate playback during the failure. Source inspection shows the warning is assigned when injection is checked, but this is not a user observation. This injection does not establish behavior under real browser permission denial or quota exhaustion.
|
||||
|
||||
## Follow-up audio observation
|
||||
|
||||
The [subsequent capture from the same page session](2026-09-04-user-storage-failure-audio.json) records injection still enabled, audio unlocked through user interaction, `toneHeard: true`, tone completion and node disconnection, and an AudioContext running at 48,000 Hz. The same 16 injected save errors remain, with no successful saves or additional audio errors. Native playback during injected storage failure therefore passes. Worklet testing was not repeated and is not required for this native-playback check. After being asked whether the “Session only: storage is unavailable” warning was visible, the user answered “yes” in chat. This explicit manual observation completes the injected-storage-failure check. The confirmation is recorded here separately from the unchanged raw JSON captures.
|
||||
|
||||
Directory import success is now observed. Remembered directory handles are not implemented by this disposable probe. Directory-denial/unavailability fallback and activation of each fixture remain unverified by this capture. Offline operation, renamed/moved-file storage boundaries, and browser profile/storage-mode details also remain outstanding. Phase 0 remains incomplete; Phase 1 has not started.
|
||||
@@ -0,0 +1,22 @@
|
||||
# Phase 0 direct-file feasibility probe
|
||||
|
||||
This disposable test application exercises browser capabilities needed by XZBT. It is not the XZBT runtime or a completed Phase 1 implementation. Its only database is `xzbt-phase0-probe-v1`; it neither reads `.env` nor makes network requests.
|
||||
|
||||
1. Open `XZBT-phase0-probe.html` directly in desktop Chromium.
|
||||
2. Import both files under `fixtures` using the file input.
|
||||
3. Select each exhibit. Set activity and master volume to identifiable values.
|
||||
4. Press **Enable audio and play test tone** and check **I heard the test tone** only if audible. Then press **Test worklet module** separately. The worklet is silent; its failure must not prevent the native tone or suspend/resume tests. Test **Suspend audio** and **Resume audio** as well.
|
||||
5. Close the browser fully and reopen the same file. Verify both exhibits, selection, and settings restore. A tab reload or tab reopen is not a full browser-restart test.
|
||||
6. Test renamed/moved copies separately and record the observed storage boundary.
|
||||
7. Enable **Inject storage failure**, then adjust settings and import files. The page must remain usable and explain that settings are session-only. This is application fault injection, not proof of real browser permission-denial behavior.
|
||||
8. Inspect the on-page evidence. Record browser/OS, results, and limitations under `docs/evidence/phase0`.
|
||||
|
||||
The probe deliberately uses visible controls for all writes and displays its results as text. No browser-origin/security overrides or autoplay-unlock bypasses are part of this test.
|
||||
|
||||
Probe version 3 keeps the same database and file path so previously saved fixtures remain available. It records explicit worklet failure stages and page Content Security Policy violations. Based on version 2's `blob:null` load failure, it loads the same fixed processor source through an embedded `data:` URL instead. The page's `script-src` allowlist replaces `blob:` with `data:` for that engine-owned module; `connect-src 'none'` continues to block network connections. No browser settings or security flags are changed, and exhibit data never supplies executable module source or URLs. The [version 3 user run](../../docs/evidence/phase0/2026-09-04-user-run-v3.md) verifies worklet loading and connection in direct-file Chrome; other feasibility checks remain pending.
|
||||
|
||||
If module loading fails, include `workletAttempts` and `securityPolicyViolations` from the evidence; if Chrome's developer console shows an additional error, include that message too. An empty violation list does not prove that every browser-level restriction has been ruled out.
|
||||
|
||||
For a recognizable persistence test, use activity `0.37` for Amber Study, `0.63` for Blue Study, master volume `0.19`, and leave Blue Study selected. Wait for **Probe state saved** before closing the browser. On reopening, capture the evidence before importing or changing anything. `restored: true` and `restoredAtLoad` then show what this load actually recovered; separately confirm that the browser was fully closed.
|
||||
|
||||
For the offline test, disable networking only within the isolated test browser using supported developer tooling; do not change the user's system network settings. Directory selection is optional and must be initiated through its button. The ordinary file input remains available.
|
||||
@@ -0,0 +1,323 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' data:; style-src 'unsafe-inline'; worker-src blob:; connect-src 'none'; img-src data:; base-uri 'none'; form-action 'none'">
|
||||
<title>XZBT — Phase 0 feasibility probe</title>
|
||||
<style>
|
||||
:root { color-scheme: dark; font: 16px/1.5 system-ui, sans-serif; background: #11151c; color: #e5e9ef; }
|
||||
body { max-width: 1000px; margin: 0 auto; padding: 32px; }
|
||||
h1 { font-size: 1.9rem; margin-bottom: 4px; } h2 { font-size: 1.15rem; }
|
||||
.muted { color: #aab6c7; } section { margin: 24px 0; padding: 20px; border: 1px solid #364154; border-radius: 8px; }
|
||||
label { display: block; margin: 12px 0; } button, select, input { font: inherit; }
|
||||
button, select { padding: 8px 12px; border: 1px solid #60738c; border-radius: 5px; background: #202c3c; color: inherit; margin: 4px 8px 4px 0; }
|
||||
button:disabled { opacity: .5; } :focus-visible { outline: 3px solid #ffcb7d; outline-offset: 3px; }
|
||||
input[type=range] { width: min(360px, 65%); vertical-align: middle; }
|
||||
canvas { width: 100%; height: 140px; background: #080c12; border-radius: 5px; }
|
||||
pre { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 12px; }
|
||||
#storage-status { color: #ffcb7d; } #message { min-height: 1.5em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>XZBT / Phase 0</h1>
|
||||
<p class="muted">Direct-file browser feasibility. This is a test probe, not the production runtime.</p>
|
||||
<p id="storage-status" role="status">Opening probe storage…</p>
|
||||
<section aria-labelledby="library-title">
|
||||
<h2 id="library-title">Import and persistence</h2>
|
||||
<label for="import-files">Import probe exhibits</label>
|
||||
<input id="import-files" type="file" accept=".xzbt,application/json" multiple>
|
||||
<button id="import-directory" type="button">Import directory</button>
|
||||
<label for="selection">Selected exhibit</label>
|
||||
<select id="selection"><option value="">No exhibits imported</option></select>
|
||||
<label for="activity">Activity <input id="activity" type="range" min="0" max="1" step="0.01" value="0.5" disabled> <output id="activity-value">0.50</output></label>
|
||||
<label for="volume">Master volume <input id="volume" type="range" min="0" max="1" step="0.01" value="0.25"> <output id="volume-value">0.25</output></label>
|
||||
<canvas id="preview" width="960" height="140" aria-label="Selected probe exhibit preview"></canvas>
|
||||
<label><input id="inject-failure" type="checkbox"> Inject storage failure</label>
|
||||
<p id="message" role="status" aria-live="polite"></p>
|
||||
</section>
|
||||
<section aria-labelledby="audio-title">
|
||||
<h2 id="audio-title">User-initiated audio</h2>
|
||||
<button id="enable-audio" type="button">Enable audio and play test tone</button>
|
||||
<button id="test-worklet" type="button" disabled>Test worklet module</button>
|
||||
<button id="suspend-audio" type="button" disabled>Suspend audio</button>
|
||||
<button id="resume-audio" type="button" disabled>Resume audio</button>
|
||||
<p class="muted">A short, quiet tone tests output. The worklet itself produces silence.</p>
|
||||
<label><input id="heard-tone" type="checkbox"> I heard the test tone</label>
|
||||
</section>
|
||||
<section aria-labelledby="evidence-title">
|
||||
<h2 id="evidence-title">Observed evidence</h2>
|
||||
<pre id="evidence">Initializing…</pre>
|
||||
</section>
|
||||
<script>
|
||||
'use strict';
|
||||
(() => {
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const databaseName = 'xzbt-phase0-probe-v1';
|
||||
const evidence = {
|
||||
probeVersion: 3,
|
||||
startedAt: new Date().toISOString(),
|
||||
url: location.href,
|
||||
protocol: location.protocol,
|
||||
secureContext: isSecureContext,
|
||||
userAgent: navigator.userAgent,
|
||||
platform: navigator.platform,
|
||||
viewport: { width: innerWidth, height: innerHeight, dpr: devicePixelRatio },
|
||||
onlineFlag: navigator.onLine,
|
||||
networkRequestsAuthored: 0,
|
||||
indexedDB: 'pending',
|
||||
restored: false,
|
||||
saves: 0,
|
||||
directoryPickerAvailable: typeof window.showDirectoryPicker === 'function',
|
||||
directoryImport: 'not tested',
|
||||
audio: 'not tested',
|
||||
tone: 'not tested',
|
||||
toneHeard: false,
|
||||
worklet: 'not tested',
|
||||
workletAttempts: [],
|
||||
securityPolicyViolations: [],
|
||||
storageFaultInjected: false,
|
||||
errors: []
|
||||
};
|
||||
let state = { exhibits: [], selected: '', parameters: {}, masterVolume: 0.25, savedAt: null };
|
||||
let db = null;
|
||||
let context = null;
|
||||
let worklet = null;
|
||||
let workletModuleLoaded = false;
|
||||
let workletTestRunning = false;
|
||||
let lastPersistError = '';
|
||||
let writeQueue = Promise.resolve();
|
||||
const report = () => {
|
||||
evidence.current = structuredClone(state);
|
||||
if (context) evidence.audioState = context.state;
|
||||
$('evidence').textContent = JSON.stringify(evidence, null, 2);
|
||||
};
|
||||
const storageStatus = () => {
|
||||
$('storage-status').textContent = !db || $('inject-failure').checked || lastPersistError
|
||||
? 'Session only: storage is unavailable. Import exhibits again on a later launch.'
|
||||
: 'Probe cache available. Close the browser fully and reopen this exact file to test restoration.';
|
||||
};
|
||||
const showError = (stage, error) => {
|
||||
const detail = { stage, name: error.name || 'Error', message: error.message || String(error) };
|
||||
evidence.errors.push(detail);
|
||||
$('message').textContent = `${stage}: ${detail.message}`;
|
||||
report();
|
||||
};
|
||||
document.addEventListener('securitypolicyviolation', (event) => {
|
||||
evidence.securityPolicyViolations.push({
|
||||
at: new Date().toISOString(),
|
||||
effectiveDirective: event.effectiveDirective,
|
||||
violatedDirective: event.violatedDirective,
|
||||
blockedURI: event.blockedURI,
|
||||
sourceFile: event.sourceFile,
|
||||
lineNumber: event.lineNumber,
|
||||
disposition: event.disposition
|
||||
});
|
||||
report();
|
||||
});
|
||||
const transaction = (mode, operation) => new Promise((resolve, reject) => {
|
||||
if (!db) return reject(new Error('Probe database is unavailable.'));
|
||||
const tx = db.transaction('probe', mode);
|
||||
const request = operation(tx.objectStore('probe'));
|
||||
let result;
|
||||
request.onsuccess = () => { result = request.result; };
|
||||
tx.oncomplete = () => resolve(result);
|
||||
tx.onerror = () => reject(tx.error || new Error('Probe transaction failed.'));
|
||||
tx.onabort = () => reject(tx.error || new Error('Probe transaction aborted.'));
|
||||
});
|
||||
const save = () => {
|
||||
const snapshot = structuredClone({ ...state, savedAt: new Date().toISOString() });
|
||||
writeQueue = writeQueue.then(async () => {
|
||||
try {
|
||||
if ($('inject-failure').checked) throw new DOMException('Injected write failure.', 'QuotaExceededError');
|
||||
await transaction('readwrite', (store) => store.put(snapshot, 'session'));
|
||||
state.savedAt = snapshot.savedAt;
|
||||
evidence.saves++;
|
||||
lastPersistError = '';
|
||||
$('message').textContent = 'Probe state saved.';
|
||||
} catch (error) {
|
||||
lastPersistError = error.message;
|
||||
showError('save', error);
|
||||
}
|
||||
storageStatus();
|
||||
report();
|
||||
});
|
||||
return writeQueue;
|
||||
};
|
||||
const render = () => {
|
||||
const current = state.exhibits.find((exhibit) => exhibit.meta.id === state.selected);
|
||||
$('selection').replaceChildren();
|
||||
if (!state.exhibits.length) $('selection').append(new Option('No exhibits imported', ''));
|
||||
for (const exhibit of state.exhibits) $('selection').append(new Option(exhibit.meta.name, exhibit.meta.id));
|
||||
$('selection').value = state.selected;
|
||||
$('activity').disabled = !current;
|
||||
const activity = current ? state.parameters[state.selected].activity : 0.5;
|
||||
$('activity').value = String(activity);
|
||||
$('activity-value').textContent = activity.toFixed(2);
|
||||
$('volume').value = String(state.masterVolume);
|
||||
$('volume-value').textContent = state.masterVolume.toFixed(2);
|
||||
const canvas = $('preview');
|
||||
const paint = canvas.getContext('2d');
|
||||
paint.clearRect(0, 0, canvas.width, canvas.height);
|
||||
if (current) {
|
||||
paint.fillStyle = current.parameters?.color?.default || '#a5b8cd';
|
||||
paint.beginPath();
|
||||
paint.arc(80 + activity * 780, 70, 16 + activity * 30, 0, Math.PI * 2);
|
||||
paint.fill();
|
||||
}
|
||||
report();
|
||||
};
|
||||
const importFiles = async (files) => {
|
||||
try {
|
||||
const candidates = [];
|
||||
for (const file of files) {
|
||||
if (!file.name.toLowerCase().endsWith('.xzbt')) continue;
|
||||
if (file.size > 1024 * 1024) throw new Error('Probe imports are limited to 1 MiB per file.');
|
||||
const exhibit = JSON.parse(await file.text());
|
||||
if (exhibit.xzbt !== '0.1' || !/^[a-z][a-z0-9_-]*$/.test(exhibit.meta?.id || '') || typeof exhibit.meta?.name !== 'string') {
|
||||
throw new Error(`Invalid probe exhibit: ${file.name}`);
|
||||
}
|
||||
const activity = exhibit.parameters?.activity?.default;
|
||||
if (typeof activity !== 'number' || !Number.isFinite(activity) || activity < 0 || activity > 1) {
|
||||
throw new Error(`Probe activity must be a finite number from 0 to 1: ${file.name}`);
|
||||
}
|
||||
candidates.push(exhibit);
|
||||
}
|
||||
for (const exhibit of candidates) {
|
||||
const previous = state.exhibits.find((entry) => entry.meta.id === exhibit.meta.id);
|
||||
if (previous && JSON.stringify(previous) !== JSON.stringify(exhibit)) throw new Error('Changed duplicate IDs are outside this probe; use the original fixtures.');
|
||||
}
|
||||
for (const exhibit of candidates) {
|
||||
if (!state.exhibits.some((entry) => entry.meta.id === exhibit.meta.id)) state.exhibits.push(exhibit);
|
||||
state.parameters[exhibit.meta.id] ??= { activity: exhibit.parameters.activity.default };
|
||||
}
|
||||
state.selected ||= state.exhibits[0]?.meta.id || '';
|
||||
evidence.importedNames = candidates.map((entry) => entry.meta.name);
|
||||
render();
|
||||
await save();
|
||||
} catch (error) { showError('import', error); }
|
||||
$('import-files').value = '';
|
||||
};
|
||||
$('import-files').addEventListener('change', (event) => { void importFiles(Array.from(event.target.files)); });
|
||||
$('selection').addEventListener('change', () => { state.selected = $('selection').value; render(); void save(); });
|
||||
$('activity').addEventListener('input', () => { state.parameters[state.selected].activity = Number($('activity').value); render(); void save(); });
|
||||
$('volume').addEventListener('input', () => { state.masterVolume = Number($('volume').value); render(); void save(); });
|
||||
$('inject-failure').addEventListener('change', () => { evidence.storageFaultInjected = $('inject-failure').checked; storageStatus(); report(); });
|
||||
$('import-directory').disabled = !evidence.directoryPickerAvailable;
|
||||
$('import-directory').addEventListener('click', async () => {
|
||||
try {
|
||||
const directory = await window.showDirectoryPicker({ mode: 'read' });
|
||||
const files = [];
|
||||
for await (const entry of directory.values()) if (entry.kind === 'file' && entry.name.toLowerCase().endsWith('.xzbt')) files.push(await entry.getFile());
|
||||
await importFiles(files);
|
||||
evidence.directoryImport = 'completed';
|
||||
} catch (error) {
|
||||
evidence.directoryImport = error.name === 'AbortError' ? 'cancelled' : 'failed';
|
||||
showError('directory import', error);
|
||||
}
|
||||
report();
|
||||
});
|
||||
$('enable-audio').addEventListener('click', async () => {
|
||||
$('enable-audio').disabled = true;
|
||||
try {
|
||||
context ??= new AudioContext();
|
||||
await context.resume();
|
||||
evidence.audio = 'unlocked through user interaction';
|
||||
evidence.sampleRate = context.sampleRate;
|
||||
$('suspend-audio').disabled = false;
|
||||
$('resume-audio').disabled = false;
|
||||
$('test-worklet').disabled = false;
|
||||
$('heard-tone').checked = false;
|
||||
evidence.toneHeard = false;
|
||||
const oscillator = context.createOscillator();
|
||||
const gain = context.createGain();
|
||||
const now = context.currentTime;
|
||||
oscillator.frequency.value = 440;
|
||||
gain.gain.setValueAtTime(0, now);
|
||||
gain.gain.linearRampToValueAtTime(0.025 * state.masterVolume, now + 0.02);
|
||||
gain.gain.linearRampToValueAtTime(0, now + 0.2);
|
||||
oscillator.connect(gain).connect(context.destination);
|
||||
oscillator.onended = () => { oscillator.disconnect(); gain.disconnect(); evidence.tone = 'ended and nodes disconnected'; report(); };
|
||||
oscillator.start(now);
|
||||
oscillator.stop(now + 0.21);
|
||||
evidence.tone = 'scheduled through native oscillator and gain nodes';
|
||||
} catch (error) { showError('audio/tone', error); }
|
||||
finally { $('enable-audio').disabled = false; }
|
||||
report();
|
||||
});
|
||||
$('heard-tone').addEventListener('change', () => { evidence.toneHeard = $('heard-tone').checked; report(); });
|
||||
$('test-worklet').addEventListener('click', async () => {
|
||||
if (workletTestRunning) return;
|
||||
workletTestRunning = true;
|
||||
$('test-worklet').disabled = true;
|
||||
const attempt = { at: new Date().toISOString(), stage: 'availability', status: 'running', contextState: context?.state };
|
||||
evidence.workletAttempts.push(attempt);
|
||||
let candidate = null;
|
||||
try {
|
||||
if (!context || !context.audioWorklet) throw new Error('AudioWorklet is unavailable on this audio context.');
|
||||
if (worklet) {
|
||||
attempt.status = 'already connected';
|
||||
} else {
|
||||
if (!workletModuleLoaded) {
|
||||
const source = 'class ProbeSilence extends AudioWorkletProcessor { process(inputs, outputs) { for (const output of outputs) for (const channel of output) channel.fill(0); return true; } } registerProcessor("xzbt-probe-silence", ProbeSilence);';
|
||||
// Fixed ASCII engine source, never exhibit data or an imported URL.
|
||||
// Some local-file contexts reject blob:null worklet module URLs.
|
||||
const url = 'data:text/javascript;base64,' + btoa(source);
|
||||
attempt.moduleURLScheme = 'data:';
|
||||
attempt.moduleSourceBytes = source.length;
|
||||
attempt.stage = 'addModule';
|
||||
await context.audioWorklet.addModule(url);
|
||||
workletModuleLoaded = true;
|
||||
}
|
||||
attempt.stage = 'construct node';
|
||||
candidate = new AudioWorkletNode(context, 'xzbt-probe-silence');
|
||||
candidate.onprocessorerror = () => {
|
||||
evidence.worklet = 'processor failed';
|
||||
showError('worklet/process', new Error('The worklet processor emitted processorerror.'));
|
||||
};
|
||||
attempt.stage = 'connect node';
|
||||
candidate.connect(context.destination);
|
||||
worklet = candidate;
|
||||
attempt.status = 'loaded and connected';
|
||||
evidence.worklet = 'loaded from engine-owned data URL and connected';
|
||||
}
|
||||
} catch (error) {
|
||||
candidate?.disconnect();
|
||||
attempt.status = 'failed';
|
||||
attempt.error = { name: error.name || 'Error', message: error.message || String(error) };
|
||||
evidence.worklet = 'failed';
|
||||
showError(`worklet/${attempt.stage}`, error);
|
||||
} finally {
|
||||
attempt.finalContextState = context?.state;
|
||||
workletTestRunning = false;
|
||||
$('test-worklet').disabled = false;
|
||||
}
|
||||
report();
|
||||
});
|
||||
$('suspend-audio').addEventListener('click', async () => { try { await context.suspend(); evidence.suspendObserved = context.state; } catch (error) { showError('audio suspend', error); } report(); });
|
||||
$('resume-audio').addEventListener('click', async () => { try { await context.resume(); evidence.resumeObserved = context.state; } catch (error) { showError('audio resume', error); } report(); });
|
||||
window.addEventListener('pagehide', () => { worklet?.disconnect(); if (context) void context.close(); db?.close(); });
|
||||
const initialize = async () => {
|
||||
$('import-files').disabled = true;
|
||||
try {
|
||||
db = await new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(databaseName, 1);
|
||||
request.onupgradeneeded = () => request.result.createObjectStore('probe');
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onblocked = () => reject(new Error('Probe storage is blocked by another connection.'));
|
||||
});
|
||||
db.onversionchange = () => { db.close(); db = null; storageStatus(); };
|
||||
const restored = await transaction('readonly', (store) => store.get('session'));
|
||||
if (restored) { state = restored; evidence.restored = true; evidence.restoredAtLoad = structuredClone(restored); }
|
||||
evidence.indexedDB = 'opened and read';
|
||||
} catch (error) { evidence.indexedDB = 'unavailable'; showError('storage initialization', error); }
|
||||
$('import-files').disabled = false;
|
||||
storageStatus();
|
||||
render();
|
||||
};
|
||||
void initialize();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,117 @@
|
||||
// Control-flow regression tests with stubbed APIs. These are NOT browser
|
||||
// feasibility tests and do not evaluate CSP, file origins, audio, or persistence.
|
||||
import { readFileSync } from 'node:fs';
|
||||
import vm from 'node:vm';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
const html = readFileSync(new URL('./XZBT-phase0-probe.html', import.meta.url), 'utf8');
|
||||
const script = html.match(/<script>([\s\S]*?)<\/script>/)[1];
|
||||
|
||||
function harness({ rejectModule = true } = {}) {
|
||||
const elements = new Map();
|
||||
const documentEvents = new Map();
|
||||
let tonesStarted = 0;
|
||||
let moduleAttempts = 0;
|
||||
const moduleUrls = [];
|
||||
const element = (id) => {
|
||||
if (!elements.has(id)) elements.set(id, {
|
||||
disabled: false, checked: false, textContent: '', handlers: new Map(),
|
||||
addEventListener(type, handler) { this.handlers.set(type, handler); }
|
||||
});
|
||||
return elements.get(id);
|
||||
};
|
||||
class StubAudioContext {
|
||||
state = 'suspended';
|
||||
currentTime = 0;
|
||||
sampleRate = 48000;
|
||||
destination = {};
|
||||
audioWorklet = { addModule: async (url) => {
|
||||
moduleAttempts++;
|
||||
moduleUrls.push(url);
|
||||
if (rejectModule) throw new DOMException("Unable to load a worklet's module.", 'AbortError');
|
||||
} };
|
||||
async resume() { this.state = 'running'; }
|
||||
async suspend() { this.state = 'suspended'; }
|
||||
createOscillator() {
|
||||
return { frequency: {}, connect: (target) => target, disconnect() {},
|
||||
start() { tonesStarted++; }, stop() {} };
|
||||
}
|
||||
createGain() {
|
||||
return { gain: { setValueAtTime() {}, linearRampToValueAtTime() {} },
|
||||
connect() {}, disconnect() {} };
|
||||
}
|
||||
}
|
||||
vm.runInNewContext(script, {
|
||||
document: { getElementById: element, addEventListener: (type, handler) => documentEvents.set(type, handler) },
|
||||
window: { addEventListener() {} },
|
||||
location: { href: 'stub-only', protocol: 'stub:' },
|
||||
navigator: { userAgent: 'unit-test stub', platform: 'stub', onLine: false },
|
||||
isSecureContext: true, innerWidth: 1920, innerHeight: 1080, devicePixelRatio: 1,
|
||||
// Never resolve initialization: storage is deliberately outside this test.
|
||||
indexedDB: { open: () => ({}) },
|
||||
AudioContext: StubAudioContext,
|
||||
AudioWorkletNode: class { connect() {} disconnect() {} },
|
||||
Blob, URL, DOMException, structuredClone, btoa
|
||||
});
|
||||
return {
|
||||
element,
|
||||
click: (id) => element(id).handlers.get('click')(),
|
||||
read: () => JSON.parse(element('evidence').textContent),
|
||||
tones: () => tonesStarted,
|
||||
attempts: () => moduleAttempts,
|
||||
moduleUrls,
|
||||
violation: (event) => documentEvents.get('securitypolicyviolation')(event)
|
||||
};
|
||||
}
|
||||
|
||||
test('stubbed worklet rejection cannot prevent native tone or suspend/resume controls', async () => {
|
||||
const probe = harness();
|
||||
await probe.click('enable-audio');
|
||||
assert.equal(probe.tones(), 1);
|
||||
assert.equal(probe.attempts(), 0, 'the native tone test must not invoke worklet loading');
|
||||
assert.equal(probe.element('suspend-audio').disabled, false);
|
||||
assert.equal(probe.element('resume-audio').disabled, false);
|
||||
await probe.click('test-worklet');
|
||||
const failed = probe.read();
|
||||
assert.equal(failed.worklet, 'failed');
|
||||
assert.equal(failed.workletAttempts[0].stage, 'addModule');
|
||||
assert.equal(failed.workletAttempts[0].error.name, 'AbortError');
|
||||
assert.equal(probe.element('test-worklet').disabled, false);
|
||||
await probe.click('suspend-audio');
|
||||
assert.equal(probe.read().suspendObserved, 'suspended');
|
||||
await probe.click('resume-audio');
|
||||
assert.equal(probe.read().resumeObserved, 'running');
|
||||
await probe.click('enable-audio');
|
||||
assert.equal(probe.tones(), 2);
|
||||
assert.equal(probe.attempts(), 1);
|
||||
assert.equal(probe.read().toneHeard, false, 'scheduling never implies a human heard the tone');
|
||||
});
|
||||
|
||||
test('a supplied CSP diagnostic is retained while network connections stay disallowed', async () => {
|
||||
const probe = harness();
|
||||
await probe.click('enable-audio');
|
||||
probe.violation({ effectiveDirective: 'script-src', violatedDirective: 'script-src',
|
||||
blockedURI: 'blob:stub', sourceFile: 'stub-only', lineNumber: 1, disposition: 'enforce' });
|
||||
assert.equal(probe.read().securityPolicyViolations[0].effectiveDirective, 'script-src');
|
||||
assert.equal(probe.read().securityPolicyViolations[0].blockedURI, 'blob:stub');
|
||||
assert.match(html, /connect-src 'none'/);
|
||||
});
|
||||
|
||||
test('the data URL contains only the fixed processor source and stubbed success is reported separately', async () => {
|
||||
const probe = harness({ rejectModule: false });
|
||||
await probe.click('enable-audio');
|
||||
await probe.click('test-worklet');
|
||||
assert.match(probe.moduleUrls[0], /^data:text\/javascript;base64,/);
|
||||
const processorSource = Buffer.from(probe.moduleUrls[0].split(',')[1], 'base64').toString('utf8');
|
||||
assert.match(processorSource, /^class ProbeSilence extends AudioWorkletProcessor/);
|
||||
assert.match(processorSource, /registerProcessor\("xzbt-probe-silence", ProbeSilence\);$/);
|
||||
new vm.Script(processorSource);
|
||||
assert.equal(probe.read().worklet, 'loaded from engine-owned data URL and connected');
|
||||
assert.equal(probe.read().workletAttempts[0].moduleURLScheme, 'data:');
|
||||
await probe.click('test-worklet');
|
||||
assert.equal(probe.attempts(), 1, 'an already loaded processor must not be registered again');
|
||||
assert.equal(probe.read().workletAttempts[1].status, 'already connected');
|
||||
assert.match(html, /script-src 'unsafe-inline' data:/);
|
||||
assert.doesNotMatch(html.match(/content="default-src[^"]+"/)[0], /https?:|unsafe-eval/);
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": { "id": "amber-study", "name": "Amber Study" },
|
||||
"parameters": {
|
||||
"activity": { "type": "number", "default": 0.25, "min": 0, "max": 1 },
|
||||
"color": { "type": "color", "default": "#e8ad57" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"xzbt": "0.1",
|
||||
"meta": { "id": "blue-study", "name": "Blue Study" },
|
||||
"parameters": {
|
||||
"activity": { "type": "number", "default": 0.75, "min": 0, "max": 1 },
|
||||
"color": { "type": "color", "default": "#6baee8" }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user