# Pulp support matrix
# Vocabulary: stable, usable, experimental, partial, planned, unsupported
schema_version: 2

platforms:
  macos:
    status: usable
    notes: Primary development platform. ARM64 (Apple Silicon) is the usable, primary
      target. x86_64 (Intel) now ships in EVERY release — the darwin-x64 CLI+SDK pair
      is a required leg, cross-compiled on the Apple-Silicon runner via
      -DCMAKE_OSX_ARCHITECTURES=x86_64 + -DPULP_RUST_CLI_TARGET=x86_64-apple-darwin and
      smoke-tested under Rosetta (release-cli.yml, os macos-15-xcompile). It remains
      experimental in maturity, not availability — GitHub CI VMs have no representative
      Intel GPU and Rosetta caps SIMD at SSE4.2/AVX2, so Metal-on-Intel-GPU behavior,
      AVX3 dispatch, and Intel-native DAW hosting are not yet verified on real Intel
      hardware (nightly-intel.yml Job A carries the native-silicon signal). skia-builder publishes all three mac Skia
      slices (mac-arm64, mac-x86_64, mac-universal); wgpu-native ships no universal
      dylib, so a universal build lipo's the two thin slices and ad-hoc re-signs the fat
      dylib (tools/cmake/PulpWgpuUniversal.cmake). Minimum macOS 13.4 (Ventura),
      arch-independent. Skia/Dawn allow macOS 13.0 (Google's Chromium
      mac_deployment_target at the pinned milestone) on both arm64 and x86_64, but
      the macOS 15.4 SDK's libc++ gates the floating-point std::to_chars overloads
      reached via std::format in Pulp's logging at macOS 13.4, which sets the real
      cross-SDK floor. The per-dependency floor is
      tracked in tools/deps/min_os.json (macos-arm64 + macos-x64; Pulp targets the max
      of every linked dependency's minimum across all requested arches, libc++ included)
      and pinned into the build by tools/cmake/PulpMinOs.cmake.
  windows:
    status: experimental
    notes: WASAPI audio, Win32 MIDI, FFTW3/MKL FFT acceleration (dynamic-loaded), NSIS installer, Authenticode signing. CI enabled.
      Minimum Windows 10 (_WIN32_WINNT/WINVER = 0x0A00, subsystem 10.0), inherited from
      Google's Chromium default at the pinned Skia/Dawn milestone. Tracked in
      tools/deps/min_os.json (windows-x64) and pinned into the build by tools/cmake/PulpMinOs.cmake;
      the artifact-measured value and a min-OS VM runtime smoke are still pending a Windows host.
  linux:
    status: experimental
    notes: ALSA audio, ALSA MIDI (monotonic input timestamps), JACK (build fixed; smoke/hotplug pending), LV2, .deb/.tar.gz plugin packaging, standalone AppImage packaging. CI enabled.
  ios:
    status: experimental
    notes: Foundation in place. AVAudioSession, AUv3, UIKit views, Metal surface.
  web:
    status: experimental
    notes: >-
      WAMv2 builds a Pulp Processor to a browser AudioWorklet plugin via
      pulp_add_wam_plugin; WebCLAP builds one to a CLAP-in-WebAssembly module via
      pulp_add_wclap (wasi-sdk). Both are exercised in a headless-Chrome CI lane
      (.github/workflows/web-plugins.yml). Pulp's own UI also runs in the browser:
      core/view widgets, flex layout, text shaping and the Ink & Signal theme
      compile to wasm and paint into a canvas through Skia Ganesh on WebGL2
      (core/view/platform/web/ + core/render/src/skia_surface_ganesh.cpp), driven by
      a requestAnimationFrame render loop, with DOM pointer/key events translated
      into the View tree. The UI module is DSP-free and mounts against BOTH ABIs
      through the web player's HostAdapter seam. DSP on the web is CPU-only TODAY:
      no GPU-compute code compiles to wasm yet. That is a gap, not a browser limit —
      the RENDER path uses WebGL2 (the Skia wasm slice is Ganesh), which has no
      compute shaders, but browser WebGPU does, and GPU DSP is reachable by running
      the compute in a Worker (WebGPU is unavailable in an AudioWorkletGlobalScope)
      and handing blocks to the audio callback over a SharedArrayBuffer ring. The
      audio callback itself stays on the CPU either way. File-backed loaders and
      native editors are compiled out (PULP_WASM / PULP_HEADLESS).

# macOS CPU-architecture tiers. macOS as a platform is `usable` (see
# platforms.macos), but that maturity is the arm64 (Apple Silicon) primary
# target. x86_64 and universal builds configure+build via
# -DCMAKE_OSX_ARCHITECTURES and are verified by the tiered Intel CI
# (docs/guides/intel-support.md), but ship experimental. Full rationale, the
# Tier 0-3 tiering, the honest canary catch/miss list, and the promotion
# criteria live in docs/guides/intel-support.md.
macos_architectures:
  arm64:
    status: usable
    notes: Apple Silicon — the primary, `usable` macOS target. Required `macos` CI gate.
  x86_64:
    status: experimental
    notes: >
      Intel builds configure+build via -DCMAKE_OSX_ARCHITECTURES=x86_64 and are
      exercised by the Intel CI tiering (Tier 0 canary per PR, Tier 1 advisory
      x86_64 build+test on path match, Tier 2 native macos-15-intel nightly,
      Tier 3 blocking universal+dual-arch-auval release gate — see
      docs/guides/intel-support.md). Ships experimental because two classes stay
      untestable in GitHub's macOS VMs: Metal on real AMD/Intel GPUs (the runners
      are VMs without a representative discrete GPU, so not even the native
      macos-15-intel runner exercises a real Intel/AMD Metal driver) and
      Intel-native DAW-in-the-loop hosting. Tier 4 (a dedicated Intel Mac /
      tartci Intel host) is CUT: macos-15-intel already covers the native Intel
      CPU + toolchain, leaving only those two GPU/DAW gaps. Highway runtime SIMD
      dispatch is arch-portable; there is no raw NEON in core/. The release
      pipeline publishes native thin x86_64 CLI + SDK artifacts
      (pulp-darwin-x64.tar.gz, pulp-sdk-darwin-x64.tar.gz), built and smoked
      natively on macos-15-intel; tools/install/install.sh auto-selects them on
      an Intel host, so Intel users get a one-line install rather than a source
      build. That leg is advisory (continue-on-error): a flaky macos-15-intel run
      does not block the arm64/linux/windows release, so most but not every
      release carries the Intel slice (installer falls back to source when absent).
  universal:
    status: experimental
    notes: >
      Universal (arm64;x86_64) builds fetch the fat mac Skia slice and lipo the
      two thin wgpu-native dylibs into a fat dylib that is ad-hoc re-signed
      (tools/cmake/PulpWgpuUniversal.cmake) — a raw lipo output fails
      codesign --verify and the arm64 slice is killed at load. The Tier 3
      release gate asserts lipo -archs + codesign --verify on every shipped
      bundle and embedded dylib before publish.

formats:
  vst3:
    macos: usable
    windows: partial
    linux: partial
    notes: >
      MIDI controllers (CC, mod wheel, sustain, channel aftertouch, pitch
      bend) reach MIDI-accepting plug-ins via IMidiMapping: the host queries
      getMidiControllerAssignment for a reserved hidden ParamID per
      (channel, controller) and the adapter decodes those parameter changes
      back into sample-accurate MIDI messages. Registered only when
      descriptor.accepts_midi=true.
  au_v2:
    macos: usable
    windows: unsupported
    linux: unsupported
    notes: >
      MIDI-accepting effects package as kAudioUnitType_MusicEffect (aumf)
      so AU hosts route inbound MIDI — set ACCEPTS_MIDI on pulp_add_plugin()
      when descriptor.accepts_midi=true. Audio-only effects stay on aufx.
      CATEGORY MidiEffect packages as kAudioUnitType_MIDIProcessor (aumi):
      MIDI in, MIDI out, no audio path, so the plugin appears in a host's
      MIDI-FX slot.
  clap:
    macos: usable
    windows: partial
    linux: partial
  standalone:
    macos: usable
    windows: partial
    linux: partial
  auv3:
    macos: experimental
    ios: experimental
  lv2:
    linux: experimental
  wam_v2:
    web: partial
    notes: A Pulp Processor builds to a WAMv2 AudioWorklet plugin via pulp_add_wam_plugin. Coverage spans an effect (PulpGain — loads, renders, generated controls in Chrome; dB-accurate gain, interleaved stereo, bypass), a richer effect (PulpChorus — input transformed), an instrument (PulpPluck — MIDI note-on produces sound and decays after note-off), and a convolution reverb (SuperConvolver — CPU PartitionedConvolver against a built-in synthetic IR; the GPU engine, file-backed IR loader and native editor are compiled out on web, and latency stays the fixed 512-sample internal block). Verified by deterministic Node runners plus native bridge tests (param/state round-trip, MIDI delivery), and by a headless-Chrome browser fixture in the web-plugins CI lane. A worklet has no second thread, so control work that must stay off the render call goes through Processor::on_non_realtime_tick(), which the adapter coalesces to once per render turn. Stereo, single-instance canary — not full WamEnv/WamGroup host conformance.
  webclap:
    web: experimental
    notes: A Pulp Processor builds to a CLAP-in-WebAssembly module via pulp_add_wclap (wasi-sdk, wasm32-wasi-threads, -fno-exceptions). The checked-in PulpGain WebCLAP (examples/web-demos/wclap-build) exports the WebCLAP host contract (clap_entry, malloc/free/cabi_realloc, growable table, shared imported memory). wclap_probe.mjs verifies the module is live; wclap_host_runner.mjs (Node) and examples/web-demos/wclap-build/browser-host (in-browser, headless-validated against Chrome/Canary) both drive the full CLAP lifecycle — create_plugin, init, activate, process — rendering audio and controlling parameters (Input Gain +6 dB raises output exactly +6 dB), with host callbacks synthesized via wasm trampoline modules (no WebAssembly.Function, no C++ host shim). SuperConvolver ships through this ABI as well as WAM, and both are driven by the SAME assertions in the web-plugins CI lane so the two ABIs cannot diverge. Pages need cross-origin isolation (COOP/COEP) for the threaded-wasm shared memory. Remaining: real-time AudioWorklet streaming and a .wclap bundle layout.
  aax:
    macos: experimental
    windows: experimental
    linux: unsupported

# Plugin extensions / host-query interfaces that ride on top of a format
# (separate from the format itself). Status reflects the Pulp side of the
# integration; SDKs are developer-supplied where required.
plugin_extensions:
  ara:
    status: experimental
    notes: >
      SDK-gated companion-factory hooks are present for CLAP
      (`kClapAraFactoryExtension`), AUv3 (`audioUnitARAFactory`), and VST3
      (`kVst3AraFactoryContextKey`). `host_supports_ara()` still returns false,
      and the published factory's document-controller callbacks are scaffold
      no-ops until the production ARA workstream wires real controller behavior.
      Celemony's ARA SDK is developer-supplied.
    limitations:
      - "AraDocumentController callback surface is scaffolded; playback renderer and content readers are not implemented."
      - "The SDK-built companion factory is process-global today; per-plugin AraDocumentController ownership/gating is not fully honored yet."
      - "Celemony ARA SDK is developer-supplied; not bundled."
  native_components:
    status: experimental
    notes: >
      Opt-in native-language audio components (Rust first) behind a private
      C-shaped FFI: a C++ NativeCoreProcessor adapter owns a source-built
      Rust/C/Zig DSP core. Boundary is a language-neutral C ABI, OFF by default —
      default builds need no Rust toolchain. The full seam is implemented across
      its phased rollout: the Processor-level FFI + adapter, the opt-in Rust
      staticlib lane (pulp_add_cargo_staticlib), non-RT domain logic via
      editor_command, stateful custom SignalGraph nodes, the public pulp_node_v1
      node ABI, and signed dynamic node packs (core/host/node_pack.hpp). Still
      experimental — not a frozen binary ABI. Rationale and scope:
      docs/reference/native-components.md.
    limitations:
      - "No frozen binary ABI yet — source-build/rebuild against the SDK is required; pulp_node_v1 is same-major append-only, not a frozen cross-release promise."
      - "iOS / AUv3 / sandboxed targets: static-bundled, signed native code only — no runtime-loaded or downloaded native DSP."
      - "Dynamic node packs are desktop-and-Android only (core/host is compiled out on iOS)."
      - "Pulp ships the FFI skeleton, bindings, CMake/Cargo glue, and reference cores — not a bundled DSP framework or crate-selection opinions."
# Known limitations per format. Keyed the same as `formats:`. The renderer
# in capabilities.md surfaces these as a "Known limitations" link on each
# row. Production-readiness workstream 08 sub-deliverable 8.5.
#
# Adding an entry here is non-blocking — the entry's status in `formats:`
# is the load-bearing label. `format_limitations:` exists so readers don't
# confuse "the adapter compiles" with "the adapter is feature-complete."
format_limitations:
  vst3:
    - "Bus 0, one sidechain input, and descriptor-declared secondary output buses are routed through ProcessBuffers; a multi-out processor that overrides process(ProcessBuffers&) writes each aux output bus, and single-output processors leave aux buses silent (workstream 01)."
    - "Dynamic bus arrangements are limited to descriptor-declared bus counts and mono/stereo layouts; unsupported layouts require host-quirk silence accommodation (workstream 01)."
  au_v2:
    - "Outbound MIDI IS wired: a plugin that sets produces_midi=true emits into midi_out during process(), and the adapter delivers it to the host via kAudioUnitProperty_MIDIOutputCallback (+ ...Info). Per-event sample offsets are preserved (offset N in -> N out), matching VST3/CLAP — proven by pulp-test-midi-out-offset-parity. produces_midi does NOT reclassify aufx->aumf: an aufx effect emits via the callback, while aumf is reserved for MIDI-ACCEPTING effects the host routes inbound MIDI to."
    - "Instruments (aumu / MusicDeviceBase) advertise one AU output ELEMENT per descriptor-declared output bus (main + aux), so an AU host lists every aux output and can route each to its own mixer channel (see examples/pulp-multi-out, an 8-voice / 8-bus synth; auval-validated multi-out). A processor that overrides process(ProcessBuffers&) writes each aux bus; one that only implements the simple process() leaves the aux buses silent (the adapter pre-zeroes every routed output bus). Buses a host leaves disconnected are delivered inactive/silent without reordering the bus->buffer mapping."
    - "MIDI effects (aumi / kAudioUnitType_MIDIProcessor) are wired end to end: PulpAUMidiProcessor, reached via PULP_AU_MIDI_EFFECT, receives host MIDI + SysEx, runs Processor::process() with no audio buses, and returns the Processor's midi_out through kAudioUnitProperty_MIDIOutputCallback with per-event sample offsets preserved. The component advertises zero audio input elements and one silent audio output element — the host renders that element to advance the plugin, which is what drives MIDI processing. A declared Bypass parameter passes MIDI through untouched (a bypassed MIDI FX is a wire, not a mute). auval -v aumi covers discovery, initialize, scope formats, properties, and the parameter surface; it runs no render or MIDI test for aumi, so in-DAW MIDI-FX behavior is only proven by hosting the plugin."
    - "Effects (aufx/aumf) are single main-in/main-out at the SDK level (AUEffectBase = AUBase(ci,1,1), and its Render pulls only input element 0). A descriptor-declared sidechain input surfaces as an inactive Sidechain bus in ProcessBuffers so sidechain_input() returns null gracefully; live sidechain-audio delivery into an AU effect requires the aumu/multi-bus path or a future 2nd-input-element pull (not testable via auval, which feeds no sidechain)."
  clap:
    - "Bus 0 routes to Processor::process(), bus 1 routes to Processor::set_sidechain(), and descriptor-declared secondary output buses are routed through ProcessBuffers; a multi-out processor that overrides process(ProcessBuffers&) writes each aux output bus, while additional input buses beyond the sidechain are not exposed."
    - "CLAP PARAM_MOD note_id/port/channel/key fields are accepted as parameter modulation but are not routed with per-note modulation scope."
  lv2:
    - "Atom sysex is ignored — the run() loop only promotes 1–3-byte short MIDI messages out of the input atom sequence; sysex sidecar wiring remains part of format-adapter production-readiness work."
  aax:
    - "Custom editor and parameter gestures run through the shared PluginViewHost on macOS and Windows, but the editor needs a Skia-enabled build: without Skia the Windows host falls back to the no-op factory and the plugin loads with no editor."
    - "The custom editor has not been validated in Pro Tools itself, and is opt-in for that reason: PULP_AAX_PLUGIN registers no editor, so a plugin gets Pro Tools' auto-generated parameter strip unless it is declared with PULP_AAX_PLUGIN_WITH_GUI. The strip is the only AAX UI any Pulp plugin has shipped with."
    - "The SDK-gated AAX suites (pulp-test-aax-effect-gui, pulp-test-aax-midi-node, pulp-test-aax-entry-registration) have never been built by Pulp's own build system: no Avid SDK is present in Pulp's CI or on any Pulp development machine, and the format's per-plugin sources are not compiled into pulp::format. The AAX logic that is genuinely covered on every lane is the SDK-free part — parameter/model mapping, gesture routing, editor sizing, MIDI reassembly. Treat the SDK-gated suites as unbuilt until an Avid SDK is wired into a lane."
    - "The InsertOrAudioSuite role is declared, but no AAX_IHostProcessor is registered, so the dedicated AudioSuite offline-render path is not implemented. Implementing it is out of scope."
  auv3:
    - "Bus 0 and descriptor-declared input bus 1 are routed through ProcessBuffers; additional input buses and secondary output buses are not exposed through the AUv3 adapter surface yet (workstream 01)."
    - "iOS jetsam pressure not modeled — heavy V8 / WebView workloads in the AUv3 process risk termination at ~50 MB (workstream 05)."
  wam_v2:
    - "Browser origin sandbox only; no Pulp-side capability manifest yet (security plan v4 §4.5)."
    - "Stereo, single-instance per node; no WamEnv/WamGroup host conformance or sample-accurate automation coverage yet."
    - "DSP is CPU-only. No GPU-compute code compiles to wasm — the Skia wasm slice is Ganesh/WebGL2, which has no compute shaders — so a plugin with a GPU engine runs its CPU engine on the web."
  webclap:
    - "Same browser-sandbox-only constraint as wam_v2."
    - "Pulp Browser Host recognizes WCLAP URLs/files, but .wclap.tar.gz unpack/instantiate via wclap-host-js is still placeholder work."
    - "Same CPU-only DSP constraint as wam_v2."

scripted_ui:
  ui_script_target_wiring:
    status: usable
    notes: UI_SCRIPT is threaded through VST3, AU, CLAP, and Standalone targets via PULP_UI_SCRIPT_PATH.
  scripted_editor_loading:
    macos: usable
    windows: partial
    linux: partial
    notes: Scripted editor sessions load in the supported desktop targets. Non-macOS desktop hosts are not yet runtime-validated to the same level.
  live_js_reload:
    macos: usable
    windows: unsupported
    linux: unsupported
    notes: Available in desktop standalone hosts that poll ScriptedUiSession::poll(). Runtime-validated only in the standalone macOS lane; plugin hosts still need a host-neutral polling contract.
  live_theme_reload:
    macos: usable
    windows: unsupported
    linux: unsupported
    notes: Sibling theme.json overrides reload in ScriptedUiSession. Runtime-validated only in the standalone macOS scripted UI lane.
  state_preservation:
    status: usable
    ci_test: pulp-test-state
    notes: Knob, fader, toggle, checkbox, toggle-button, and XY pad values are restored across scripted UI reloads. JavaScript heap state is rebuilt.

audio_io:
  coreaudio:
    status: usable
    platform: macos
  avaudiosession:
    status: experimental
    platform: ios
    notes: >-
      AVAudioSession category / interruption / route-change / media-services-reset
      all observed in Swift and forwarded to native listeners via
      pulp_ios_audio_session_emit. Runtime validation still waits on
      iOS on-device smoke coverage.
  wasapi:
    status: experimental
    platform: windows
    notes: >-
      Shared-mode and exclusive-mode playback (DeviceConfig::share_mode),
      a shared low-latency path via IAudioClient3 (DeviceConfig::low_latency),
      IMMNotificationClient hotplug, and AUDCLNT_E_DEVICE_INVALIDATED recovery
      (clean-stop + device-change notify; host re-opens).
  alsa:
    status: experimental
    platform: linux
    notes: >-
      Output path works. Input capture and real device-metadata
      enumeration remain pending.
  jack:
    status: experimental
    platform: linux
    notes: >-
      Optional backend compiled when libjack is available; the Linux audio
      factory prefers JACK when a running server is detected, then falls back
      to ALSA. JACK-gated enumeration/open smoke coverage exists, but broader
      hotplug and device-manager policy remain pending.

midi_io:
  coremidi:
    status: usable
    platform: macos
  win32_midi:
    status: experimental
    platform: windows
  alsa_midi:
    status: experimental
    platform: linux

rendering:
  dawn_metal:
    status: experimental
    platform: macos
    notes: Presentable surface via CAMetalLayer-backed NSView. GPU path opt-in via use_gpu in WindowHost and PluginViewHost. CoreGraphics remains default.
  skia_graphite:
    status: experimental
    platform: macos
    notes: Skia Graphite wired through Dawn device. Offscreen and on-screen rendering work. CoreGraphics is still the default path.
  dawn_metal_ios:
    status: experimental
    platform: ios
    notes: IOSGpuWindowHost and IOSGpuPluginViewHost implemented. Not yet runtime-validated on device.
  dawn_d3d12:
    status: experimental
    platform: windows
    notes: Surface creation via SurfaceSourceWindowsHWND implemented. SDL3 extraction for HWND. Not runtime-validated on hardware.
  dawn_vulkan:
    status: experimental
    platform: linux
    notes: Surface creation via X11NativeHandle / SurfaceSourceXlibWindow implemented; SDL3 extracts X11 and Wayland handles, but GpuSurface consumes X11 only today. Not runtime-validated on hardware.
  coregraphics_fallback:
    status: usable
    platform: macos
    notes: Current default render path for all macOS view hosts. Production-ready.

css_parity:
  grid_layout:
    status: usable
    notes: >
      CSS Grid Layout Level 1: LayoutMode::grid, template columns/rows with fr/px/auto
      units, column/row gaps, auto-placement, grid item spanning (grid-column/grid-row).
      JS bridge: createGrid, setGrid.
  flexbox:
    status: usable
    notes: >
      Full flexbox: direction, wrap, grow/shrink/basis, gap, row-gap/column-gap,
      justify-content (start/center/end/space-between/space-around/space-evenly),
      align-items, align-self, order, margin, padding shorthand expansion.
  transforms:
    status: usable
    notes: >
      CSS Transforms Level 1: translate(x,y), rotate(deg), scale(s), skew(x,y).
      Transform origin (normalized 0–1). Composited in paint_all. JS bridge:
      setTranslate, setRotation, setScale, setTransformOrigin, setTransform
      (full 2D affine matrix), clearTransform.
  animations:
    status: usable
    notes: >
      FrameClock-driven animation system. ValueAnimation with easing curves
      (ease-in/out/in-out, cubic, spring). Motion tokens for consistent timing.
      Widget-level animations: knob hover glow, toggle slide, fader thumb scale,
      scroll bar fade, tooltip opacity. JS bridge: animate, defineKeyframes,
      setAnimation, setMotionToken.
  backgrounds_borders:
    status: usable
    notes: >
      CSS Backgrounds Level 3: background-color, linear-gradient (4 directions +
      multi-stop). Border: width, color, radius (per-corner planned). Box shadow:
      offset, blur, spread, color. JS bridge: setBackground, setBorder,
      setBackgroundGradient, setBackgroundRepeat, setBackgroundPosition,
      setBackgroundSize, setBoxShadow. The size/position/repeat bridge slots
      are storage-only until raster background-image paint is wired.
  filters:
    status: usable
    notes: >
      CSS Filter Effects Level 1: blur(px). Applied per-element in paint_all.
      JS bridge: setFilter("blur(4px)").
  colors:
    status: usable
    notes: >
      CSS Color Level 4: #RGB, #RRGGBB, #RRGGBBAA, rgb(), rgba(), hsl(), hsla(),
      100+ named CSS colors. Full parseColor() in C++ bridge and JS prelude.
  text:
    status: usable
    notes: >
      CSS Text Level 3: text-transform (uppercase/lowercase/capitalize),
      text-decoration (underline/line-through/overline), text-align (left/center/right),
      text-overflow (ellipsis/clip), font-size, font-weight, font-style, letter-spacing,
      line-height, multi-line labels. JS bridge: setTextTransform, setTextDecoration,
      setTextAlign, setTextOverflow, setFontSize, setFontWeight, setFontStyle.
  positioning:
    status: usable
    notes: >
      CSS Positioned Layout Level 3: position (static/relative/absolute/fixed/sticky),
      top/right/bottom/left offsets, z-index. Applied during layout_children.
      JS bridge: setPosition, setTop/Right/Bottom/Left, setZIndex.
  canvas_2d:
    status: usable
    ci_test:
      - pulp-test-canvas
      - pulp-test-canvas-widget
    notes: >
      25 draw command types on CanvasWidget: fillRect, strokeRect, fillRoundedRect,
      strokeRoundedRect, fillCircle, strokeCircle, strokeLine, fillText, setFont,
      setFillColor, setStrokeColor, setLineWidth, beginPath, moveTo, lineTo,
      quadTo, cubicTo, closePath, fillPath, strokePath, save, restore, translate,
      scale, rotate, clipRect. Image APIs are scoped separately under runtime_apis.
  opacity:
    status: usable
    notes: Per-element opacity (0.0–1.0) applied as layer alpha in paint_all.
  overflow:
    status: usable
    notes: Overflow hidden/visible clipping on container views.
  cursor:
    status: usable
    notes: CSS cursor property with 7 styles (default, pointer, crosshair, text, grab, grabbing, not-allowed).
  transitions:
    status: partial
    notes: Transition duration stored as theme dimension token. Full CSS transition shorthand parsed in JS prelude.
  calc_expressions:
    status: usable
    notes: >
      calc(), min(), max(), clamp() expression evaluator with nested function support,
      arithmetic operators (+, -, *, /), and mixed-unit operands. Proper operator
      precedence (multiply/divide before add/subtract).
  unit_resolution:
    status: usable
    notes: >
      resolveLength() converts em (parent font-size), rem (root font-size),
      % (parent dimension), vw/vh/vmin/vmax (viewport), ch (approximate) to px.
      resolveCSSLength() provides unified calc-aware API.
  selectors:
    status: usable
    notes: >
      Full selector engine: tag, .class, #id, descendant (space), child (>),
      :first-child, :last-child, :nth-child(An+B), :nth-last-child, :only-child,
      :empty, :checked, :disabled, :not(selector), :hover, :focus, :active,
      :first-of-type, :last-of-type, :nth-of-type(An+B),
      attribute selectors [attr], [attr="val"], [attr~=], [attr^=], [attr$=], [attr*=].
  dom_api:
    status: usable
    notes: >
      closest(selector), matches(selector), contains(node), innerHTML get/set with
      HTML parser, outerHTML, querySelector/querySelectorAll on elements,
      append/prepend/before/after/replaceWith (DOM4), classList.replace(),
      focus()/blur(), cloneNode, createDocumentFragment(), <option> sync for <select>.
  events:
    status: usable
    ci_test: pulp-test-events
    notes: >
      click, dblclick, mousedown/up, mouseenter/leave, wheel, scroll, contextmenu,
      keydown/keyup, input/change, focus/blur, pointer events (L2), gesture events.
      stopPropagation, stopImmediatePropagation, preventDefault. CustomEvent constructor.
      window.addEventListener for global events. Capture + bubble event flow.
  per_side_borders:
    status: usable
    notes: >
      Per-side borders (border-top/right/bottom/left with individual width + color).
      Per-corner border-radius (border-top-left-radius etc., 4 independent corners).
  animation_properties:
    status: usable
    ci_test:
      - pulp-test-animation
      - pulp-test-widget-animation
    notes: >
      CSS animation-* properties parsed: name, duration, timing-function, delay,
      iteration-count (including infinite), direction, fill-mode. animation shorthand.
      Execution via FrameClock animate() bridge.
  css_shorthands:
    status: usable
    notes: >
      flex-flow, place-items, place-content, inset, box-sizing (parsed),
      margin-inline/block, padding-inline/block (CSS logical properties),
      -webkit-line-clamp, background-repeat/position/size (parsed and
      round-tripped; raster background paint deferred).
  media_queries:
    status: usable
    notes: >
      matchMedia() with min-width, max-width, min-height, max-height, orientation
      (landscape/portrait). window.innerWidth/innerHeight dynamic via getRootSize().
  aspect_ratio:
    status: usable
    notes: CSS aspect-ratio property (16/9 shorthand or single number).
  visibility:
    status: usable
    notes: CSS visibility hidden (preserves layout space, sets opacity to 0).
  pointer_events_css:
    status: usable
    notes: CSS pointer-events none/auto. View.hit_testable_ flag skips in hit_test.
  outline:
    status: usable
    notes: CSS outline property (width, color). Parsed from shorthand "2px solid blue".
  white_space:
    status: usable
    notes: CSS white-space (normal/nowrap/pre). Maps to Label multi_line flag.
  text_shadow:
    status: partial
    notes: CSS text-shadow parsed and routed through setTextShadow / setTextShadow* storage; Label paint shadow rendering remains planned.
  font_family:
    status: partial
    notes: >
      CSS font-family parsed and routed through setFontFamily; Skia/TextShaper
      resolve comma-separated fallback lists, with registerFont for bundled
      faces. Most Label paint paths honor own and inherited font-family, but one
      path (label.cpp soft-wrap default) still falls back to "Inter" without
      consulting inheritable_font_family(), so a container View -> child Label
      font-family cascade does not paint in that case. Kept partial until that
      paint path reads the inherited family. Validated by
      pulp-test-widget-bridge-wave5-css and pulp-test-canvas-fonts.

widgets:
  image_view:
    status: usable
    notes: >
      ImageView via createImage/setImageSource decodes file-backed images through
      Canvas::draw_image_from_file / draw_image_from_file_rect on supported backends;
      missing files or non-image backends fall back to a filename placeholder.
  list_box:
    status: usable
    notes: >
      Virtualized ListBox with scroll wheel support, auto-scroll on keyboard navigation.
      JS bridge: createListBox, setListItems, setListSelected, setListRowHeight.
  combo_box:
    status: usable
    ci_test: pulp-test-combo-dropdown
    notes: Dropdown with global click-outside dismiss, keyboard navigation, hover tracking.
  scroll_view:
    status: usable
    ci_test: pulp-test-scroll-view
    notes: ScrollView with smooth scroll animation, bar fade-in/out, content size management.
  text_editor:
    status: usable
    ci_test:
      - pulp-test-text-editor
      - pulp-test-text-editor-mouse
      - pulp-test-text-editor-paint
      - pulp-test-text-editor-policy
      - pulp-test-text-editor-multiline
      - pulp-test-text-input
      - pulp-test-context-menu
      - pulp-test-cluster-step
      - pulp-test-bidi-text
    notes: >
      Full text editor: grapheme-safe UTF-8 cursor/delete, platform keyboard
      movement and selection variants, word/line delete shortcuts, page
      movement, selection (click/drag/double/triple), word-granular
      double-click drag, clipboard (Cmd/Ctrl+C/X/V/A and
      paste-and-match-style), password clipboard/selected-text protection,
      explicit clipboard and line-ending policies, default Cut/Copy/Paste/Select
      All context menu, undo/redo with selection restoration, configurable Tab
      and multiline Return policies, read-only and disabled policy, numeric-only,
      max length, paste sanitizer, input filter/validator hooks, password mode,
      public caret/selection APIs, programmatic set_text undo reset, multi-line
      scrolling, placeholder, and IME composition with marked text.
  tree_view:
    status: usable
    ci_test: pulp-test-tree-view
    notes: Hierarchical tree with expand/collapse, indent levels.
  progress_bar:
    status: usable
    notes: Progress indicator with animated fill.
  tooltip:
    status: usable
    notes: Fade-in/out tooltip attached to any view.
  # Audio / music-domain widgets.
  meter:
    status: usable
    notes: Meter, MultiMeter, CorrelationMeter — peak/RMS level bars with ballistics; CI coverage includes pulp-test-visualization. See core/view/include/pulp/view/widgets.hpp.
  spectrum_view:
    status: usable
    notes: Real-time FFT spectrum display with log/linear frequency axis; CI coverage includes pulp-test-visualization. See core/view/include/pulp/view/widgets.hpp.
  spectrogram_view:
    status: usable
    notes: Scrolling STFT spectrogram; CI coverage includes pulp-test-visualization. See core/view/include/pulp/view/widgets.hpp.
  waveform_view:
    status: usable
    notes: WaveformView for rendering audio buffers; CI coverage includes pulp-test-visualization. See core/view/include/pulp/view/widgets.hpp.
  waveform_editor:
    status: usable
    notes: Selection, zoom, region-based editor built on WaveformView; CI coverage includes pulp-test-waveform-editor. See core/view/include/pulp/view/waveform_editor.hpp.
  midi_keyboard:
    status: usable
    notes: Display + interaction keyboard; note-on/off events routed through Binding; CI coverage includes pulp-test-phase9-widgets. See core/view/include/pulp/view/midi_keyboard.hpp.
  preset_browser:
    status: usable
    notes: Searchable, category-grouped preset browser with keyboard navigation; CI coverage includes pulp-test-preset-browser. See core/view/include/pulp/view/preset_browser.hpp.
  eq_curve_view:
    status: usable
    notes: Draggable EQ band handles over the true biquad magnitude response (signal::response_curve_db), with an optional spectrum overlay resampled onto the shared log-frequency axis; CI coverage includes pulp-test-phase9-widgets. See core/view/include/pulp/view/eq_curve_view.hpp.
  graph_editor_view:
    status: usable
    ci_test: pulp-test-graph-editor-view
    notes: Canvas-based node editor on top of pulp::canvas; renders a SignalGraph as a draggable, connectable graph; CI coverage includes pulp-test-graph-editor-view. See core/view/include/pulp/view/widgets/graph_editor_view.hpp.
  # General-purpose UI widgets surfaced by the audit as missing rows.
  file_browser:
    status: usable
    notes: FileBrowser + FileDropZone; CI coverage includes pulp-test-file-browser. See core/view/include/pulp/view/file_browser.hpp.
  color_picker:
    status: usable
    notes: HSV + hex color picker with live swatch; CI coverage includes pulp-test-phase9-widgets. See core/view/include/pulp/view/color_picker.hpp.
  concertina_panel:
    status: usable
    notes: Stacked accordion-style panel with expand/collapse per section; CI coverage includes pulp-test-view-layout-widgets. See core/view/include/pulp/view/concertina_panel.hpp.
  split_view:
    status: usable
    notes: Draggable splitter between two views; horizontal and vertical; CI coverage includes pulp-test-split-view. See core/view/include/pulp/view/split_view.hpp.
  breadcrumb:
    status: usable
    notes: Breadcrumb trail navigation; CI coverage includes pulp-test-phase9-widgets. See core/view/include/pulp/view/breadcrumb.hpp.
  lasso:
    status: usable
    notes: Marquee / drag-to-select overlay; CI coverage includes pulp-test-lasso. See core/view/include/pulp/view/lasso.hpp.
  code_editor:
    status: usable
    ci_test: pulp-test-code-editor
    notes: Syntax-highlighted text editor with gutter and line numbers; CI coverage includes pulp-test-code-editor. See core/view/include/pulp/view/code_editor.hpp.

js_authoring:
  web_compat_layer:
    status: usable
    notes: >
      JS prelude providing document.createElement, element.style, addEventListener,
      StyleSheet, querySelector/querySelectorAll, getBoundingClientRect, getComputedStyle.
      148 named CSS colors, shorthand expansion, transform/transition parsing.
      No DOM/CSSOM — pure JS shim over native widget bridge.

documentation:
  getting_started:
    status: usable
    notes: Setup, first plugin, UI scripting, hot-reload, themes.
  migration_guide:
    status: usable
    notes: React/CSS side-by-side mapping tables for developers coming from web.
  api_reference:
    status: usable
    notes: All 111+ JS bridge functions documented with signatures and examples.
  cookbook:
    status: usable
    notes: 10 practical recipes for common UI patterns.
  starter_template:
    status: usable
    notes: Gain plugin template with UI script, --template CLI flag.
  component_showcase:
    status: usable
    notes: Every widget type demonstrated in one JS file.
  design_token_guide:
    status: usable
    notes: Theme system, inheritance, custom tokens, export formats.
  custom_rendering_guide:
    status: usable
    notes: Three layers — JS canvas to GPU shaders.

testing:
  web_compat_validation:
    status: usable
    notes: >
      424 browser-style validation tests: CSS value/color/shorthand/calc parsing (157),
      computed style + layout tests for flex/grid/position/nested (135),
      event tests for click/hover/keyboard/focus/bubbling/selectors (81),
      visual reftests and screenshot regression (46).
  unit_tests:
    status: usable
    notes: 1622+ Catch2 tests across all subsystems. CI on every PR.
  format_validation:
    status: usable
    notes: CLAP dlopen tests, VST3 load tests, auval for AU.
  audio_golden_files:
    status: usable
    notes: DSP output compared against reference files with tolerance.

platform_maturity:
  cursor_management:
    status: usable
    platform: macos
    notes: CursorStyle enum mapped to NSCursor in mouseMoved. All 7 styles supported.
  tab_focus:
    status: usable
    notes: Tab/Shift+Tab cycles focus through focusable views. Wraps at ends.
  accessibility:
    macos:
      status: usable
      notes: VoiceOver via NSAccessibilityElement. AccessRole maps to NSAccessibilityRole.
    ios:
      status: usable
      notes: VoiceOver via UIAccessibilityElement with accessibilityIncrement/Decrement for sliders. See core/view/platform/ios/accessibility_ios.mm.
    android:
      status: usable
      notes: TalkBack via JNI bridge exposing role, label, value, table metadata, and click/increment/decrement actions. See core/view/platform/android/accessibility_android.cpp.
    windows:
      status: partial
      notes: >
        UIA provider tree, WM_GETOBJECT hook, per-widget fragments, and
        value/focus/name event helpers are implemented in
        core/view/platform/win/accessibility_win.cpp. Direct UIA client and
        screen-reader regression tests are still pending.
    linux:
      status: partial
      notes: >
        AT-SPI2 over direct D-Bus (no libatk) exports root/application objects,
        per-widget Accessible/Component/Value objects, and event-hook
        marshalling; session-bus loopback tests cover tree/value behavior.
        Real registry/Orca signal receipt remains pending.
  ime_composition:
    status: usable
    platform: macos
    notes: Full NSTextInputClient — setMarkedText, unmarkText, firstRectForCharacterRange for candidate window positioning.
  context_menu:
    status: partial
    notes: >-
      on_context_menu callback, rightMouseDown dispatch, and view-tree
      ContextMenu actions are usable. registerContextMenu dispatches coordinates
      to JS; showContextMenu delegates to platform PopupMenu but current in-tree
      backends return -1/no selection because native item selection is not wired
      through the bridge yet.
  keyboard_shortcuts:
    status: usable
    notes: registerShortcut bridge function, shortcuts checked before global key dispatch.
  file_dialogs:
    status: partial
    notes: >-
      macOS native (NSOpenPanel/NSSavePanel) — `usable`. Windows ships
      an opt-in built-in IFileDialog backend (open/open-many/save/
      choose-folder); Linux ships an opt-in built-in xdg-desktop-portal
      FileChooser backend over a runtime-dlopen libdbus client. Hosts
      opt in with FileDialog::install_native_backend() (the standalone
      app does this automatically), and Linux honest-fails when libdbus
      or the portal service is absent. File-type filters and a
      preselected folder are a follow-up for the Windows/Linux built-in
      backends. No native provider ships yet for iOS/Android; callers
      there register their own backend or receive explicit no-selection.
      has_backend() stays honest on every platform.
  native_child_view_embedding_window_host:
    status: partial
    notes: >-
      Standalone native child-view embedding is an instance capability, not a
      blanket platform guarantee. Built-in `WindowHost` embedding is macOS
      only; built-in iOS `WindowHost` does not currently expose child-view
      handles. Windows/Linux/Android require a host-registered
      `WindowHost::Factory` that returns non-null native handles and implements
      attach/bounds/detach.
    platforms:
      macos: usable
      ios: unsupported
      windows: partial
      linux: partial
      android: partial
  native_child_view_embedding_plugin_view_host:
    status: partial
    notes: >-
      Plugin editor native child-view embedding is built in on macOS and iOS.
      Windows/Linux/Android require a host-registered
      `PluginViewHost::Factory` that returns non-null native handles and
      implements attach/bounds/detach; no first-party child-embedding
      implementation ships yet for those platforms.
    platforms:
      macos: usable
      ios: usable
      windows: partial
      linux: partial
      android: partial

input:
  pointer_events:
    status: usable
    notes: >
      W3C PointerEvent API: pointerdown/pointermove/pointerup/pointercancel with
      pointerId, pointerType (mouse/touch/pen), isPrimary, pressure, clientX/clientY.
      Unified model — same JS code works for mouse (macOS), touch (iOS), and stylus (Apple Pencil).
  pointer_capture:
    status: usable
    notes: >
      setPointerCapture/releasePointerCapture for drag interactions.
      gotpointercapture/lostpointercapture events. Implicit release on pointerup.
  stylus:
    status: usable
    platform: ios
    notes: >
      Apple Pencil properties: pressure (0.0–1.0), altitudeAngle, azimuthAngle.
      pointerType="pen" discrimination. Exposed via PointerEvent JS API.
  gesture_events:
    status: usable
    notes: >
      gesturestart/gesturechange/gestureend with scale and rotation.
      macOS: magnifyWithEvent/rotateWithEvent trackpad gestures.
      iOS: multi-touch gesture analysis (planned).
  ipados_hover:
    status: usable
    platform: ios
    notes: UIHoverGestureRecognizer for trackpad/mouse hover on iPadOS 13+.
  touch_action_css:
    status: usable
    notes: CSS touch-action property to control default gesture handling (auto, none, pan-x, pan-y, manipulation).
  coalesced_predicted:
    status: partial
    notes: >
      getCoalescedEvents()/getPredictedEvents() API on PointerEvent objects.
      JS API structure in place. iOS coalescedTouches/predictedTouches data piping planned.

runtime_apis:
  canvas_2d:
    status: usable
    ci_test:
      - pulp-test-canvas
      - pulp-test-canvas-widget
    notes: >
      25+ draw commands including gradient fills (linear/radial), arc/ellipse,
      textAlign/textBaseline, clearRect, clipRect, globalAlpha, lineCap/lineJoin,
      globalCompositeOperation (blend modes), fillRoundedRect, strokeCircle.
  clipboard:
    status: usable
    ci_test: pulp-test-clipboard
    notes: >-
      navigator.clipboard.readText/writeText via platform::Clipboard C++ API.
      macOS/iOS/Windows use native pasteboard APIs (text + binary
      set_data/get_data). Linux uses xclip/wl-copy for text AND binary
      custom-type data (wl-copy --type / xclip -t, MIME type sanitized;
      xsel is text-only and honest-fails for binary); set_text/set_data
      return false honestly when no tool is on PATH or no display is
      reachable. Android routes through a host-registered bridge
      (Clipboard::set_android_bridge / clear_android_bridge); generated
      app wiring remains pending.
  local_storage:
    status: usable
    notes: localStorage.getItem/setItem/removeItem — file-backed in temp directory.
  performance:
    status: usable
    notes: performance.now() via std::chrono::steady_clock (sub-millisecond precision).
  fetch:
    status: usable
    notes: Minimal fetch(url, opts) via curl exec. GET/POST, returns {text(), json()}.
  encoding:
    status: usable
    notes: TextEncoder/TextDecoder (UTF-8), atob/btoa (base64).
  crypto:
    status: partial
    notes: crypto.getRandomValues() (Math.random-based, not cryptographic).
  image_constructor:
    status: usable
    notes: new Image() with src setter and onload callback for preloading.
  console_extended:
    status: usable
    notes: console.log/warn/error/info/debug + console.time/timeEnd.
  structured_clone:
    status: usable
    notes: structuredClone() via JSON round-trip.
  custom_event:
    status: usable
    notes: CustomEvent constructor with detail property, dispatchEvent support.
  timers:
    status: usable
    notes: setTimeout/setInterval/requestAnimationFrame via __requestFrame__ bridge.
  canvas_draw_image:
    status: partial
    notes: >
      canvasDrawImage records Canvas2D drawImage calls. The JS shim normalizes
      the 3-arg form to destination bounds from the image dimensions; the native
      bridge records the 5-arg destination form and the 9-arg source-rectangle
      form. Skia-backed canvases decode and draw file-backed images with
      source-rect slicing and image-smoothing state; CoreGraphics/base backends,
      missing files, unsupported encoded formats, and data-URI replay fall back
      to the labeled placeholder.
  drag_and_drop:
    status: usable
    notes: registerDrop(id, callback) + View::on_drop. Receives type/data/x/y on file or text drop.
  font_loading:
    status: partial
    notes: loadFont(path) validates existence only. registerFont(family, path) registers bundled .ttf/.otf files with the canvas font registry; FontFace and document.fonts remain unsupported.
  webgpu_shaders:
    status: usable
    notes: >
      Dawn/WebGPU backend available. setWidgetShader(id, skslCode) installs a custom
      SkSL body shader on a shader-capable widget, reporting compile errors rather
      than failing silently. getGPUInfo() queries backend. compileShader() validates.
      SkSL as a view post-effect (filtering already-rendered content) is not supported.

ai_shader_design:
  yoga_layout:
    status: usable
    notes: >
      Meta Yoga v3.2.1 (MIT) replaces hand-rolled flexbox. Correct CSS Flexbox L1:
      margin:auto centering, flex-wrap, absolute positioning, min-content/max-content.
  shader_engine:
    status: usable
    notes: >
      RuntimeEffectCache (process-lifetime SkRuntimeEffect compiler). draw_with_sksl()
      on Canvas with GPU rendering via SkiaCanvas. compileShader() validates SkSL.
      setWidgetShader/clearWidgetShader for Knob/Fader/Toggle body-layer shaders.
      Shader `time` is now driven from FrameClock. Recommended workflow is
      preset/material-first restyling with audio-plugin style-family routing;
      raw SkSL remains a developer escape hatch. The write-once-multi-backend
      contract (SkSL → Skia Graphite → WGSL → Dawn → Metal / D3D12 / Vulkan /
      OpenGL ES) and its fragment / post-effect scope limits are documented in
      docs/reference/shaders.md.
  declarative_schema:
    status: usable
    notes: >
      Rive-inspired JSON widget schema: elements (arc/circle/line/rect/text),
      value binding, color token resolution, percentage dimensions. Interpreted
      by render_schema() at paint time. setWidgetSchema/clearWidgetSchema on
      Knob/Fader/Toggle. Recommended for structured non-color widget look changes
      alongside preset/material overrides.
  lottie_animations:
    status: partial
    notes: >
      setWidgetLottie/seekWidgetLottie bridge, Lottie JSON storage, and scrub
      state on Knob/Fader/Toggle work and are tested. Skia chrome/m152 restores
      the jsonreader and skresources archives required by native skottie, and
      the configure-time real link probe in core/canvas/CMakeLists.txt enables
      PULP_LOTTIE only where the complete archive set links. LottieView still
      degrades to a no-op on any slice that fails that probe. The wasm slice
      omits jsonreader/skresources, so Lottie remains unavailable there. See
      external/skia-build/VERSION.md.
  web_animations_api:
    status: usable
    notes: >
      element.animate(keyframes, options) with Animation object (play/pause/cancel).
      Easing: linear, ease-in, ease-out, ease-in-out. fill mode, animationend event.
  design_tokens_interop:
    status: usable
    notes: >
      importDesignTokens/exportDesignTokens in W3C Design Tokens format.
      Enables round-trip with Figma token plugins, Stitch, v0.
  model_agnostic_ai:
    status: usable
    notes: >
      setAICli/getAICli configures AI CLI command at runtime.
      Default: Claude. Swappable to Gemini/Codex CLI. execAsync enables
      non-blocking shell-backed chat flows in local tools. Design workflows now
      carry provider/model/reasoning-effort metadata and expand command templates
      with {prompt_file}, {model}, {provider}, {reasoning_effort}, and {output_file}.
      Full navigator.gpu pipeline API not exposed — Canvas 2D + shaders is the design.
  design_debug_harness:
    status: experimental
    notes: >
      `pulp design-debug` runs the design chat pipeline headlessly and emits
      before/after/diff screenshots plus a JSON report containing provider/model/
      reasoning-effort metadata, target bounds, prompt/response files, and
      screenshot diff statistics. Supports replaying saved responses. The default
      Skia backend renders widget SkSL offscreen (`render_backend: skia-headless`,
      `widget_sksl_render_supported: true`). `--capture-backend live-gpu` now
      drives the real `pulp-design-tool` app in automation mode and records
      `render_backend: skia-live-gpu` with true live GPU widget screenshots.

design_import:
  figma_adapter:
    status: usable
    notes: JSON IR parsing, audio widget detection, token extraction. MCP live fetch via Claude skill.
  stitch_adapter:
    status: usable
    notes: JSON IR + HTML fallback parsing. Stitch MCP integration via Claude skill.
  v0_adapter:
    status: usable
    notes: JSON IR + Tailwind className extraction. URL fetch via --url flag.
  pencil_adapter:
    status: usable
    notes: JSON IR parsing with Yoga layout near-1:1 translation. Pencil MCP integration via Claude skill.
  designmd_adapter:
    status: partial
    parser-version: "0.1"
    format-version: alpha
    notes: >
      Google DESIGN.md (Apache-2.0). Emits tokens.json only — no
      ui.js, because DESIGN.md describes a system, not a screen. Strict
      detection (filename + frontmatter fence + name: + at least one of
      colors/typography/rounded/spacing/components; all-of with 95% min
      confidence). yaml-cpp (MIT) is vendored via CMake FetchContent for
      the frontmatter parse. Design lint / diff, Tailwind export,
      round-trip, and component scaffolding remain staged follow-ups. See
      reference/imports/designmd.md.
  code_generator:
    status: usable
    notes: >
      Native mode (createCol/createKnob/setFlex) and web-compat mode
      (document.createElement/style). Audio widget sizing constraints,
      Yoga layout requirements encoded.
  w3c_tokens:
    status: usable
    notes: >
      Full W3C/DTCG Design Tokens: group $type inheritance, alias resolution
      with cycle detection, math expressions, composite types (typography,
      shadow, border). Round-trip safe.
  figma_variables_sync:
    status: experimental
    notes: Parse Figma Variables JSON, export Theme as Figma Variables format. Slash-to-dot path conversion.
  stitch_design_system_sync:
    status: experimental
    notes: Parse Stitch Design System JSON (colors, fonts, roundness, spacing), export Theme as Stitch format.
  validation:
    status: usable
    notes: Headless render + screenshot comparison. Similarity threshold, diff image generation.
  cli_import_design:
    status: experimental
    notes: >
      pulp import-design with --from, --file, --url, --frame, --screen,
      --output, --tokens, --validate, --reference, --diff, --debug flags.
  cli_export_tokens:
    status: experimental
    notes: pulp export-tokens with theme JSON input or built-in dark theme default.
  claude_skill:
    status: usable
    notes: /import-design skill with MCP-aware flow for Figma, Stitch, Pencil, v0.

timeline_engine:
  model:
    status: experimental
    notes: Immutable project snapshots, typed transactions, canonical persistence, undo/redo, release downgrade, takes/comps, track freeze, clip-level None/Resample/Stretch time-conform intent, and crash-consistent native journals.
  playback:
    status: experimental
    notes: Immutable compiled programs, master transport, arrangement audio/note/automation rendering, sample-rate conversion, external MIDI synchronization, and bounded offline plus realtime application of clip-level None/Resample/Stretch time-conform intent.
  capture:
    status: experimental
    notes: Fixed-capacity audio/MIDI capture with control-side materialization and sealed recording commit; applications own device I/O and media publication.
  dawproject_import:
    status: experimental
    notes: Fail-closed DAWproject v1 linear subset with flat beats-timed note/audio tracks, sealed WAV identity, and explicit resource limits. A track's <Channel> is admitted when neutral and refused by its own concept when it states a volume or pan.
  dawproject_export:
    status: experimental
    notes: Bounded DAWproject writer over the same subset the importer reads. Emits project.xml plus an in-band loss manifest; everything outside the subset is declared in the capability table and refused by run_export unless the caller accepts each concept by name.
  smf_interop:
    status: experimental
    notes: Fail-closed Standard MIDI File import and a raw exporter that strictly checks visited clip event and tick-grid shapes but does not audit unrelated project state. The separate Format::Smf adapter performs the project-wide census, requires per-concept consent, and emits a versioned central loss manifest.
  cli_mcp:
    status: experimental
    notes: Seven operations retain stateless tool-timeline entry points shared with pulp seq and pulp render; three additional MCP-local diff/undo/redo operations use process-local DocumentSessions capped at 32 sessions, a 64 MiB aggregate admission charge (a deterministic serialized-size plus history proxy, not direct heap measurement), and 64 MiB per complete encoded result, with oldest-first eviction and no pulp seq session subtools. Diff reports the exact dirty set plus before/after revisions for the latest successful transition. The shared interchange boundary consumes and produces standard .dawproject ZIP containers atomically without replacing existing outputs.
  sequence_adapter:
    status: experimental
    notes: SequenceProcessor projects host transport and a caller-owned immutable playback program into plugin-format processing without owning editing, media resolution, devices, or UI.

subsystems:
  runtime: stable
  events: usable
  audio: usable
  midi: usable
  signal: usable
  state: stable
  format: usable
  platform: usable
  canvas: usable
  render: experimental
  view: usable
  osc: experimental

# ── Known limitations ────────────────────────────────────────────────────────
#
# Each entry keys a capability path already declared above and carries a list
# of known limitations that should be visible to plugin authors. Every
# limitation MUST cite a tracking location — typically a planning doc or
# a GitHub issue — so readers can follow the work.
#
# tools/list_limitations.py renders this block as a markdown table for
# injection into docs/reference/capabilities.md.
#
# Consistency rule enforced by check-docs-consistency.py (warn-mode):
# every key under limitations: must resolve to a real entry in the matrix.
limitations:
  timeline_engine.cli_mcp:
    - text: "The headless render path emits arrangement audio only; it does not instantiate hosted devices or MIDI instruments, and unknown plugin-delay compensation remains null rather than being invented."
      tracked_in: "../guides/timeline-sdk.md#one-typed-edit-through-cli-and-mcp"
  timeline_engine.dawproject_import:
    - text: "DAWproject import is a bounded linear subset: nested groups, warps, seconds-timed lanes, and unsupported timeline constructs fail the import rather than being dropped."
      tracked_in: "../guides/timeline-sdk.md#optional-dawproject-importer"
  timeline_engine.dawproject_export:
    - text: "DAWproject export is a bounded writer, not Tier-1 support: it emits flat tracks, beats-timed clips, inline notes, referenced audio, and a single tempo and meter. Markers, clip gain and fades, embedded media, mixer state, automation, devices, take lanes, and freeze are declared lost in the capability table and refused unless the caller accepts each concept by name. A neutral channel is emitted so a receiving DAW can register the track; that is not an export of the document's authored mixer state, which is still reported as dropped."
      tracked_in: "../guides/timeline-sdk.md#optional-dawproject-exporter"
  timeline_engine.smf_interop:
    - text: "Standard MIDI File import accepts format 0/1 with a metrical division, note on/off, and the tempo, time-signature, track-name, and end-of-track meta events; SMPTE divisions, format 2, and any other event fail the import unless the caller opts into ignoring non-note events."
      tracked_in: "../guides/timeline-sdk.md#optional-standard-midi-file-interop"
    - text: "Raw SMF export strictly rejects unsupported clip, event, and time-grid shapes it visits, but it is not a project-wide loss audit. The separate smf-interchange adapter performs the full concept census and requires per-concept consent before omitting unsupported containers or project state, stripping note modifiers, quantizing velocity, or stepping tempo ramps; run_export appends a versioned loss manifest."
      tracked_in: "../guides/timeline-sdk.md#optional-standard-midi-file-interop"
  timeline_engine.capture:
    - text: "Capture owns fixed-capacity callback buffers only; device I/O, durable media publication, and submission of materialized Timeline commands remain application responsibilities."
      tracked_in: "../guides/timeline-sdk.md#takes-comps-freeze-and-capture"
  formats.clap:
    - text: "Bus 0 routes to Processor::process(), bus 1 routes to Processor::set_sidechain(), and descriptor-declared secondary output buses are routed through ProcessBuffers; a multi-out processor that overrides process(ProcessBuffers&) writes each aux output bus, while additional input buses beyond the sidechain are not exposed."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.1"
    - text: "CLAP PARAM_MOD note_id/port/channel/key fields are accepted as parameter modulation but are not routed with per-note modulation scope."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.1"
  formats.vst3:
    - text: "Bus 0, one sidechain input, and descriptor-declared secondary output buses are routed through ProcessBuffers; a multi-out processor that overrides process(ProcessBuffers&) writes each aux output bus, and single-output processors leave aux buses silent."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.2"
    - text: "Dynamic bus arrangements are limited to descriptor-declared bus counts and mono/stereo layouts; unsupported layouts require host-quirk silence accommodation."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.2"
    - text: "Controller and per-note expression input is host-mediated: CC, pitch bend, and channel aftertouch arrive when the host honors IMidiMapping hidden parameters, and per-note tuning/pressure/timbre arrive for MPE-enabled plug-ins through INoteExpressionController. There is no separate raw poly-pressure event route."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.2"
  formats.au_v2:
    - text: "Plugin-side parameter changes do not propagate back to the host (no AUParameterListenerNotify)."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.3"
    - text: "Outbound MIDI from AU v2 effects is not wired yet; HandleMIDIEvent / HandleSysEx feed the adapter's MidiBuffer, but effects that set produces_midi=true have no render-notify path to emit MIDI back to the host."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.3"
  formats.auv3:
    - text: "Bus 0 and descriptor-declared input bus 1 are routed through ProcessBuffers; additional input buses and secondary output buses are not exposed through the AUv3 adapter surface yet."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.4"
    - text: "MIDI arrives as raw bytes; no type dispatch to note/CC/pitchbend/aftertouch."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.4"
    - text: "iOS validation is stale — no on-device example or AVAudioSession ↔ C++ bridge."
      tracked_in: "planning/production-readiness/05-auv3-mobile.md"
  formats.lv2:
    - text: "Atom sysex events are not routed — only 1–3-byte short MIDI messages in the atom input sequence reach Processor::process()."
      tracked_in: "planning/production-readiness/01-format-adapters.md#1.5"
  audio_io.wasapi:
    - text: "Full-duplex render/capture is not exposed as one synchronized WASAPI device: a WasapiDevice wraps either one render endpoint or one capture endpoint, so callers that need synchronized input/output must open and synchronize two devices."
      tracked_in: "planning/production-readiness/02-audio-midi-io.md#2.1"
    # Hotplug: IMMNotificationClient device-change callbacks are wired;
    # no stale stub limitation remains.
  audio_io.alsa:
    - text: "No input capture path."
      tracked_in: "planning/production-readiness/02-audio-midi-io.md#2.2"
    - text: "Hardcoded sample-rate list; no real enumeration."
      tracked_in: "planning/production-readiness/02-audio-midi-io.md#2.2"
  audio_io.jack:
    - text: "Server-backed open/start/stop smoke depends on a reachable JACK server and skips otherwise; JACK hotplug/device-manager policy remains pending."
      tracked_in: "planning/production-readiness/02-audio-midi-io.md#2.2"
  midi_io.coremidi:
    - text: "MIDI 2.0 channel-voice input is flattened to MIDI 1.0 where representable; per-note and other unsupported UMP statuses are not delivered through MidiInputCallback."
      tracked_in: "planning/production-readiness/02-audio-midi-io.md#2.6"
  midi_io.win32_midi:
    - text: "Default legacy mmeapi path has no Windows MIDI Services / MIDI 2.0 transport and no hotplug; SysEx input is routed via MIM_LONGDATA. The opt-in WinRT MIDI 2.0 backend requires PULP_HAS_WINRT_MIDI and the Windows MIDI Services SDK."
      tracked_in: "planning/production-readiness/02-audio-midi-io.md#2.4"
  midi_io.alsa_midi:
    - text: "Hotplug notifications depend on runtime libudev/udevd; when unavailable, the port-change callback is stored but will not fire, so clients must re-enumerate manually."
      tracked_in: "planning/production-readiness/02-audio-midi-io.md#2.5"
  platform_maturity.accessibility.windows:
    # UIA provider tree + event helpers are implemented in source. Remaining:
    # direct client/screen-reader regression coverage and hardening.
    - text: "Direct UIA client and screen-reader regression tests remain pending; provider tree, WM_GETOBJECT, and value/focus/name event helpers are implemented in source."
      tracked_in: "planning/production-readiness/04-accessibility.md#4.1"
  platform_maturity.accessibility.linux:
    # AT-SPI object tree and event-hook marshalling are loopback-tested on the
    # session bus. Remaining: real a11y registry / Orca signal receipt.
    - text: "Real AT-SPI registry/Orca signal receipt remains pending; per-widget Accessible/Component/Value objects and event-hook marshalling are loopback-tested on the session bus."
      tracked_in: "planning/production-readiness/04-accessibility.md#4.2"

# ── Schema-v2 additions ──────────────────────────────────────────────────────
#
# Widgets: inventory every widget in core/view/include/pulp/view/ so the
# consistency checker can cross-reference capabilities.md and catch stale
# widget tables. Status reflects the widget class itself, not the theme/
# platform surface it renders on.
widgets:
  label:            { status: usable, header: widgets.hpp }
  knob:             { status: usable, header: widgets.hpp }
  fader:            { status: usable, header: widgets.hpp }
  toggle:           { status: usable, header: widgets.hpp }
  checkbox:         { status: usable, header: widgets.hpp }
  toggle_button:    { status: usable, header: widgets.hpp }
  icon:             { status: usable, header: widgets.hpp }
  image_view:       { status: usable, header: widgets.hpp,
                      notes: "File-backed decode through Canvas::draw_image_from_file / draw_image_from_file_rect on supported backends; placeholder fallback when decode/backend support is missing." }
  meter:            { status: usable, header: widgets.hpp }
  multi_meter:      { status: usable, header: widgets.hpp }
  correlation_meter: { status: usable, header: widgets.hpp }
  xy_pad:           { status: usable, header: widgets.hpp }
  waveform_view:    { status: usable, header: widgets.hpp }
  spectrum_view:    { status: usable, header: widgets.hpp }
  spectrogram_view: { status: usable, header: widgets.hpp }
  panel:            { status: usable, header: widgets.hpp }
  combo_box:        { status: usable, header: ui_components.hpp }
  tooltip:          { status: usable, header: ui_components.hpp }
  progress_bar:     { status: usable, header: ui_components.hpp }
  list_box:         { status: usable, header: ui_components.hpp }
  scroll_view:      { status: usable, header: ui_components.hpp }
  text_editor:      { status: usable, header: text_editor.hpp }
  tree_view:        { status: usable, header: tree_view.hpp }
  preset_browser:   { status: usable, header: preset_browser.hpp }
  midi_keyboard:    { status: usable, header: midi_keyboard.hpp }
  waveform_editor:  { status: usable, header: waveform_editor.hpp }
  eq_curve_view:    { status: usable, header: eq_curve_view.hpp }
  file_browser:     { status: usable, header: file_browser.hpp }
  file_drop_zone:   { status: usable, header: file_drop_zone.hpp }
  code_editor:      { status: usable, header: code_editor.hpp }
  breadcrumb:       { status: usable, header: breadcrumb.hpp }
  split_view:       { status: usable, header: split_view.hpp }
  concertina_panel: { status: usable, header: concertina_panel.hpp }
  toolbar:          { status: usable, header: toolbar.hpp }
  color_picker:     { status: usable, header: color_picker.hpp }
  property_list:    { status: usable, header: property_list.hpp }
  graph_editor_view: { status: usable, header: graph_editor_view.hpp,
                       notes: "Canvas-based node editor driving SignalGraph." }
  canvas_widget:    { status: usable, header: canvas_widget.hpp }
  table:
    status: partial
    header: table.hpp
    notes: >
      Rows, themed cells, and click-to-sort columns are implemented; built-in
      table scrolling/scrollbar remains planned.
  modulation_matrix:   { status: usable, header: modulation_matrix_widget.hpp,
                          notes: "Source-to-destination route widget backed by ModulationMatrix; focused data-model and widget interaction tests exist." }
  ab_compare:          { status: planned, notes: "Planned A/B comparison widget." }
  resizable_shell:     { status: planned, notes: "Planned documented resizable-shell pattern." }

# Plugin formats beyond audio host integration.
ara:
  status: experimental
  notes: >
    SDK-gated companion-factory hooks exist in CLAP, AUv3, and VST3. Without
    PULP_ENABLE_ARA, ara_companion_factory_for() returns nullptr and hosts treat
    plugins as non-ARA. With the SDK enabled, the published factory is an
    ABI-valid scaffold with no-op document-controller callbacks while
    production-readiness workstream 06 finishes real ARA 2.x behavior across
    VST3 + AU + CLAP.
  tracked_in: "planning/production-readiness/06-ara.md"

# Accessibility feature coverage (beyond platform bridge existence). This is
# separate from platform_maturity.accessibility.* which records per-platform
# bridge status. The entries below describe which cross-platform a11y
# primitives are uniformly exposed.
accessibility_features:
  roles_labels_values:
    status: usable
    notes: >
      Validated by test/test_platform_maturity.cpp, test/test_view.cpp,
      test/test_accessibility_tree.cpp, and test/test_widget_bridge_html_aria.cpp
      for View roles/labels/values, accessibility snapshots, and HTML/ARIA slot
      routing; native bridge source maps these slots on macOS/iOS/Android/Windows/Linux.
  slider_increment_decrement:
    status: partial
    notes: >
      iOS/Android source bridges route VoiceOver/TalkBack increment/decrement
      actions to on_accessibility_adjust(float). Direct platform-action
      regression tests and uniform macOS/Windows/Linux action semantics are still
      pending.
  actions:
    status: planned
    notes: Per-widget std::vector<Action> exposure planned.
  live_region_announce:
    status: partial
    notes: >
      announce_accessibility(text, priority) and set_announcement_sink() are
      implemented and covered by pulp-test-announce. Built-in platform bridges
      do not install live-region sinks yet, so calls log/no-op unless a host or
      platform backend supplies one.
  tree_invalidation_events:
    status: partial
    notes: >
      Cross-platform provider event helpers exist, Windows raises UIA
      structure/value/focus/name events, and Linux rebuilds AT-SPI trees plus
      marshals value/focus/name events. test/test_accessibility_provider.cpp
      and test/test_accessibility_tree.cpp cover the no-op/live helper paths.
      macOS/iOS/Android notification parity and real client signal receipt are
      still pending.
  a11y_test_harness:
    status: partial
    notes: >
      Headless cross-platform snapshots are covered by
      test/test_accessibility_tree.cpp, and Linux has a session-bus AT-SPI
      loopback harness. Direct per-platform assistive-client harnesses remain
      planned.
