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
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-22
39 changes: 39 additions & 0 deletions openspec/archive/2026-05-demo-benchmark-orchestration/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Context

The current demo path spans `src/main.ts` and `src/benchmark/Benchmark.ts`, but the seam is shallow. `Benchmark` owns both pure math and stateful execution, while `main.ts` owns DOM lookup, status updates, progress, validation, and direct sorter construction.

This change deepens the orchestration path by making execution and presentation explicit modules with narrow interfaces.

## Goals / Non-Goals

**Goals:**

- Make benchmark execution unit-testable without DOM or real WebGPU.
- Make demo UI flow unit-testable without `document`.
- Keep `main.ts` as a thin bootstrap file.

**Non-Goals:**

- Redesign `GPUContext` again; that lands in `injectable-gpu-runtime`.
- Change benchmark outputs or visible UI behavior.
- Extract radix prefix-sum in this change.

## Decisions

### 1. Split pure benchmark helpers from execution

Keep `Benchmark` for `calculateSpeedup`, `calculateAverage`, and `formatResults`. Move `runSingle` and `runAll` into `BenchmarkRunner`.

### 2. Add a `DemoController` seam

Move run-button and run-all orchestration, progress updates, validation, and error handling into a controller with injected dependencies.

### 3. Isolate DOM access behind `DomView`

Create a concrete adapter for DOM reads/writes so tests can drive orchestration with fake views.

## Risks / Trade-offs

- **More files** -> Accept smaller modules to gain locality and test seams.
- **Public benchmark API churn** -> Preserve helper exports and document runner extraction in the change.
- **Controller/view split may feel verbose** -> The leverage is deterministic tests and a thinner bootstrap.
27 changes: 27 additions & 0 deletions openspec/archive/2026-05-demo-benchmark-orchestration/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Why

`main.ts` and `Benchmark` still braid DOM wiring, benchmark sequencing, sorter lifecycle, validation, and presentation into shallow modules. That makes the demo flow hard to unit-test and keeps UI/runtime knowledge spread across multiple call sites.

## What Changes

- Extract benchmark execution into a dedicated `BenchmarkRunner` seam with injected sorter factory, random data provider, and clock.
- Extract demo orchestration into a `DemoController` seam with a `DomView` adapter for DOM reads/writes.
- Shrink `main.ts` to bootstrap only and keep `Benchmark` focused on pure formatting/math helpers.
- Add unit tests for orchestration, progress/status flow, validation flow, and error handling without requiring real WebGPU.

## Capabilities

### New Capabilities

- `demo-orchestration`: Deep orchestration seam for demo and benchmark flows, separating controller logic from DOM adapters and benchmark execution.

### Modified Capabilities

- `sorting`: Benchmark execution moves behind an injected runner while preserving existing benchmark behavior.

## Impact

- **Affected code:** `src/main.ts`, `src/benchmark/Benchmark.ts`, new `src/benchmark/BenchmarkRunner.ts`, new `src/demo/` files, browser/unit tests
- **Affected APIs:** `Benchmark` likely loses execution methods; orchestration moves into new modules.
- **Dependencies:** No new packages.
- **Systems:** Demo UI, benchmark execution, validation flow, and public benchmark surface.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## ADDED Requirements

### Requirement: Demo orchestration uses injected seams

The demo benchmark flow SHALL execute through controller and view seams so orchestration can be tested without direct DOM or WebGPU dependencies.

#### Scenario: Controller drives benchmark execution

- **WHEN** a user starts a single benchmark or the full suite
- **THEN** the controller SHALL coordinate progress, status, validation, and result reporting through injected runner and view interfaces

#### Scenario: DOM access stays in the view adapter

- **WHEN** the demo needs to read controls or update status, progress, and results
- **THEN** those DOM operations SHALL be isolated behind a concrete view adapter

### Requirement: Benchmark execution is a separate runner

Benchmark execution SHALL live behind a runner seam distinct from pure formatting and math helpers.

#### Scenario: Runner executes benchmark cases

- **WHEN** benchmark execution is requested
- **THEN** the runner SHALL use injected sorter factories, random data providers, and clocks to produce benchmark results
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## MODIFIED Requirements

### Requirement: Performance Benchmarking

The benchmark system SHALL preserve its existing timing and reporting behavior while executing through a dedicated runner seam rather than the formatting helper module.

#### Scenario: Runner preserves benchmark behavior

- **WHEN** a caller runs a single benchmark or the default suite
- **THEN** the system SHALL still measure native and GPU paths, average timings, and report speedups as before

#### Scenario: Formatting stays available independently

- **WHEN** code needs to format benchmark results
- **THEN** it SHALL be able to do so without instantiating the execution runner
17 changes: 17 additions & 0 deletions openspec/archive/2026-05-demo-benchmark-orchestration/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## 1. Test-first orchestration seams

- [ ] 1.1 Add failing unit tests for `BenchmarkRunner` execution with injected sorter factory and random provider
- [ ] 1.2 Add failing unit tests for `DemoController` progress, validation, and error flow with a fake view

## 2. Implementation

- [ ] 2.1 Extract pure helpers into a slim `Benchmark` module
- [ ] 2.2 Add `BenchmarkRunner` and move benchmark execution there
- [ ] 2.3 Add `DemoController` and `DomView`, then shrink `main.ts` to bootstrap only

## 3. Validation

- [ ] 3.1 Run `npm run typecheck`
- [ ] 3.2 Run `npm run lint`
- [ ] 3.3 Run `npm run test`
- [ ] 3.4 Run `npm run build`
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-22
63 changes: 63 additions & 0 deletions openspec/archive/2026-05-injectable-gpu-runtime/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
## Context

`GPUContext` currently owns both the interface and the browser-specific implementation for support checks, adapter acquisition, device creation, limits mapping, and device-loss subscription. Tests can only reach most branches by stubbing browser globals, which keeps the interface shallow and the implementation hard to move.

The goal of this change is to deepen `GPUContext`, not replace it. Callers should still use one lifecycle module, but the browser runtime becomes an adapter behind a seam.

## Goals / Non-Goals

**Goals:**

- Keep `GPUContext` as the public lifecycle module.
- Introduce an injectable runtime interface for support checks and adapter acquisition.
- Preserve `new GPUContext()` as the default browser path.
- Make initialization, failure, loss, and recovery paths unit-testable without browser globals.

**Non-Goals:**

- Refactor demo/benchmark orchestration in this change.
- Add non-browser production adapters yet.
- Change sorting algorithms or shader ownership.

## Decisions

### 1. Add a `GPURuntime` seam under `src/core/runtime/`

Create an interface that owns:

- support detection
- adapter acquisition

Add `browserGPURuntime` as the default adapter that wraps `navigator.gpu`.

**Why:** Browser-global access is the shallow part. Moving it behind one seam gives `GPUContext` leverage and keeps future adapters possible.

**Alternative:** Keep static browser-global checks and only inject requestAdapter. Rejected because support detection and acquisition belong to the same runtime adapter.

### 2. Keep `GPUContext` as the public module

`GPUContext` constructor accepts an optional runtime adapter, defaulting to `browserGPURuntime`. `initialize()` and `recover()` continue to define lifecycle semantics and error shaping.

**Why:** Callers keep one familiar interface. The seam changes implementation ownership, not the high-level API shape.

**Alternative:** Replace `GPUContext` with factories or free functions. Rejected because it would increase churn before the orchestration seam lands.

### 3. Export runtime types, not extra orchestration

Export the runtime interface and browser adapter from `src/index.ts` so tests and advanced consumers can inject them directly.

**Why:** Constructor injection without exported types keeps the seam half-hidden.

**Alternative:** Keep runtime files internal. Rejected because public constructor injection needs a public type.

### 4. Rewrite `GPUContext` tests around fake runtimes

Use injected fake runtimes/adapters/devices for initialization branches, limits mapping, recovery, and device-loss callbacks. Keep only small browser-global tests for the default static support path.

**Why:** The interface is the test surface. The fake runtime gives locality for failure-path testing without browser globals.

## Risks / Trade-offs

- **Public API surface grows slightly** -> Limit it to runtime interface + default browser adapter.
- **Fake GPU objects can drift from WebGPU reality** -> Keep browser E2E smoke tests and only fake branches that do not need real hardware.
- **Future adapters may want more hooks** -> Start with the smallest seam: support detection + adapter acquisition.
27 changes: 27 additions & 0 deletions openspec/archive/2026-05-injectable-gpu-runtime/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Why

`GPUContext` still hard-codes `navigator.gpu`, adapter acquisition, and device-loss wiring inside one shallow module. That blocks deterministic unit tests, leaks browser globals into call sites, and makes future runtime adapters impossible without editing the implementation in place.

## What Changes

- Add an injectable GPU runtime seam so `GPUContext` acquires adapters and support checks through an adapter interface instead of browser globals.
- Keep `GPUContext` as the public lifecycle module, but move browser-global access into a dedicated browser adapter.
- Expand unit coverage for adapter absence, device request failure, limits mapping, device-loss callbacks, and recovery using fake runtimes.
- Export the runtime types needed for external injection while keeping the default browser path intact.

## Capabilities

### New Capabilities

- None.

### Modified Capabilities

- `infrastructure`: GPU context initialization must support injected runtime adapters while preserving the default browser runtime path.

## Impact

- **Affected code:** `src/core/GPUContext.ts`, new `src/core/runtime/` files, `src/index.ts`, `test/core/GPUContext.test.ts`
- **Affected APIs:** `GPUContext` constructor gains an optional runtime adapter; runtime interface/types become importable.
- **Dependencies:** No new packages.
- **Systems:** Core runtime initialization, lifecycle handling, and unit-test seams.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## MODIFIED Requirements

### Requirement: GPU Context Initialization

The GPUContext SHALL initialize through an injectable runtime adapter while preserving the default browser runtime path and existing lifecycle guarantees.

#### Scenario: Default browser runtime remains available

- **WHEN** a caller constructs `GPUContext` without providing a runtime adapter
- **THEN** the context SHALL use the browser runtime adapter for support detection and adapter acquisition

#### Scenario: Injected runtime drives initialization

- **WHEN** a caller constructs `GPUContext` with a runtime adapter
- **THEN** `initialize()` SHALL use that adapter instead of reading browser globals directly

#### Scenario: Injected runtime failure is surfaced as typed errors

- **WHEN** the injected runtime reports no adapter or device creation fails
- **THEN** GPUContext SHALL preserve the existing typed error semantics for adapter and device failures

#### Scenario: Injected runtime remains compatible with recovery and loss callbacks

- **WHEN** device loss occurs and `recover()` is called on a context using an injected runtime
- **THEN** the context SHALL reset state, reacquire a device through the same runtime adapter, and continue notifying registered callbacks
18 changes: 18 additions & 0 deletions openspec/archive/2026-05-injectable-gpu-runtime/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## 1. OpenSpec and test setup

- [x] 1.1 Finalize infrastructure delta spec for injected GPU runtime behavior
- [x] 1.2 Add failing `GPUContext` tests for injected runtime initialization, failure paths, limits mapping, and recovery/loss callbacks

## 2. Runtime seam implementation

- [x] 2.1 Add `src/core/runtime/` runtime interface and default browser adapter
- [x] 2.2 Refactor `GPUContext` to use injected runtime adapters while preserving `new GPUContext()` as the default path
- [x] 2.3 Export runtime seam types from `src/index.ts`

## 3. Validation

- [x] 3.1 Update `GPUContext` tests to remove unnecessary browser-global stubbing from non-browser paths
- [x] 3.2 Run `npm run typecheck`
- [x] 3.3 Run `npm run lint`
- [x] 3.4 Run `npm run test`
- [x] 3.5 Run `npm run build`
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-22
39 changes: 39 additions & 0 deletions openspec/archive/2026-05-scan-module-extraction/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Context

The repository already moved radix prefix-sum onto the GPU, but the implementation stayed embedded in `RadixSorter`. That gives callers no scan seam, couples shader ownership to radix sorting, and leaves scan behavior hard to test independently.

This change extracts the scan path into a deep module that RadixSorter can consume.

## Goals / Non-Goals

**Goals:**

- Give GPU prefix-sum its own module, shader ownership, and tests.
- Reduce `RadixSorter` to histogram/scatter orchestration plus scan consumption.
- Align docs/specs with actual GPU prefix-sum behavior.

**Non-Goals:**

- Change radix sort user-facing behavior.
- Refactor demo/benchmark orchestration here.
- Build a general-purpose GPU algorithm framework.

## Decisions

### 1. Add `src/sorting/scan/`

Create a scan module with its own interface, initialization, dispatch, and teardown.

### 2. Split shader ownership

Move Blelloch scan kernels out of `radix.wgsl` into scan-owned WGSL so shader locality matches module ownership.

### 3. Keep RadixSorter as a consumer

RadixSorter will request exclusive prefix sums through a narrow interface and keep radix-specific buffers/passes local.

## Risks / Trade-offs

- **Shader split churn** -> Accept one-time movement for better locality and reuse.
- **Extra module lifecycle** -> Keep scan interface narrow and explicit.
- **Spec/doc drift risk** -> Update stable sorting specs as part of the change.
27 changes: 27 additions & 0 deletions openspec/archive/2026-05-scan-module-extraction/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Why

`RadixSorter` still owns histogram, scatter, and GPU prefix-sum orchestration inside one shallow module. The scan pipeline has real leverage on its own, but today its interface is trapped inside `RadixSorter` and its shader kernels are mixed into `radix.wgsl`.

## What Changes

- Extract GPU prefix-sum into a dedicated scan module with its own initialization, dispatch, and cleanup.
- Split scan WGSL kernels from radix-only kernels.
- Update RadixSorter to depend on the scan seam instead of owning scan pipeline details directly.
- Add dedicated scan tests and align specs/docs with GPU-based prefix-sum ownership.

## Capabilities

### New Capabilities

- `gpu-prefix-sum-module`: Dedicated scan module for GPU exclusive prefix sums with reusable runtime ownership.

### Modified Capabilities

- `sorting`: Radix sort shall consume the standalone scan module instead of embedding scan orchestration directly.

## Impact

- **Affected code:** `src/sorting/RadixSorter.ts`, new `src/sorting/scan/` files, `src/shaders/radix.wgsl`, new scan shader file(s), related tests/docs/specs
- **Affected APIs:** New scan module surface; RadixSorter internal orchestration changes.
- **Dependencies:** Likely overlaps conceptually with existing `gpu-prefix-sum` implementation history but should land as a new architectural extraction change.
- **Systems:** Radix sorting internals, shader ownership, and scan-specific tests.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## ADDED Requirements

### Requirement: GPU prefix-sum is a standalone module

The system SHALL provide GPU exclusive prefix-sum through a dedicated scan module with its own initialization, dispatch, and cleanup interface.

#### Scenario: Scan module computes exclusive prefix sums

- **WHEN** a caller provides input, output, and scan configuration buffers
- **THEN** the scan module SHALL execute the GPU prefix-sum passes without requiring RadixSorter-specific knowledge

#### Scenario: Scan module owns scan pipeline resources

- **WHEN** the scan module is initialized or destroyed
- **THEN** it SHALL create and clean up scan-specific pipeline and shader resources independently
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## MODIFIED Requirements

### Requirement: Radix Sort Implementation

The RadixSorter SHALL perform histogram and scatter passes while delegating GPU prefix-sum work to a dedicated scan module.

#### Scenario: RadixSorter consumes the scan seam

- **WHEN** a radix pass needs prefix sums
- **THEN** RadixSorter SHALL invoke the standalone scan module instead of owning scan dispatch logic directly

#### Scenario: Sorting behavior remains unchanged

- **WHEN** valid input arrays are sorted through RadixSorter
- **THEN** the sorter SHALL continue producing correctly sorted ascending output
18 changes: 18 additions & 0 deletions openspec/archive/2026-05-scan-module-extraction/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## 1. Test-first scan seam

- [x] 1.1 Add failing tests for scan-module interface and scan-specific runtime behavior
- [x] 1.2 Add failing integration coverage for RadixSorter consuming the scan seam

## 2. Implementation

- [x] 2.1 Add scan module files under `src/sorting/scan/`
- [x] 2.2 Split scan WGSL kernels from `radix.wgsl`
- [x] 2.3 Refactor `RadixSorter` to consume the scan module
- [x] 2.4 Update docs/specs for GPU prefix-sum ownership

## 3. Validation

- [x] 3.1 Run `npm run typecheck`
- [x] 3.2 Run `npm run lint`
- [x] 3.3 Run `npm run test`
- [x] 3.4 Run `npm run build`
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-21
Loading
Loading