diff --git a/INTERCHANGE.md b/INTERCHANGE.md index 274954b..9e75d60 100644 --- a/INTERCHANGE.md +++ b/INTERCHANGE.md @@ -83,7 +83,7 @@ Each child reference contains `type` and `id`. The type is `node` or `group`. Al ### 3.3 Nodes and Groups -A node contains its effective semantic kind and nearest containing group. `parentGroupId` is `null` for a root node. `iconId` and `detail` remain `null` when omitted; icon fallback and visual detail treatment are downstream concerns. +A node contains its effective semantic kind and nearest containing group. `parentGroupId` is `null` for a root node. `iconId` preserves either an unnamespaced theme icon or a namespaced provider icon as defined by the language specification, and remains `null` when omitted. Icon resolution, fallback, provider-pack selection, and visual detail treatment are downstream concerns. A group contains its nearest containing group, direct children, and group-scoped layout. `parentGroupId` is `null` for a root group. Group entries use depth-first declaration order: a parent precedes all descendants. diff --git a/README.md b/README.md index 03acd75..a84839b 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ The language is currently a proposal for Stack 1.0. No compatibility guarantee a - [ADR-0002: Make the canonical theme catalog own icons](./docs/decisions/0002-theme-owned-icons.md) - [ADR-0003: Standardize compiler interchange and conformance fixtures](./docs/decisions/0003-standardize-compiler-interchange-and-conformance.md) - [ADR-0004: Distribute shared editor language assets from the specification](./docs/decisions/0004-distribute-editor-language-assets.md) +- [ADR-0005: Separate provider icon namespaces from theme icons](./docs/decisions/0005-provider-icon-namespaces.md) - [Examples](./examples) - [Conformance suite](./conformance) - [`@stack-sh/language`](./packages/language) @@ -39,9 +40,9 @@ diagram "Checkout" { detail "TypeScript" } - node database "Orders" { + node database "Amazon RDS" { kind database - icon "postgresql" + icon "aws:rds" } edge browser -> api "HTTPS" { @@ -79,7 +80,7 @@ npm run pack:check - Keep source deterministic, diffable, and safe to generate. - Make semantic mistakes explicit instead of silently guessing. - Let renderers choose typography, spacing, color, routing, and responsive composition within the selected theme. -- Resolve themes and their one-to-many icon collections from `@stack-sh/theme`. +- Resolve unnamespaced icons from `@stack-sh/theme` and namespaced icons from explicit provider packs. - Evolve additively within a language major version. ## Contributing diff --git a/SPECIFICATION.md b/SPECIFICATION.md index a780518..7d62fe3 100644 --- a/SPECIFICATION.md +++ b/SPECIFICATION.md @@ -133,9 +133,14 @@ An icon identifier is written as a string with this logical form: ```text icon-name +provider:icon-name ``` -The decoded identifier matches `[a-z0-9][a-z0-9-]*` and contains between 1 and 64 ASCII characters. Icon identifiers are case-sensitive and are resolved within the diagram's effective theme. +An unnamespaced identifier matches `[a-z0-9][a-z0-9-]*` and contains between 1 and 64 ASCII characters. It resolves within the diagram's effective theme. + +A provider identifier has exactly one colon. Its namespace matches `[a-z][a-z0-9-]{1,31}` and its icon name matches `[a-z0-9][a-z0-9-]{0,63}`. It resolves only from an explicitly installed provider pack with the same namespace. Provider namespaces do not override unnamespaced theme icons or another provider namespace. + +Icon identifiers are case-sensitive. They are symbolic identifiers, never file paths, package names, or network locations. ## 5. Grammar @@ -278,7 +283,7 @@ A requested theme that is not present in the installed catalog version produces A theme may affect typography metrics and therefore exact element positions, but it MUST NOT change or hide nodes, groups, edges, labels, directionality, semantic kinds, or layout constraints. Every theme MUST preserve legibility, accessible contrast, and non-color distinctions required elsewhere in this specification. -Each theme owns its icon collection. This is a one-to-many relationship: one selected theme resolves zero or more authored icon identifiers to theme-specific SVG assets. The same logical icon may therefore use different SVG artwork in `light`, `dark`, or any other theme. +Each theme owns its unnamespaced icon collection. This is a one-to-many relationship: one selected theme resolves zero or more authored unnamespaced icon identifiers to theme-specific SVG assets. The same logical icon may therefore use different SVG artwork in `light`, `dark`, or any other theme. Namespaced provider icons are resolved from separate explicitly installed provider packs and preserve their provider artwork independently of the selected theme. Stack source cannot define theme values, inherit from a network resource, or add per-element visual overrides. The catalog is explicitly installed or bundled by the renderer and MUST NOT be fetched solely because a theme identifier appears in source. @@ -321,7 +326,7 @@ Examples include `"Next.js"`, `"Order orchestration"`, and `"PostgreSQL 17"`. ### 7.4 Icons -`icon` decorates a node without changing its kind, identity, or accessibility label. It names a logical icon in the diagram's effective theme: +`icon` decorates a node without changing its kind, identity, or accessibility label. An unnamespaced value names a logical icon in the diagram's effective theme: ```stack node database "Primary database" { @@ -330,11 +335,22 @@ node database "Primary database" { } ``` -Every theme in `@stack-sh/theme` MUST provide a fallback visual treatment for every node kind. Named icons beyond those fallbacks are theme-owned. The same icon identifier used by multiple themes MUST represent the same logical subject, but each theme MAY provide different SVG artwork appropriate to its palette and visual system. +A namespaced value selects a product icon from an explicitly installed provider pack: + +```stack +node assets "Amazon S3" { + kind storage + icon "aws:s3" +} +``` + +The node `kind` remains the authored Stack semantic category and MUST NOT be replaced by a provider pack's recommended kind. When provider guidance requires or recommends it, the official product name SHOULD appear in the node label or detail near the icon. + +Every theme in `@stack-sh/theme` MUST provide a fallback visual treatment for every node kind. Unnamespaced named icons beyond those fallbacks are theme-owned. The same unnamespaced icon identifier used by multiple themes MUST represent the same logical subject, but each theme MAY provide different SVG artwork appropriate to its palette and visual system. -If `icon` is omitted, the renderer uses the selected theme's fallback for the node kind. If an authored icon identifier is absent from the selected theme, rendering continues with that fallback and emits warning `STK5001`. +If `icon` is omitted, the renderer uses the selected theme's fallback for the node kind. If an unnamespaced identifier is absent from the selected theme, or a namespaced identifier is absent from the installed provider packs, rendering continues with that fallback and emits warning `STK5001`. -Renderers MUST NOT fetch an icon from an arbitrary network location solely because it appears in source. Icon assets, licensing, validation, caching, and updates are concerns of `@stack-sh/theme`. Renderers SHOULD report the resolved icon identifier and catalog version in output metadata so visual changes can be reproduced. +Renderers MUST NOT fetch an icon from an arbitrary network location solely because it appears in source. Core icon assets, licensing, validation, and updates are concerns of `@stack-sh/theme`. A provider pack is caller-supplied local data governed by its own source terms; the renderer MUST NOT treat it as Apache-2.0 merely because Stack-authored code uses that license. Renderers SHOULD report the resolved icon identifier, catalog version, provider pack revision, official source release, archive hash, and terms URL in output metadata or an accompanying notice so visual changes and obligations can be reproduced. Icons MUST NOT be the only accessible indication of a node's meaning. Renderers control icon size, color, stroke, masking, and placement to preserve visual consistency. @@ -639,7 +655,7 @@ Renderers MUST derive accessible names from diagram, group, node, and edge text, Source strings are untrusted plain text. Renderers that target HTML or SVG MUST escape them for the output context. Source must never be interpreted as HTML, Markdown, script, a file path, or a network URL. -Themes and icons MUST be loaded from the installed `@stack-sh/theme` catalog. A renderer must not interpret a theme or icon identifier as a file path, package name, or network location. +Themes and unnamespaced icons MUST be loaded from the installed `@stack-sh/theme` catalog. Namespaced icons MUST be loaded only from explicitly installed, validated provider packs. A renderer must not interpret a theme or icon identifier as a file path, package name, or network location, and must not initiate a network request from one. ## 14. Examples diff --git a/conformance/valid/provider-icon-namespace/expected.ir.json b/conformance/valid/provider-icon-namespace/expected.ir.json new file mode 100644 index 0000000..46d1f2c --- /dev/null +++ b/conformance/valid/provider-icon-namespace/expected.ir.json @@ -0,0 +1,28 @@ +{ + "schemaVersion": "1.0", + "languageVersion": { + "major": 1, + "minor": 0 + }, + "title": "Provider icon namespace", + "themeId": "default", + "children": [ + { + "type": "node", + "id": "assets" + } + ], + "nodes": [ + { + "id": "assets", + "label": "Amazon S3", + "kind": "storage", + "iconId": "aws:s3", + "detail": null, + "parentGroupId": null + } + ], + "groups": [], + "edges": [], + "layout": null +} diff --git a/conformance/valid/provider-icon-namespace/source.stack b/conformance/valid/provider-icon-namespace/source.stack new file mode 100644 index 0000000..7dac80e --- /dev/null +++ b/conformance/valid/provider-icon-namespace/source.stack @@ -0,0 +1,8 @@ +stack 1.0 + +diagram "Provider icon namespace" { + node assets "Amazon S3" { + kind storage + icon "aws:s3" + } +} diff --git a/docs/decisions/0005-provider-icon-namespaces.md b/docs/decisions/0005-provider-icon-namespaces.md new file mode 100644 index 0000000..93aa45c --- /dev/null +++ b/docs/decisions/0005-provider-icon-namespaces.md @@ -0,0 +1,25 @@ +# ADR 0005: Separate provider icon namespaces from theme icons + +## Status + +Accepted + +## Context + +Stack 1.0 originally allowed only unnamespaced icon identifiers resolved by the selected theme. Architecture diagrams also need exact provider products such as Amazon S3 without changing the node's semantic `kind`. Provider artwork has source-specific copyright, trademark, redistribution, modification, attribution, and lifecycle terms that cannot be inherited from Stack's Apache-2.0 code. + +## Decision + +Stack accepts either an unnamespaced theme icon or one `:` identifier. Provider namespaces are lowercase, explicit, and cannot override a theme icon or another provider namespace. + +Provider packs are separate caller-supplied resources. A renderer does not discover, download, upload, or cache a pack. It preserves the authored node kind, uses the existing kind fallback with `STK5001` when a pack or icon is unavailable, and records the exact pack and asset provenance when provider artwork is embedded. + +The provider-pack manifest, validation, and rights boundary are owned by `stack-sh/theme`. Language implementations validate only the symbolic identifier shape; they do not infer a file, package, URL, or license from it. + +## Consequences + +- Existing unnamespaced documents and theme behavior remain valid. +- A specific provider product can use stable source syntax without assigning vendor semantics to `kind`. +- Hosts must install or import provider packs explicitly and comply with their separate terms. +- Native and browser renderers can share the same pure in-memory resolution behavior. +- Missing provider resources remain warnings with deterministic core fallbacks. diff --git a/schemas/normalized-ir.schema.json b/schemas/normalized-ir.schema.json index 2b0d580..47098a3 100644 --- a/schemas/normalized-ir.schema.json +++ b/schemas/normalized-ir.schema.json @@ -69,7 +69,7 @@ }, "iconIdentifier": { "type": "string", - "pattern": "^[a-z0-9][a-z0-9-]{0,63}$" + "pattern": "^(?:[a-z0-9][a-z0-9-]{0,63}|[a-z][a-z0-9-]{1,31}:[a-z0-9][a-z0-9-]{0,63})$" }, "languageVersion": { "type": "object",