<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>iridis: changelog</title>
    <link>https://studnicky.github.io/iridis/</link>
    <description>Chromatic pipeline for dynamic palette derivation. Pluggable, OKLCH-native, contrast-enforced.</description>
    <language>en-US</language>
    <atom:link href="https://studnicky.github.io/iridis/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>iridis 0.4.5</title>
      <link>https://studnicky.github.io/iridis/#0.4.5</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.4.5</guid>
      <pubDate>Tue, 19 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[### Fixed

- **Image-mode theming** now drives the docs cascade end-to-end. `ImageToTheme` was reading `state.metadata['gallery'].dominantColors` and `.histogram`, but the engine writes outputs to the colon-prefixed `state.metadata['gallery:dominantColors']` and `state.metadata['gallery:histogram']` keys. The extractor silently produced an empty seed list every run, so `configStore.paletteColors` never updated and dropping an image or picking a preset left every `--iridis-*` variable on the page unchanged. The component now reads the canonical keys; the spectrograph and the docs-wide theme both track the extracted palette and re-resolve through dark/light framing toggles.
- **WCAG / APCA role badges** in `BuildResolvedRoles` were sourced from a non-existent `state.metadata['wcag']` blob. The contrast plugin writes three separate keys — `state.metadata['contrast:aa']`, `['contrast:aaa']`, `['contrast:apca']`. `roleBadge` now reads each key directly so the AA / AAA / APCA·Bronze indicators render again.]]></description>
    </item>
    <item>
      <title>iridis 0.4.4</title>
      <link>https://studnicky.github.io/iridis/#0.4.4</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.4.4</guid>
      <pubDate>Mon, 18 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[### Changed

- **README structure** trimmed to match the json-tology pattern: composite header SVG up top, then concise Documentation / Requirements / Install / License / Changelog sections. The long-form usage walkthroughs that previously lived in the README are kept on the docs site.
- **Composite header SVG** (`docs/public/readme-header.svg`) replaces the standalone `iridis-node.svg`. The new SVG is 1280×320, base64-embeds `docs/public/logo.png` on the left, and renders the wordmark + version pill + tagline + palette swatches on the right. Single asset, self-contained, used by both the README and the release-publish workflow.

### Removed

- `docs/public/iridis-node.svg` and its template — superseded by `readme-header.svg`.]]></description>
    </item>
    <item>
      <title>iridis 0.4.3</title>
      <link>https://studnicky.github.io/iridis/#0.4.3</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.4.3</guid>
      <pubDate>Mon, 18 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[### Fixed

- **README header image now uses the sidebar logo.** v0.4.2 shipped with a brand-new hex-node SVG drawn from the favicon vector. The README image is supposed to match the rest of the site — every other surface (VitePress sidebar, favicon, apple-touch-icon, msapplication-TileImage) renders `docs/public/logo.png`. The versioned SVG now base64-embeds `logo.png` directly so the README, GitHub release pages, and the docs site all show the same artifact. Version pill below the image unchanged.]]></description>
    </item>
    <item>
      <title>iridis 0.4.2</title>
      <link>https://studnicky.github.io/iridis/#0.4.2</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.4.2</guid>
      <pubDate>Mon, 18 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Docs and release-pipeline polish.

### Added

- **Versioned README header.** Replaces the static logo with a brand-styled `iridis-node` hex SVG that carries the current release version in a pill below the wordmark. The SVG is referenced via `raw.githubusercontent.com` so it renders correctly in the README on GitHub and in release-notes bodies.
- **Release-publish workflow** (`.github/workflows/release.yml`). Fires on any `v*` tag push: verifies the stamped SVG matches the tag, extracts the matching `## [<version>]` section from `CHANGELOG.md`, and creates or updates the GitHub release with a body that embeds the per-tag SVG URL. Historical release pages render the version that was tagged at that moment rather than always-latest. Supports `workflow_dispatch` for manual republish.
- **`scripts/stamp-version.mjs`** reads `packages/core/package.json` and stamps every `docs/public/*.svg.template` into its sibling `.svg` with the current version. `--check` flag is the CI drift guard. Wired into `predocs:build` so the docs site always carries a fresh stamp.
- **Self-contained `og-image.svg`.** Embeds the favicon vector inline so the SVG renders correctly when uploaded as the GitHub social preview or fetched by link-unfurl bots that don't co-locate adjacent assets.]]></description>
    </item>
    <item>
      <title>iridis 0.4.1</title>
      <link>https://studnicky.github.io/iridis/#0.4.1</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.4.1</guid>
      <pubDate>Mon, 18 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[### Fixed

- **Peer-dependency ranges in plugin packages** updated to `^0.4.0` so workspace resolution links `@studnicky/iridis` correctly under `npm ci`. The 0.4.0 release shipped with plugins declaring `^0.3.0`, which made `npm ci` fall through to the public npm registry (where the package is unpublished) and 404. CI build for the docs site failed as a result; this release fixes the workspace-internal linking.]]></description>
    </item>
    <item>
      <title>iridis 0.4.0</title>
      <link>https://studnicky.github.io/iridis/#0.4.0</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.4.0</guid>
      <pubDate>Mon, 18 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Schema-first engine foundation, flat slot grammar, and full test refactor. Breaking on plugin slot names and the `PluginOutputsRegistry` / `PluginMetadataRegistry` module-augmentation pattern.

### Breaking

- **Flat colon-namespaced slot grammar.** `state.outputs` and `state.metadata` use a single top-level key per slot, namespaced by plugin (`capacitor:statusBar`, `vscode:themeJson`, `stylesheet:cssVars`, `rdf:reasoningGraph`, `gallery:histogram`, `contrast:aa`, `core:json`, etc.). The previous namespace-object pattern (`state.outputs['capacitor'].statusBar`) is gone. User configs map slot keys to filenames 1:1 in `output.files`.
- **`declare module '@studnicky/iridis'` augmentation pattern deleted.** Plugins now expose `schemas(): { outputs?: Record<string, JSONSchema>, metadata?: Record<string, JSONSchema> }` on `PluginInterface` to declare their slot shapes. The engine validates `state.outputs[slot]` and `state.metadata[slot]` against the contributing plugin's schema at run exit.
- **Typed intakes throw on non-conforming input.** `intake:hex`, `intake:rgb`, `intake:hsl`, `intake:oklch`, `intake:lab`, `intake:p3`, `intake:named`, `intake:imagePixels` reject malformed input with a descriptive error. `intake:any` keeps polymorphic dispatch — iterates delegates, first non-null parse wins, throws only if no delegate matches.

### Added

- **ajv-backed validator** with per-engine compile cache. Replaces the hand-rolled draft-07 walker. `Validator` is now a thin wrapper around ajv.
- **`json-schema-to-ts`** derives TS interfaces from `as const` schema literals via `FromSchema`. JSON Schema is the source of truth for every boundary type.
- **`TaskManifestSchema`** validates every adopted task's manifest at `Engine.adopt`, again at `Engine.pipeline`, and at `Engine.run` entry. Defense in depth.
- **Upstream `output.files` validation** at `Cli.run`: rejects configs that declare a slot no pipeline task writes. The error names the missing slot and lists the slots that ARE produced.
- **Bundle aggregator pattern** (opt-in): plugins may ship an `emit:<plugin>Bundle` task that reads sibling slots and writes one combined slot for users who want one file containing the whole plugin namespace.
- **Scenario-matrix test suites** across all 9 packages. Cells, coordinate-tagged assertion messages, happy/edge/unhappy scenarios per subject. ~700 scenarios workspace-wide.

### Fixed

- **VSCode P3 leak.** `EnsureContrast.apply` now preserves the source gamut. sRGB-sourced foregrounds (hex inputs) round-trip through contrast adjustment as sRGB instead of being re-stamped as `displayP3`. VSCode theme slots like `gitDecoration.addedResourceForeground` emit `#rrggbb` hex for sRGB inputs instead of `color(display-p3 …)`.

### Removed

- **Dead `requires` runtime guards** in vscode tasks (`ApplyModifiers`, `EmitVscodeSemanticRules`, `EmitVscodeThemeJson`). `Engine.pipeline` validates these declarations at build time; the runtime throws were unreachable.
- **`PluginOutputsRegistry` / `PluginMetadataRegistry`** TypeScript registry interfaces. Plugin output and metadata shapes now flow through schemas, not module augmentation.

### Dependencies

- `@studnicky/iridis` (core) adds runtime deps: `ajv@^8.20.0`, `json-schema-to-ts@^3.1.1`. The "zero runtime dependencies" claim is gone.]]></description>
    </item>
    <item>
      <title>iridis 0.3.6</title>
      <link>https://studnicky.github.io/iridis/#0.3.6</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.3.6</guid>
      <pubDate>Sun, 17 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Docs site refactor. Eliminates the entire import-map fragility class.

### Removed

- **CDN externalisation of Vue, PrimeVue, `@primeuix/themes`, and mermaid.** The site used to load these from `esm.sh` at runtime via an import-map declared in `<head>`; that approach forced a fragile head-ordering dance (see v0.3.4 / v0.3.5) because VitePress 1.x emits its own `<script type="module">` and modulepreload links BEFORE any `head` config entry, and Vite's own `transformIndexHtml` does not fire on VitePress's SSG output. None of the other VitePress sites in this workspace use the CDN pattern. Trading the small bundle-size saving for HTML-spec compliance, predictable production behaviour, no cross-origin handshake to `esm.sh`, no corporate-firewall failures, and no future-VitePress-upgrade breakage.
- `CDN_VERSIONS`, `CDN_PRIMEVUE_SUBPATHS`, `CDN_EXTERNAL_PATTERNS`, `IS_BUILD`, `buildImportMap`, `vite.build.rollupOptions.external`, and the `transformHtml` hook are all removed from `docs/.vitepress/config.ts`.

### Changed

- **VitePress 1.5.0 → 1.6.4** (current stable, was already in the lockfile via `^1.5.0`; range explicit now).
- **Vue 3.5.0 → 3.5.34** (current stable).
- **mermaid 11.14.0 → 11.15.0** (current stable).]]></description>
    </item>
    <item>
      <title>iridis 0.3.5</title>
      <link>https://studnicky.github.io/iridis/#0.3.5</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.3.5</guid>
      <pubDate>Sun, 17 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Cleanup of v0.3.4.

### Changed

- **Import map injection is now declarative.** Replaces the v0.3.4 regex-based move-after-emit hack with a direct `transformHtml` insertion at the top of `<head>` (`String.replace('<head>', ...)`). VitePress 1.x has no position-controlled head-injection API, and Vite's own `transformIndexHtml` hook does NOT fire on VitePress's SSG-rendered output, so `transformHtml` remains the source-of-truth hook. `buildImportMap` now returns a typed `Record<string, string>` instead of pre-stringified JSON; the `transformHtml` hook wraps it in the `<script type="importmap">` tag at insertion time.
- **Engine logger silenced on the docs site.** `consoleLogger.level = 'error'` set at theme boot in `docs/.vitepress/theme/index.ts`. The engine's CVD-advisory and missing-role warnings fire on every projector run (hundreds per session as the user edits the palette) and aren't actionable from inside the docs. Library consumers configure their own level; this only affects the docs site.]]></description>
    </item>
    <item>
      <title>iridis 0.3.4</title>
      <link>https://studnicky.github.io/iridis/#0.3.4</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.3.4</guid>
      <pubDate>Sun, 17 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Hotfix.

### Fixed

- **Production import map ordering.** VitePress emits its `<script type="module" src="app.js">` and modulepreload links into `<head>` BEFORE the entries declared in the `head` config. Per the HTML spec, an import map MUST appear before any module script or modulepreload anchor; otherwise the browser parses it too late and silently ignores it. Without the fix the live site rendered the SSR shell but failed to mount `BuildPanel` and PrimeVue, because every bare specifier (`vue`, `primevue/*`, `@primeuix/themes`) resolved to a relative path like `/iridis/vue` and 404'd. A new `transformHtml` hook in `docs/.vitepress/config.ts` pulls the import map out of its emitted position and reinserts it immediately before the first module-script or modulepreload anchor. Verified in built dist: import map now on line 12, app script on line 13.]]></description>
    </item>
    <item>
      <title>iridis 0.3.3</title>
      <link>https://studnicky.github.io/iridis/#0.3.3</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.3.3</guid>
      <pubDate>Sun, 17 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Docs minor with one breaking URL change.

### Added

- **PrimeVue Accordion in the reference panels.** `BuildImageOptionsGuide`, `BuildEngineKnobsGuide`, and `BuildRoleSchemaGuide` now render one accordion panel per knob instead of a flat list. Single-select (default); the leg label sits on the left, the toggle indicator on the right, full row is the click target. Initial state opens the first panel so first paint is never blank.

### Changed

- **Home page consolidates the project pitch + builder.** `AuroraHero` sits above the `BuildPanel`; the prose (what you get, when to install, where to look next) sits below. One scrollable surface instead of two cross-linked pages.
- **`BuildPanel` header replaced by a connected tab bar.** The page title and subtitle are gone; the tabs themselves now act as the panel header with the `Reset to defaults` button sitting in the same row at the right. CSS gives the tab bar and the panel body shared borders and rounded corners so they read as one card.
- **Project-wide copy scrub.** Every em-dash (`—`) in human-readable surfaces (markdown docs, Vue components, TypeScript JSDoc, CSS comments, CHANGELOG, README, llms.txt, manifest, package.json description fields, RSS feed template, SEO meta-tag templates) replaced with context-appropriate punctuation. `ConsoleLogger`'s ` — ` field separator swapped for ` | ` with the golden test fixtures updated in lockstep. AI-isms (3 hits across the project: `seamlessly`, `elevated`, and one in the VSCode plugin) rewritten or cut. En-dashes in numeric ranges and proper-name citations preserved.

### Removed

- **BREAKING: `/about` and `/build` routes deleted.** Both URLs return 404 now. The content lives on the home page (`/`). Links in the sidebar, the navbar builder toggle, the `try-it-out` page, the `image-to-theme` page, and the SEO sitemap have all been updated to point at `/`.]]></description>
    </item>
    <item>
      <title>iridis 0.3.2</title>
      <link>https://studnicky.github.io/iridis/#0.3.2</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.3.2</guid>
      <pubDate>Sun, 17 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Docs minor.

### Added

- **`BuildImageOptionsGuide.vue`**: reference panel paired with the Image-tab sliders. One block per extractor knob (algorithm, palette size, histogram bpc, ΔE input cap, harmonize, lightness range, chroma range) with short summary + long explanation + hover-detail. Same shape as `BuildRoleSchemaGuide`.
- **`BuildEngineKnobsGuide.vue`**: reference panel paired with the Configuration-tab knob grid. One block per engine knob (framing, color space, algorithm, contrast level, envelope mode, role schema) with the same structure.
- **Famous-photo preset library.** Six public-domain photos hosted on `upload.wikimedia.org`: *Great Wave* (Hokusai, c. 1831), *Starry Night* (Van Gogh, 1889), *Earthrise* (Apollo 8, 1968), *Blue Marble* (Apollo 17, 1972), *Pillars of Creation* (Hubble, 2014), *Carina Nebula* (JWST, 2022). The iridis logo stays as the project reference.
- **Mount-time preset availability probe.** Every preset URL is loaded via an `Image()` element with `onload`/`onerror` and an 8s timeout; unreachable URLs are filtered from the chip row and logged via `console.warn` so misconfiguration surfaces in dev-tools without breaking the page.

### Changed

- **Image-tab right column layout.** Histogram on top spans the column; below, a sub-grid pairs a small `BuildImageOptionsGuide` with the controls stack (algorithm `SelectButton` + slider channels). Each slider carries a `title` tooltip matching the guide's inline explanation.
- **Configuration-tab layout.** Mirrors the Role-schema tab: `BuildEngineKnobsGuide` on the left, knob grid on the right, stacking single-column below 920px.]]></description>
    </item>
    <item>
      <title>iridis 0.3.1</title>
      <link>https://studnicky.github.io/iridis/#0.3.1</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.3.1</guid>
      <pubDate>Sun, 17 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Patch.

### Fixed

- **`ImageToTheme.vue` source-mode picker placement.** The source-mode SelectButton (File / URL / Preset) and its input row sit on the LEFT column under the image drop zone, mirroring `IridisPicker`'s mode-tabs slot. Right column owns output configuration only: histogram, algorithm SelectButton, sliders.]]></description>
    </item>
    <item>
      <title>iridis 0.3.0</title>
      <link>https://studnicky.github.io/iridis/#0.3.0</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.3.0</guid>
      <pubDate>Sat, 16 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Pre-alpha. Image clustering primitives, consolidated `/build` workspace, unified SEO/structured-data template.

### Added

- **`gallery:histogram` task** (`@studnicky/iridis-image`). 5-bits-per-channel weighted histogram over `state.colors`; emits one record per non-empty bin with `hints.weight` set to the pixel count. Pair upstream with `intake:imagePixels` and downstream with `gallery:extract`.
- **`clusterMedianCutWeighted` math primitive** (`@studnicky/iridis`). Generalises `clusterMedianCut` to respect per-record `hints.weight`; bucket splits partition by cumulative weight (not count), so a heavily-weighted color survives reduction even when surrounded by low-weight neighbours. Selection score is `weight × widest_range` (minimum-within-cluster-error heuristic).
- **`clusterDeltaEMerge` math primitive** (`@studnicky/iridis`). Agglomerative ΔE2000 clustering. Each input record starts as its own cluster; merges the closest pair until exactly K remain. Forward-progress guard handles all-NaN distance pathology. Output `hints.weight` is the merged cluster's total.
- **`gallery:extract` algorithm dispatch** (`@studnicky/iridis-image`). New `metadata.gallery.algorithm` slot selects `'median-cut'` (default; dispatches to `clusterMedianCutWeighted` when any input carries a weight, else `clusterMedianCut` for plain palettes) or `'delta-e'` (pre-trims by descending weight to bound the agglomerative reducer's input).
- **`/build` workspace consolidation.** Five-tab workbench at `/iridis/build` (User palette / Image / Role schema / Configuration / Code) sharing one `configStore` so every tab edits the same SPA-wide theme. `BuildResolvedRoles` grid renders below the tabs as the canonical live read-out.
- **Standalone live-demo pages.** `/iridis/try-it-out` and `/iridis/image-to-theme` render the seed-picker and image-extraction workflows respectively, each followed by the shared resolved-roles grid. Both add to the sidebar.
- **Unified SEO + structured-data template** (`docs/.vitepress/config.ts`). `BreadcrumbList` JSON-LD on every page, `Organization` JSON-LD with `sameAs` to GitHub + npm + author, `HowTo` JSON-LD gated to `/recipes/*`, `article:modified_time` + `article:author` when git lastUpdated resolves. `preconnect` + `dns-prefetch` to `esm.sh` for LCP improvement. `hreflang` en-US + x-default. Bingbot directive. Referrer policy `origin-when-cross-origin`.
- **PWA manifest** (`docs/public/manifest.webmanifest`). Icons, screenshots, theme color, scope, start_url.
- **RSS feed** (`docs/.vitepress/dist/feed.xml`, generated at build-end). Parses `CHANGELOG.md` versions into RSS 2.0; linked from `<head>` via `rel=alternate type=application/rss+xml`.
- **`llms.txt`** (`docs/public/llms.txt`). Markdown index of canonical URLs per llmstxt.org standard, for LLM crawlers (ChatGPT, Perplexity, Claude).
- **Per-page `description:` frontmatter.** Every route under `/concepts`, `/recipes`, `/reference`, plus the top-level pages, ships a unique 110-160 char declarative description; SERP previews and unfurl cards no longer share the site-level fallback.
- **`iridis.seo` config in `package.json`.** Single namespace for Google Search Console + Bing Webmaster Tools verification tokens and the X/Twitter handle. All three are explicitly-public markers, not credentials. Empty string suppresses the corresponding tag.
- **Reactive `appliedRoles` projection** (`docs/.vitepress/theme/stores/applyConfigToDocument.ts`). The projector publishes the role → hex map atomically on every successful `engine.run`, so downstream consumers (code-snippet panel, role cards) subscribe via Vue reactivity instead of polling `document.documentElement.style`.
- **Docs hero + atmosphere components.** `AuroraHero`, `ColorOrgan`, `IridisCursorBlob`, `IridisSwatchTape`, `PaletteCTA`: decorative components composed by `about.md` and the global layout slots. All respect `prefers-reduced-motion`.
- **Reference: role-schema field pages.** `/iridis/reference/role-schema/{name, intent, lightness-range, chroma-range, derived-from, hue-lock, required, contrast-pairs}`: one page per schema field with shape, semantics, and examples.

### Changed

- **`/build` is the new docs home.** `docs/index.md` renders `<BuildPanel />`; the legacy two-panel layout is consolidated into the tabbed workspace.
- **Builder defaults strict-by-default.** `contrastLevel: 'AAA'`, `contrastAlgorithm: 'apca'` in both the JSON Schema spec and the runtime `docsConfigDefaults`. First-visit users get the most rigorous accessibility audit the engine can run.
- **Plugin peer-dep ranges bump to `^0.3.0`.** Every first-party plugin (`@studnicky/iridis-{capacitor,cli,contrast,image,rdf,stylesheet,tailwind,vscode}`) declares `@studnicky/iridis: ^0.3.0` as its peer dependency.
- **`hasAnyWeight` predicate semantics** (`gallery:extract` dispatcher). Presence of `hints.weight` is the signal; the previous predicate excluded `weight === 1` and silently fell back to `clusterMedianCut`, losing the weight bookkeeping a histogram with uniform-weight bins had set up.

### Fixed

- **`<title>` tag duplication on home + about.** Home page (`title: iridis`) used to render as `iridis | iridis` because `titleTemplate: ':title | iridis'` appended unconditionally; about page (`title: About iridis`) used to render as `iridis | iridis` because VitePress derived the title from the `<h1>iridis</h1>` inside `<AuroraHero>` over the frontmatter. `transformPageData` now forces `pageData.title` from frontmatter and sets `pageData.titleTemplate = false` when the page title equals the site title.
- **Empty `og:description` + `twitter:description` on every page lacking frontmatter description.** `transformPageData` used `??` to coalesce, but VitePress sets `pageData.description = ''` (empty string, not undefined) when no description is supplied, so `??` passed through. Switched to `||` with explicit frontmatter extraction.
- **`ImageToTheme.vue` duplicate source picker.** The LEFT column duplicated the source-mode `SelectButton`, file input, and `ref="fileInput"` already present in the RIGHT column: two hidden file inputs with the same template ref, two visible mode pickers. LEFT column is now drop-zone + preview only.
- **`BuildCodePanel.vue` reactivity gap.** Module-scope `void themeStore` never established a reactive dependency; the CSS-vars snippet read from `document.documentElement.style` which the async projector updates after the watch fires (race condition). Computed now reads the new reactive `appliedRoles` ref directly.
- **`ClusterDeltaEMerge` pass-through preserves wide-gamut.** When `k >= colors.length`, the pass-through used to reallocate every record via `fromOklch`, stripping `displayP3` (re-derived only when out of sRGB) and dropping non-weight hint keys. Now returns inputs verbatim when a weight is already declared; otherwise reallocates via the factory with merged hints.
- **`ClusterMedianCutWeighted` type-cast bypasses.** Three `as ColorRecordInterface` casts replaced with `for...of` guards consistent with the rest of the file.
- **Stale comments referencing the deleted `RightPanel.vue`.** Comments in `MultiOutputDemo.vue`, `SidebarResize.vue`, `theme/index.ts`, and `base/IridisCard.vue` rewritten to reflect current state.

### Removed

- **`docs/.vitepress/theme/components/RightPanel.vue`**: split into `BuildPanel` (tabbed workbench) + `BuildResolvedRoles` (shared resolved-roles grid).
- **`docs/.vitepress/theme/components/TryItOutForm.vue`**: replaced by `IridisDemo` standalone on `/try-it-out`.
- **`docs/.vitepress/theme/stores/panelState.ts`**: the consolidated `/build` workspace has no right-panel-toggle state to track.]]></description>
    </item>
    <item>
      <title>iridis 0.2.0</title>
      <link>https://studnicky.github.io/iridis/#0.2.0</link>
      <guid isPermaLink="false">https://studnicky.github.io/iridis/changelog/0.2.0</guid>
      <pubDate>Fri, 15 May 2026 12:00:00 GMT</pubDate>
      <description><![CDATA[Pre-alpha. First wide-gamut + ontology-driven release.

### Added

- **Cross-plugin Display-P3 propagation.** Stylesheet (scoped + unscoped), Tailwind theme, VS Code theme JSON, and the RDF reasoning graph all emit P3 forms when an input role carries populated `displayP3`. Capacitor stays sRGB per its native OS surface limits.
- **`intake:p3` task.** Parses CSS `color(display-p3 r g b [/alpha])` strings. `intake:any` dispatches P3 strings to it automatically. `SourceFormatType` extends with `'displayP3'`.
- **OKLCH gamut handling in `ColorRecordFactory.fromOklch`.** Detects out-of-sRGB input via the new `GamutMapSrgb` primitive (CSS Color 4 §13.2.2 binary-search chroma reduction); populates `displayP3` from the unclipped original; populates `rgb` from the gamut-mapped value so sRGB consumers stay safe.
- **`OklchToDisplayP3` math primitive.** Class + singleton, cites Björn Ottosson OKLab + CSS Color 4 §17.6.
- **`@supports`-wrapped P3 cascade in `EmitCssVars` + `EmitCssVarsScoped`.** Emitted CSS orders blocks `:root` → `@media (prefers-color-scheme: dark)` → `@supports (color: color(display-p3 0 0 0))` → `@media (forced-colors: active)`.
- **Research-grounded CVD compliance.** `EnforceCvdSimulate` evaluates every pair against all four canonical CVD types (deuteranopia, protanopia, tritanopia, achromatopsia). Per-type thresholds in `cvdThresholds.ts` cite BVM97, MOF09, VBM99, CIE76, SWD05, WCAG21, WS82. Bipartite signal: warning fires on either `|drop|` exceeding `dropMagnitude` OR `simulatedContrast` falling below the WCAG 1.4.11 floor. Output preserves shape; adds `dropThreshold` + `minSimulatedContrast` for auditability.
- **Structured logger.** `ConsoleLogger` channels accept `(scope, op, message, context?: Record<string, unknown>)`. The logger formats; callers never interpolate. Level evaluated first so suppressed calls return before any context object is touched. New `trace` channel. Order: `silent < error < warn < info < debug < trace`.
- **Ontology-driven role intent.** `ResolveRoles` propagates `RoleDefinitionInterface.intent` onto resolved `ColorRecordInterface.hints`. `EmitCssVars.forcedColorsToken` switches on `hints.intent`; no substring inference on role names. APCA `requiredLc`, WCAG required ratio, and Capacitor StatusBar style all read the same intent slot. `RoleSchemaEditor`'s intent picker exposes the canonical 10-value `ColorIntentType` union.
- **Canonical `ColorIntentType` union.** Ten values: `text | background | accent | muted | critical | positive | link | button | onAccent | onButton`.
- **Reusable docs components.** `RoleCard`, `PairCard`, `ResolvedRoleCard`, `PaletteSwatch`, `PaletteEditor`, `FormField`, `ExportBar`. All form fields surface native `title` tooltips explaining their purpose.
- **`xState`-style dispatcher actions.** `editRoleSchema` action publishes user-edited schemas as `custom-<timestamp>` entries in `roleSchemaByName`. Components dispatch typed actions; the dispatcher owns the registry shape.
- **CDN externalisation for docs heavy deps.** Vue, PrimeVue (14 subpaths), `@primeuix/themes`, and mermaid load from esm.sh via a `<head>` import map. Theme chunk 582 → 175 KB. Vitepress build emits zero warnings.
- **Plugin type re-exports.** Every plugin re-exports its `augmentation.ts` slot interfaces via `src/types/index.ts`; every plugin's `package.json` exposes `./types` in its exports map.
- **Math primitive consistency.** `oklchToRgbRaw`, `clamp01`, `clamp` promoted from free functions to class + singleton (`OklchToRgbRaw`, `Clamp01`, `Clamp`) matching the project's `<Name> { apply(...) }` pattern.
- **Test coverage extended to 220 scenario-runner tests.** Wide-gamut, CVD compliance, intake-any dispatcher, golden fixtures (`quickPalette`, `emit-cssVars`, `reason-serialize`), structured-logger zero-work-at-suppressed-levels, role-intent propagation, plus the original critical-path coverage.

### Changed

- **`ColorIntentType` union trimmed to 10 canonical values.** Legacy values `base`, `surface`, `neutral` removed; consumer schemas migrated (`base`/`surface` → `background`, `neutral` → `muted`).
- **`ColorRecordInterface.displayP3` semantics.** Populates only when the input OKLCH lies outside sRGB-gamut OR the record arrived via `intake:p3`; stays `undefined` for sRGB-representable inputs. `ColorRecordInterface.rgb` is always sRGB-safe (gamut-mapped from OKLCH when needed).
- **`ColorRecordInterface` shape is monomorphic.** Every allocation produces the same V8 hidden class. `displayP3` + `hints` slots are required `T | undefined`, written explicitly by the factory.
- **`Engine` caches resolved task sequence on `pipeline()`.** Invalidated on `adopt()`.
- **`ConsoleLogger` is a module singleton.** `Engine.run` no longer allocates a fresh logger per call.
- **`EnsureContrast` mutates an OKLCH `L` scalar.** Single `ColorRecord` allocation at return (was up to 50 per failing pair).
- **`MultiOutputDemo.vue` plugin imports are dynamic.** Theme chunk drops 207 KB; lazy chunks load on mount.
- **`iridis-8` dark mode `on-brand` lightness range.** Flipped from `[0.96, 1.00]` (white) to `[0.04, 0.14]` (dark) so the brand pair always reaches 4.5:1. Light mode unchanged.

### Fixed

- **Workspace `tsc` gate.** Root `tsconfig.json` was previously bypassed by `tsc --noEmit`; `tsc --build` now drives every package reference.
- **`Engine.adopt` plugin shape validation.** Hand-rolled JSON Schema walker at `core/src/model/Validator.ts` validates `PluginInterface` at adoption. Duplicate plugin `name` adoption warns.
- **`Engine.pipeline` enforces `manifest.requires`.** A task that requires another task must appear after it; declared-out-of-order throws.
- **`state.graph` relocated to RDF plugin namespace.** Canonical `PaletteStateInterface` no longer carries a graph slot; RDF reads/writes `state.outputs.reasoning.graph` via plugin augmentation.
- **`MathPrimitiveInterface` + `ColorMathRegistry` removed.** Plugins import singletons directly; engine no longer carries a math registry.
- **`SrgbToDisplayP3` + `DisplayP3ToSrgb` deleted.** Zero internal consumers; replaced by `OklchToDisplayP3` + the inverse chain inside `IntakeP3`.
- **`RoleSchemaEditor` no longer mutates the registry directly.** Edits dispatch through `editRoleSchema`; the dispatcher owns the `{ dark, light }` pair shape so downstream consumers see a complete entry.
- **Right panel widens to 50 % of the viewport** (was capped at 720 px).
- **Vitepress logo path resolution.** Asset paths switched `/iridis/logo.png` → `/logo.png` so VitePress applies the base prefix at build time.

### Removed

- **Legacy `ColorIntentType` values:** `base`, `surface`, `neutral`.
- **`base/IridisInput.vue` + `base/IridisSelect.vue`**: dead wrappers, never consumed.
- **`SrgbToDisplayP3` + `DisplayP3ToSrgb` math primitives**: superseded by `OklchToDisplayP3` + `IntakeP3`.
- **Substring-based forced-colors token inference.** Ontology is the contract.]]></description>
    </item>
  </channel>
</rss>
