Palette alpha versus native key identity — 2026-10-20

Partial contract resolved; D3D-ALPHA-KEY-PRECEDENCE-20261014 remains blocked on a native observation or a version-specific normative statement. No renderer policy changed. The unknown is whether and how an effective source key operates on an alpha-bearing DirectDraw palette, not whether its fourth byte contains alpha or whether two equal-RGB indices are the same index.

Base: 63d22caf879ca77d891d284f7926416a210136c2, independently observed at remote main before work. Read the October 14 palette-alpha-render and legacy-key-filter-contract reports and the current TODO. Current main, not the older shared checkout, supplies all tested source. Filtered/state144 policy belongs to the parallel task and is not investigated here.

Narrow contract table

API/version and exact format Determinate contract Limit
DirectDraw7 texture, DDPF_PALETTEINDEXED8, 8-bit indices, attached 256-entry DDPCAPS_8BIT palette with DDPCAPS_ALPHA Each four-byte PALETTEENTRY supplies RGB and an 8-bit alpha through peFlags. Values 0, 1, 64, 127, 128, 255 remain distinct alpha values. Alpha palettes attach only to DDSCAPS_TEXTURE surfaces. Microsoft CreatePalette This API contract does not specify precedence with COLORKEYENABLE.
Same indexed format, ordinary palette without DDPCAPS_ALPHA The fourth byte is not enabled palette alpha; do not reinterpret ordinary palette flags as transparency. The fixture verifies current opaque expansion separately. GetCaps This does not qualify every ordinary palette flag combination or attachment lifecycle.
DX6-era source key on a palettized texture, single low=high key The native key value denotes an index. Equal RGB at indices 7 and 8 does not make index 8 a member of key 7. NVIDIA Color Key in Direct3D, issue five Identity alone does not establish that key rejection is enabled for an alpha palette. No range-key claim.
DX6 palette-alpha availability versus DX5 compatibility Microsoft's contemporary DX6 announcement lists alpha in texture palettes as a new feature. Microsoft, May 1998 Do not project this capability into DX5 from today's aggregated header documentation. No DX5 alpha-palette support claim.
DX6-era truecolor with in-texel alpha, e.g. 1:5:5:5 NVIDIA's issue three says alpha textures use texel alpha and ignore keying; issue four explicitly gives 1:5:5:5 as an alpha format. Vendor implementation guidance, not a universal Microsoft DX6/7 rule. It never names DDPCAPS_ALPHA; extending it to palette alpha would be an inference.
DirectDraw indexed 1/2/4-bit, indexed-to-8, and truecolor alpha Indexed-to-8 is a distinct format; DDPF_ALPHAPIXELS describes alpha in the pixel format. Microsoft DDPIXELFORMAT No packed-index, indexed-to-8, alpha-only, or truecolor-key equality fixture here. The 8-bit fixture cannot qualify them.
D3D9 P8 versus A8P8 P8 is an 8-bit index; A8P8 includes separate 8-bit texel alpha. D3DFORMAT Palette alpha is used only when texture alpha is absent and ALPHAPALETTE is supported; otherwise absent alpha is 255. Texture Palettes D3D9 device palettes are not DirectDraw palette objects. This does not decide legacy key precedence; D3D8 behavior is not established here.
DX6/7 alpha palette, effective key, nonkey alpha below 128 Raw membership is false. The alpha value alone cannot encode membership. Final fragment survival, key/alpha precedence, and color/depth results remain unknown. A membership result is not a universal draw result.

Microsoft's D3DPrimCaps separately names alpha-palette and transparency support. Capability names do not specify simultaneous use. Modern documentation indexed under historical structure names is not proof of introduction date.

The DX5 SDK-text mirror's render-state page was also inspected. It describes alpha-test acceptance, blending, and independent depth writes, but lists an old ALPHAREF representation and ALPHABLENDENABLE number. It is not used as an ABI or palette-alpha oracle. No authenticated original DX6/7 SDK passage resolving the simultaneous case was recovered. No applicable native receipt was found in the supplied palette bundles or bounded palette/native filename search.

Current production behavior, not a normative oracle

At the pinned base, src/09ab-handlers-d3dim-core.wat:749 decodes keyed textures. Mode 1 compares raw indices and clears matching alpha while retaining RGB. Nonmatches retain enabled palette alpha. Mode 2 with an alpha palette traps before decoding; this is an explicit unresolved-contract guard.

The software span at line 5334 has the corresponding explicit-mode guard. Its point-index identity route at line 5342 excludes alpha palettes. Such palettes in ordinary key mode therefore reach the expanded-RGB comparison at line 5540. That is not the corrected ordinary-palette point route and does not preserve duplicate-color index identity.

lib/d3d9-fixed.js:347 emits sample0.a < 0.5 discard before combiners and the application's alpha test. Thus a nonkey alpha of 1, 64, or 127 is discarded even when an application test would accept it; 128 survives this predicate. The shared backend filename does not select a D3D9 contract. Source order shows rejected shader fragments never reach the color output; software gates stencil/depth on its draw decision. These are static observations, not measured framebuffer/depth results.

NVIDIA's note describes a default zero-alpha rejection for non-alpha textures and application alpha-test/blend controls. It cannot justify the fixed half-alpha threshold for alpha palettes, nor settle whether a key match should replace their alpha.

Executed evidence

Run node docs/re-notes/d3d-alpha-key-contract-20261020.js <evidence-directory>.

The fixture compiles the exact current d3dim_texture_fetch_prepared helper and exact raw-index equality expression from the GPU decoder. It runs 24 pairs: six alpha values × ordinary/alpha mode × key/nonkey index, with identical RGB at both indices. All pass; whole fixture memory remains unchanged. No JS replacement of either WAT computation is used. The excluded truecolor decoder traps if reached. A 64-KiB standalone memory and explicit pointers replace the full VM.

Six additional cases execute the current shader predicate as a scalar expression. They reproduce four nonkey discards below 128 and two survivors. These are characterization, with null normative final-draw expectations, not six conformance passes. No GPU is run.

Evidence: scratch/runs/20261020-d3d-alpha-key-contract-oct20/{helpers.wat,helpers.wasm,tests.json,test.log,test.stderr,preflight.json,receipt.json}. Source and extracted-module SHA-256 hashes are in tests.json. The recovery recipe needs only the two committed files and tracked compiler/core/backend at this base. Initial fixture syntax used an unsupported inline memory export and then a bare unreachable; both were corrected before the successful run. No production failure was involved.

Exact missing native observation and stopping condition

Use an authorized original DX6 device interface and separately Device7 on a documented OS/runtime/driver/device that reports and successfully creates alpha-palettized textures. Record version identities, caps, enumerated DDPIXELFORMAT, actual palette creation/attachment flags, HRESULTs, and read-back states. Unsupported/rejected format is evidence of a limitation, not a precedence answer.

Use point sampling at interior texel centers, one stage selecting texture alpha and RGB, no fog/specular/stencil, no border or mixed taps. Set one raw source key (low=high=7) before load. Give indices 7 and 8 identical RGB and independently sweep their alpha through 0,1,64,127,128,255. Include a nonkey whose RGB equals the integer key and an ordinary palette control. Compare effective key disabled/enabled; hold the filtered/state144 question outside this observation.

For each keyed/nonkey case capture:

The unresolved outputs are whether key 7 preserves/replaces/ignores palette alpha, whether nonkey low alpha receives any implicit test, which alpha reaches explicit testing/blending, and whether each outcome writes depth. Do not assign guessed expected pixels to these cells. Version-specific native receipts could establish a bounded observed contract; they would not automatically prove every driver equivalent.

Stop here: no renderer edits, runtime access, browser, heavy build, integration, capability changes, or state144 policy selection.