Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ artifact: context
Leif reported repeated WebGL context loss and ResizeObserver undelivered notifications after the viewer/site adoption. Direct resize callbacks mutate the observed layout; lost contexts are retried on every draw. Slice zoom currently allocates the complete enlarged grid (3840 squared in a modest viewport). This repair is confined to browser drawables and scheduling. Native camera mathematics, editing semantics, parser/storage and trust policies remain unchanged.

PR95 and site PR528 merged. Leif's continued Safari failure report keeps this approved repair active. The startup path failed to latch null/already-lost contexts before event delivery; resource creation also lacked null guards. The follow-up covers that same loss-suspension/restoration requirement, with actual Safari and deterministic startup/resource regressions. No new camera, loader, format or trust-policy scope is added.

PR96 and site PR529 merged. Leif reported failed main CI. Linux run 38090326009 missed a synthetic first touch edit and intermittently targeted the wrong cell in CRLF coverage. The approved deferred drawing/virtual-surface implementation needs matching gesture-test readiness: wait for rendered grid metadata and queued frames, then use the full scroll surface for cell coordinates. This follow-up changes test helpers only, with unchanged edit/Undo assertions and runtime bytes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ artifact: plan
4. Verify regressions and the repository gate, publish a feature PR, then import its tested commit in a site follow-up PR.

5. Repair the startup failure/event gap and partial-resource initialization under the existing loss-suspension contract; verify actual Safari and import the tested follow-up in the site.

6. Reproduce the main Linux Slice gesture failures, synchronize helpers with deferred drawing and the virtual grid, verify repeated and full Linux/macOS suites, and publish a feature CI repair without another site import.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"canonical_applied": true,
"base_commit": "fc4944c134a8f351f3073bf4febcdebb87df2f81",
"created_at": 1791664278,
"updated_at": 1791668070,
"updated_at": 1791674266,
"affected_specs": [
"ThreeMDViewer"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ artifact: tasks
- [x] Add and verify every text catalog example, local binary/composition/folder inputs and desktop/phone size-boundary regressions requested by Leif.

- [x] Verify the Safari startup/event gap, safe resource failure and actual native restoration.

- [x] Reproduce Linux main UI Slice timing failures and synchronize synthetic gesture coordinates with rendered grid metadata and the full scroll surface.
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,11 @@ The viewer suite now visits all 293 text examples across 48 axes through Cubes,
Safari follow-up: reproduce null/already-lost startup contexts before loss-event delivery, 256 redraws, safe null resource creation, and genuine early loss/restoration. Verify actual Safari alongside Playwright; record the independent native observations and the unresolved hardware cause honestly.

2026-10-10 Safari follow-up verification: complete Chromium/WebKit suite 256 passed without retries or skips (2 workers, 2.8 minutes), followed by the additional genuine early-loss test passing in both engines (2 checks). Native Safari 26.5.2 independently held at one context request while lost, retained Slice erase/Undo, and rebuilt exactly once after restoration. See docs/evidence/viewer-context/README.md for the proof boundaries.

## Linux CI gesture readiness

Main UI runs 38086816399 and 38090326009 exposed synthetic Slice gestures measuring stale/empty metadata or using clipped bitmap bounds before queued drawing. Wait for rendered ARIA grid dimensions and two animation frames, then measure cell centers on the full virtual scroll surface in one browser evaluation. Keep the existing exact source, CRLF/download, Undo/history, cancellation, touch navigation and phone assertions; do not add retries or relax expectations.

On Ubuntu Noble ARM64 with Playwright 1.61.1, the unmodified main helper failed 2 of 30 focused Chromium checks. The repaired helper passed 90 repeated checks with two workers and no retries. The full macOS Chromium/WebKit suite passed 258 checks in 2.6 minutes with two workers and no retries or skips. Full suite receipts are recorded in docs/evidence/viewer-context/README.md; exact-head hosted CI is reported on the follow-up PR. Local ARM64 Docker is not the hosted x86_64 runner.

Full Linux Chromium/WebKit verification passed 250 checks in 6.5 minutes with two workers and no retries; the eight existing CI image-snapshot skips remain unchanged. Playwright 1.61.1 ran in the official Ubuntu Noble ARM64 container with CI=1. The complete macOS suite passed all 258 checks in 2.6 minutes without retries or skips. All 293 catalog documents across 48 axes, local binary/composition/folder inputs, source/history and desktop/phone boundaries remain covered.
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,42 @@
"requirement_ids": [
"REQ-ThreeMDViewer-004"
]
},
{
"timestamp": 1791674248,
"commit": "bf94d2b72495ea8cd30ad4242641fb3e81605184",
"contract_digest": "5a8d804bd8dfc53eff71bfb3730f3f1d2d69f478fa86df116d24bb2ba58727d4",
"execution_digest": "a9d0e55787398e55789e1c53fd78961550829b38e5a77d178af3b25bc23b5b81",
"workspace_digest": "06c7afec6136b3c7293a454a7f565fc12ffb22f5edf7a57481d3b1518ce3806d",
"passed": true,
"commands": [
{
"command": "specsync check --spec ThreeMDViewer --strict",
"success": true,
"exit_code": 0
}
],
"requirement_ids": [
"REQ-ThreeMDViewer-004"
]
},
{
"timestamp": 1791674262,
"commit": "a8d44c98cbfb37b698dcfedd79f8e7ce4a8f1bbb",
"contract_digest": "5a8d804bd8dfc53eff71bfb3730f3f1d2d69f478fa86df116d24bb2ba58727d4",
"execution_digest": "a9d0e55787398e55789e1c53fd78961550829b38e5a77d178af3b25bc23b5b81",
"workspace_digest": "06c7afec6136b3c7293a454a7f565fc12ffb22f5edf7a57481d3b1518ce3806d",
"passed": true,
"commands": [
{
"command": "specsync check --spec ThreeMDViewer --strict",
"success": true,
"exit_code": 0
}
],
"requirement_ids": [
"REQ-ThreeMDViewer-004"
]
}
]
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"timestamp": 1791668066,
"commit": "7e71e559cd361690c90f2c7a524d51454faa2307",
"timestamp": 1791674262,
"commit": "a8d44c98cbfb37b698dcfedd79f8e7ce4a8f1bbb",
"contract_digest": "5a8d804bd8dfc53eff71bfb3730f3f1d2d69f478fa86df116d24bb2ba58727d4",
"execution_digest": "9878f72ac82aa4235a97ffd20a77e741a627082e032fd33d5d9167bb959452ca",
"workspace_digest": "139b08547f6a7c8bb8d38caa46a61fee966a48fad2ac98868c7f18faf3ba4ea9",
"execution_digest": "a9d0e55787398e55789e1c53fd78961550829b38e5a77d178af3b25bc23b5b81",
"workspace_digest": "06c7afec6136b3c7293a454a7f565fc12ffb22f5edf7a57481d3b1518ce3806d",
"passed": true,
"commands": [
{
Expand Down
14 changes: 14 additions & 0 deletions docs/evidence/viewer-context/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,17 @@ The follow-up latches failure/loss directly during initialization and drawing, r
- Null startup context and already-lost startup context each stay at one request through 256 redraws, Slice, phone resizing, 16x and Expand. All five null GPU-resource cases stop safely with Slice/Preview usable and source preserved.

Tested `web/viewer.html` SHA-256: `591ab297de99f35198abf1214490e6be8c211e553d192d315bc8ef0ec85a16fa`. Tested `uitests/viewer.spec.mjs` SHA-256: `ca0222441f219e230f8571d389c66430e35d33ce21c4b198812989332524dbff`. Native observations supplement the automated WebKit checks; they do not prove every Safari device or the deployed public revision. Current repository-gate/provenance outcomes are reported separately after the actual gates run. No human diff review, trusted signature or lifecycle closure is claimed for this follow-up.

## Main UI CI follow-up (2026-10-10)

After PR96 merged at ff547d72e4ca14ffa2f103f9f0c260efca70d8a2, [main UI run 38090326009](https://github.com/CorvidLabs/3md/actions/runs/38090326009) failed the touch-stroke/navigation assertion and retried a wrong-cell CRLF edit. [Previous main UI run 38086816399](https://github.com/CorvidLabs/3md/actions/runs/38086816399) also exposed empty metadata and missed first edits. Other current main workflows passed.

The synthetic gesture helpers measured before the deferred Slice redraw completed and treated the clipped canvas bitmap as the complete grid. The repair waits for rendered ARIA grid dimensions and queued drawing/layout frames, then measures cell centers on the full virtual scroll surface. Exact source, CRLF/download, Undo/history, cancellation, touch navigation and phone assertions remain unchanged. No retry, timeout, skip, runtime, camera, parser, format, dependency or trust-policy change is included. The site already imported the merged Safari repair; this test-only follow-up needs no site import.

- Unmodified main on Linux: 2 failures / 28 passes in 30 focused Chromium checks, two workers and no retries; missed touch edits reproduce the hosted failure.
- Repaired helpers on Linux: 90/90 repeated focused Chromium checks passed, two workers and no retries.
- Complete Linux suite: 250 passed, eight existing CI image-snapshot skips, two workers, no retries, 6.5 minutes. Official Playwright 1.61.1 Ubuntu Noble ARM64 container with CI=1; this is not the hosted x86_64 runner.
- Complete macOS suite: 258 passed, two workers, no retries or skips, 2.6 minutes.
- Both complete suites retain all 293 catalog documents across 48 axes, actual binary/composition/folder inputs, desktop/phone size limits, Slice edits/history, camera/picking and startup loss/restoration coverage. Exact-head hosted CI is reported on the follow-up PR after it runs.

Unchanged `web/viewer.html` SHA-256: `591ab297de99f35198abf1214490e6be8c211e553d192d315bc8ef0ec85a16fa`. Tested `uitests/viewer.spec.mjs` SHA-256: `c318d3f046da23036803c7543ac52e48057558cfeea58f38277f6e449ca40d64`. Repository-gate and actual unsigned Codex provenance outcomes are reported separately after those steps run. These receipts do not claim human diff review, trusted signatures, main CI completion for this unmerged branch, or lifecycle acceptance/archive.
4 changes: 4 additions & 0 deletions specs/ThreeMDViewer/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ The expanded viewer regression set covers all catalog documents and supported lo
## Safari startup follow-up

Leif reported 256 context-loss messages after PR95 and the site import merged. Actual Safari 26.5.2 reproduced the loss on the public site; a fresh copy subsequently rendered, so the hardware/origin cause is not proven. Initialization previously returned on a null or already-lost context without latching it, allowing hundreds of getContext attempts before a loss event. Startup and resource failures now stop further allocations/draws, and restoration clears the cached error and rebuilds once. Shader, color, camera, picking, format and editing behavior remain unchanged. Native Safari evidence complements Playwright WebKit; a green WebKit run alone did not establish Safari stability.

## Main UI CI follow-up

After PR96 merged, Linux main UI run 38090326009 exposed Slice gesture tests measuring before the deferred redraw completed. The helpers used the clipped bitmap bounds as the full grid and separately read dimensions that could still be empty or stale. The helpers now require rendered grid dimensions, wait through queued drawing/layout frames and target cells using the full virtual scroll surface. Exact source, CRLF, download and Undo assertions remain unchanged. The runtime is byte-identical to the merged Safari startup repair; this verification follow-up requires no site import.
2 changes: 2 additions & 0 deletions specs/ThreeMDViewer/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,5 @@ spec: ThreeMDViewer.spec.md
- [x] Add and verify every text catalog example, local binary/composition/folder inputs and desktop/phone size-boundary regressions requested by Leif.

- [x] Stop startup context retries before loss-event delivery, guard unavailable GPU resources, and verify genuine early loss/restoration in both automated engines and native Safari.

- [x] Repair Linux Slice gesture test readiness for deferred drawing and the virtual scroll surface, retaining exact edit/history assertions.
8 changes: 8 additions & 0 deletions specs/ThreeMDViewer/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,11 @@ The viewer suite now visits all 293 text examples across 48 axes through Cubes,
Simulate null and already-lost context creation without a delivered loss event, issue 256 redraws and change views/viewport: one getContext attempt and unchanged failed bitmap. Null shader/program/vertex-array/mesh/instance allocations must stop without invalid GPU calls and preserve Slice/Preview. A genuine WEBGL_lose_context startup loss suppresses the application's loss event, then restoration rebuilds once with source, pose and slice preserved. Inspect actual Safari startup, Slice editing/Undo during loss and restoration, alongside the complete cross-browser suite.

2026-10-10 Safari follow-up verification: complete Chromium/WebKit suite 256 passed without retries or skips (2 workers, 2.8 minutes), followed by the additional genuine early-loss test passing in both engines (2 checks). Native Safari 26.5.2 independently held at one context request while lost, retained Slice erase/Undo, and rebuilt exactly once after restoration. See docs/evidence/viewer-context/README.md for the proof boundaries.

## Linux CI gesture readiness

Main UI runs 38086816399 and 38090326009 exposed synthetic Slice gestures measuring stale/empty metadata or using clipped bitmap bounds before queued drawing. Wait for rendered ARIA grid dimensions and two animation frames, then measure cell centers on the full virtual scroll surface in one browser evaluation. Keep the existing exact source, CRLF/download, Undo/history, cancellation, touch navigation and phone assertions; do not add retries or relax expectations.

On Ubuntu Noble ARM64 with Playwright 1.61.1, the unmodified main helper failed 2 of 30 focused Chromium checks. The repaired helper passed 90 repeated checks with two workers and no retries. The full macOS Chromium/WebKit suite passed 258 checks in 2.6 minutes with two workers and no retries or skips. Full suite receipts are recorded in docs/evidence/viewer-context/README.md; exact-head hosted CI is reported on the follow-up PR. Local ARM64 Docker is not the hosted x86_64 runner.

Full Linux Chromium/WebKit verification passed 250 checks in 6.5 minutes with two workers and no retries; the eight existing CI image-snapshot skips remain unchanged. Playwright 1.61.1 ran in the official Ubuntu Noble ARM64 container with CI=1. The complete macOS suite passed all 258 checks in 2.6 minutes without retries or skips. All 293 catalog documents across 48 axes, local binary/composition/folder inputs, source/history and desktop/phone boundaries remain covered.
34 changes: 24 additions & 10 deletions uitests/viewer.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,14 +1284,28 @@ async function openSlice(page, source) {
await page.locator("#sliceTab").click();
await expect(page.locator("#sliceGrid")).toBeVisible();
}
async function sliceCellPoints(page, points) {
const grid = page.locator("#sliceGrid");
await expect(grid).toBeVisible();
await expect(grid).toHaveAttribute("aria-colcount", /^[1-9]\d*$/);
await expect(grid).toHaveAttribute("aria-rowcount", /^[1-9]\d*$/);
// Tab/viewport changes queue drawing, then layout may queue a second frame.
// Measure the rendered scroll surface after those frames, not the old bitmap.
await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
return grid.evaluate((canvas, points) => {
const surface = document.getElementById("sliceSurface").getBoundingClientRect();
const width = Number(canvas.getAttribute("aria-colcount"));
const height = Number(canvas.getAttribute("aria-rowcount"));
return points.map(([x, y]) => ({ clientX: surface.x + (x + 0.5) * surface.width / width,
clientY: surface.y + (y + 0.5) * surface.height / height }));
}, points);
}
async function sliceStroke(page, points, end = "pointerup") {
const rect = await page.locator("#sliceGrid").boundingBox();
const dimensions = await page.locator("#sliceDimensions").textContent();
const [w,h] = dimensions.match(/\d+/g).map(Number);
const at = ([x,y]) => ({pointerId:77,pointerType:"mouse",button:0,isPrimary:true,clientX:rect.x+(x+.5)*rect.width/w,clientY:rect.y+(y+.5)*rect.height/h});
await page.dispatchEvent("#sliceGrid","pointerdown",at(points[0]));
for (const point of points.slice(1)) await page.dispatchEvent("#sliceGrid","pointermove",at(point));
await page.dispatchEvent("#sliceGrid",end,at(points.at(-1)));
const locations = await sliceCellPoints(page, points);
const at = point => ({pointerId:77,pointerType:"mouse",button:0,isPrimary:true,...point});
await page.dispatchEvent("#sliceGrid","pointerdown",at(locations[0]));
for (const point of locations.slice(1)) await page.dispatchEvent("#sliceGrid","pointermove",at(point));
await page.dispatchEvent("#sliceGrid",end,at(locations.at(-1)));
}

test.describe("Sculpt Slice parity", () => {
Expand Down Expand Up @@ -1412,10 +1426,10 @@ test("Slice navigation finishes a touch stroke and ignores its late pointer move
await page.goto("/viewer.html");await viewerReady(page);
const source=sliceSource([["...","..."]]);await openCollection(page,{"one.3md":source,"two.3md":source});
await openedFile(page,"one.3md").click();await page.locator("#sliceTab").click();
const rect=await page.locator("#sliceGrid").boundingBox();
await page.dispatchEvent("#sliceGrid","pointerdown",{pointerId:83,pointerType:"touch",isPrimary:true,button:0,clientX:rect.x+rect.width/6,clientY:rect.y+rect.height/4});
const [first, late] = await sliceCellPoints(page, [[0,0], [1,0]]);
await page.dispatchEvent("#sliceGrid","pointerdown",{pointerId:83,pointerType:"touch",isPrimary:true,button:0,...first});
await openedFile(page,"two.3md").click();
await page.dispatchEvent("#sliceGrid","pointermove",{pointerId:83,pointerType:"touch",clientX:rect.x+rect.width/2,clientY:rect.y+rect.height/4});
await page.dispatchEvent("#sliceGrid","pointermove",{pointerId:83,pointerType:"touch",...late});
await page.dispatchEvent("#sliceGrid","pointerup",{pointerId:83,pointerType:"touch"});
expect(await page.evaluate(()=>window.threeMd.source)).toBe(source);
await openedFile(page,"one.3md").click();expect(await page.evaluate(()=>window.threeMd.source)).toBe(source.replace('...\n...','#..\n...'));
Expand Down
Loading