SSSR-URL
Asset/URL Link Registry
(Addendum to SSSR – Smart Searchable Signal Registry)
1. Introduction
The Smart Searchable Signal Registry (SSSR) is the authoritative, cross-module control plane for all semantic references in ZAYAZ. While its core purpose is to govern data signals (metrics, codes, formulas), the same principles apply to digital assets — such as hazard pictograms, logos, templates, documents and other URL-addressable resources.
The Asset/URL Link extension of SSSR centralizes the definition, classification, and resolution of all external and internal file references. Instead of embedding brittle, hard-coded URLs in data tables, ZAYAZ stores semantic asset identifiers (sssr:asset.<class>.<asset_id>[.<ext>]) which are dynamically resolved into valid URLs at runtime.
This approach delivers four strategic benefits:
- Durability – Asset references remain valid even if file locations, CDN providers, or folder structures change.
- Governance & Auditability – Every asset is registered with metadata, versioning rules, usage scope, and compliance tags, ensuring traceability across all consuming modules.
- Dynamic Optimization – The resolver can select the most appropriate asset variant (e.g., SVG for web, CMYK TIF for print) based on runtime context, without changing stored references.
- Cross-Module Consistency – All engines (FOGE, EXPORT, DAIM, APIs) resolve assets through the same policy layer, guaranteeing uniform output.
In practice, the asset subsystem introduces two dedicated registry tables alongside the core SSSR:
sssr_asset_class_registry– Defines asset classes (e.g.,hazard_pictograms), their path templates, default folders, fallback order, and resolution policies.sssr_assets_registry– Defines individual assets, their available variants, template overrides, and the source table/column where they are referenced.
Together with sssr_global_config (for system-wide defaults like cdn_base), these tables enable a dedicated assets-URL-resolver service to translate [SSSR_REF: …] markers into correct, policy-compliant URLs in real time.
This design ensures that asset linking in ZAYAZ is as robust, auditable, and future-proof as its core data signal referencing, while also enabling direct integration into internal modules, partner APIs, and client-facing endpoints.
2. Always go through the SSSR instead of hardcoding direct links to libraries of assets.
Here’s why:
- Traceable Asset Management
- If we reference
[SSSR_REF: sssr:hazard_pictograms.gif_url @ HZP-0003](example), we can later change the storage location, CDN provider, or file format without breaking any references. - The SSSR keeps the logical link (“hazard pictogram – GIF variant”) separate from the physical URL, so only the registry entry needs updating.
- Multi-Format Routing
- A single pictogram may exist in GIF, EPS, TIFF, SVG, etc.
- By registering all variants in the signal metadata (symbol_url_gif, symbol_url_eps, symbol_url_label_tiff), we can let engines (FOGE, DAIM, exporters) pick the right one for the output context (web dashboard = GIF/SVG, print = EPS/TIFF) without hardcoding it.
- Compliance & Auditability
- As an example, some hazard pictograms are legally regulated (GHS, CLP) — linking them via SSSR ensures we can log which exact official asset was served and from which source version. This is important for traceability in SDS or chemical safety reports.
- CDN & Localization Support
- The SSSR entry can route to a different URL based on:
- Geo (EU/US CDNs)
- Language (localized hazard labels)
- Theme (dark/light UI)
- Direct linking in the symbol_url column skips all that intelligence.
- Future-proofing for Theming/Branding
- If Viroway or a client wants custom hazard icon styling, only the SSSR entry changes.
- Direct links in our tables would require bulk updates and risk breaking historical data.
Implementation
- In
code_registry.symbol_url, store theSSSR_REFpointing to the canonical pictogram entry (e.g., GIF version for default use). - Keep the
hazard_pictogramstable as the asset source table in SSSR with columns for each format. - Let FOGE or your export logic resolve the right format at runtime via preferred_format or output_context.
3. SSSR Links
3.1. SSSR Signal Registry
The signal registry (signal_registry) provides a universal way to reference any row in any table.
Each signal_id has three parts:
- "sssr:" (prefix marking it as a system reference)
- The source table
- The source column
The table and column are separated by a dot ".".
Example:
sssr:label_elements.label_element_id
Here:
label_elements= table namelabel_element_id= column name
To reference a specific row, append " @ <row_id>".
Example:
sssr:label_elements.label_element_id @ LEID-0001
Finally, wrap it with [SSSR_REF: … ] to create the full inline reference:
[SSSR_REF: sssr:label_elements.label_element_id @ LEID-0001]
Rules:
- Always put a space after "
SSSR_REF:". - Always put spaces around "@".
- All links in all tables (except inside the signal registry itself) must use this format.
3.2. SSSR Asset Registry
Assets are managed in a dedicated registry: sssr_asset_registry.
Assets are different from signals because they are independent objects (files, images, pictograms, logos, etc.) that are referenced by many rows across the system.
3.2.1. Basic asset reference**
To link to an asset, the format is similar, but with "asset." after “sssr:":
sssr:asset.hazard_pictograms.GHS01.gif
asset= registry typehazard_pictograms= asset class (defined in thesssr_asset_class_registrytable)GHS01.gif= the asset code + file extension
Since each asset already has its own row in sssr_asset_registry, we do not append a row reference (@ ROWID) in the basic form.
3.2.2. Extended Inline Form
However, when we want to track provenance — i.e. which tables/columns/rows are linking to an asset — we use the extended inline form:
[SSSR_REF: sssr:asset.hazard_pictograms.GHS01.gif @ sssr:label_elements.symbol_sssr @ LEID-0029]
This tells us:
- The asset =
hazard_pictograms.GHS01.gif - The source column =
label_elements.symbol_sssr - The source row =
LEID-0029
The extended form is essential for auditing, AI self-healing, and exports, because it embeds full provenance directly in the reference.
Summary:
- Signals (
sssr:<table>.<column> @ row_id) = references to data rows. - Assets (
sssr:asset.<class>.<asset_code>) = references to files/resources. - Extended inline form = adds source provenance (@ table.column @ row_id) to assets (and optionally signals) for auditing and portability.
3.3. Extended Inline Form for Links to Assets
In tables: store the compact string form:
[SSSR_REF: sssr:asset.hazard_pictograms.GHS01.gif @ sssr:code_registry.code_type @ CR-00213]
Everything before the first @ is the target (what to resolve as a URL). The @ sssr:table.column @ row_id part is provenance (only used for export/ audit/ feed AI). On export / in APIs: convert to JSON on the fly. This gives a human-readable cells, a single canonical string format..
Precise grammar (so it’s easy to parse)!
EBNF:
SSSR_REF := "[SSSR_REF: " TARGET ( " @ " SRC_REG ( " @ " ROW_ID )? )? "]"
TARGET := "sssr:" 1*( CHAR - WHITESPACE - "]" )
SRC_REG := "sssr:" 1*( CHAR - WHITESPACE - "@" - "]" )
ROW_ID := 1*( CHAR - "]" )
Rules:
- Exactly one space after the colon and around each
@. - No trailing spaces.
- TARGET MUST start with sssr: and contain no
]. SRC_REGMUST besssr:<table>.<column>.ROW_IDshould be the row’s PK (e.g.,CR-00213).
Examples:
[SSSR_REF: sssr:asset.hazard_pictograms.GHS01]
[SSSR_REF: sssr:asset.hazard_pictograms.GHS01.gif]
[SSSR_REF: sssr:asset.hazard_pictograms.GHS01.gif @ sssr:code_registry.code_type @ CR-00213]
Converter → JSON (TypeScript)
type Enriched =
| { target: string; source?: { registry: string; row_id?: string } };
export function parseSSSRRef(ref: string): Enriched | null {
const m = ref.match(/^\[SSSR_REF:\s*(.+?)\s*(?:@\s*(sssr:[\w.]+)\s*(?:@\s*([^\]]+))?)?\]$/);
if (!m) return null;
const [, target, srcReg, rowId] = m;
const out: Enriched = { target: target.trim() };
if (srcReg) out.source = { registry: srcReg.trim(), ...(rowId ? { row_id: rowId.trim() } : {}) };
return out;
}
Converter → JSON (Postgres SQL)
CREATE OR REPLACE FUNCTION sssr_to_json(ref text)
RETURNS jsonb LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
m text[];
BEGIN
-- regex groups: 1=target, 2=source registry, 3=row_id
SELECT regexp_match(ref, '^\[SSSR_REF:\s*(.+?)\s*(?:@\s*(sssr:[\w.]+)\s*(?:@\s*([^\]]+))?)?\]$') INTO m;
IF m IS NULL THEN
RETURN NULL;
END IF;
RETURN jsonb_strip_nulls(jsonb_build_object(
'target', m[1],
'source', CASE WHEN m[2] IS NULL THEN NULL
ELSE jsonb_strip_nulls(jsonb_build_object('registry', m[2], 'row_id', m[3])) END
));
END $$;
Direct Link Behavior
- The resolvers can safely treat the target alone
(sssr:asset…)as a complete link for URL resolution. - The
@ sssr:<table>.<column> @ <row>part is ignored by URL resolution, used only for provenance when exporting, logging, or AI autonomy.
Array-of-Refs Pattern Example:
[
"[SSSR_REF: sssr:asset.hazard_pictograms.explos @ sssr:label_elements.symbol_sssr @ LEID-0029]",
"[SSSR_REF: sssr:asset.hazard_pictograms.flamme @ sssr:label_elements.symbol_sssr @ LEID-0029]"
]
- Each string is a full
SSSR_REFwith:- the asset target (
hazard_pictograms.explos,hazard_pictograms.flamme), - the source table + column (
label_elements.symbol_sssr), - and the row ID (
LEID-0029).
- the asset target (
- The array tells the resolver that multiple assets are bound to the same label element.
- When enriched/exported, this can either remain as a JSON array of refs, or be expanded into atomic enriched JSON objects for each pictogram.
This is consistent with the “multiple target references” pattern in the SSSR design: use an array of valid SSSR_REF strings when more than one reference applies .
Recommendation:
- Keep this array-of-refs structure in the tables.
- At export/API time, the resolver can emit them either as a JSON array of enriched objects or flatten them into multiple rows depending on consumer needs.
SSSR Link Examples: Signals vs Assets
| Type | Basic Form | Extended Inline Form (with provenance) | Meaning |
|---|---|---|---|
| Signal (row in a table) | [SSSR_REF: sssr:label_elements.label_element_id @ LEID-0001] | [SSSR_REF: sssr:label_elements.label_element_id @ LEID-0001] (extended form is the same because signals always need the row id) | Points to the label_element_id in the label_elements table, row LEID-0001. |
| Asset (file in asset registry) | [SSSR_REF: sssr:asset.hazard_pictograms.GHS01.gif] | [SSSR_REF: sssr:asset.hazard_pictograms.GHS01.gif @ sssr:label_elements.symbol_sssr @ LEID-0029] | Points to the asset GHS01.gif in the hazard_pictograms class. Extended form also records which source table/column (label_elements.symbol_sssr) and row (LEID-0029) is using this asset. |
3.4. Key Takeaways
- Signals always need @
row_id, because they’re row-level references. - Assets don’t use @
row_idin the basic form (they are global rows insssr_asset_registry). - The extended inline form adds provenance (table + column + row) for traceability, audits, and AI self-healing.
4. SSSR “Asset/URL Link”
We have a dedicated SSSR “Asset/URL Link” section and route every file/link through it. That keeps links portable, auditable, and swappable without touching data tables or code. It’s fully aligned with how SSSR is meant to centralize column-level references and cross-table links (incl. the [SSSR_REF: <signal_id> @ <row_id>] pattern).
We use
- A registry area:
assets_registry(as part of SSSR). - Resolver microservice: assets-url-resolver that expands an sssr: asset ID into the correct URL at runtime (UI, exports, APIs).
- Tokenized URL templates: so we can flip base domains/folders once and everything updates.
sssr_asset_registry schema (SSSR → assets_registry)
| Field | Purpose |
|---|---|
| asset_id (PK) | Canonical signal, e.g. sssr:assets.hazard_pictograms.GHS01 |
| asset_class | hazard_pictogram, logo, policy_pdf, etc. |
| variants[] | Array of objects: {format, mime, dpi, width_px, usage_hint} (e.g., gif, svg, eps, tiff_label) |
| template_vars | Defaults: cdn_base, version, locale, theme, folder |
| fallback_order[] | e.g., ["svg","gif","tiff_label"] |
| hash / etag | Integrity/audit (and cache-busting) |
| license/source | Legal metadata (e.g., GHS/CLP source) |
| visibility_scope | admin/user/system/public (matches SSSR visibility) |
| availability_status | active/draft/deprecated |
| created_at/updated_at | Governance/audit trail |
sssr_asset_class_registry schema
| Column | Type | Description | Example / Rules |
|---|---|---|---|
| asset_class | text (PK) | Canonical class name for a family of assets. | hazard_pictograms, logos, templates_pdf |
| path_template | text | URL “recipe” with placeholders resolved by the resolver. Must include {{cdn_base}} and {{code}}. {{ext}} strongly recommended. | {{cdn_base}}/{{folder}}/{{code}}.{{ext}} |
| defaults | jsonb | Class ‑level default variables used to fill the template and drive selection logic. Required keys: folder, fallback_order. | {"folder":"library/images/pictograms/hazard_pictograms","fallback_order":["svg","gif","tif"]} |
| availability_status | text (enum) | Lifecycle: active, draft, deprecated. | active |
| created_at / updated_at | timestamptz | Governance/audit timestamps. | auto‑managed triggers |
| description | text | Human‑readable purpose of the class. | “GHS/CLP hazard pictograms for SDS & labeling.” |
| integrity_rules | jsonb | Class‑level integrity constraints. | {"allowed_ext":["svg","gif","tif","eps"],"path_regex":"^library/images/pictograms/.\*"} |
| resolver_policy | jsonb | How to pick variants at runtime (context/locale/version). | {"variant_selection":{"print":["tif","eps"],"web":["svg","gif"]},"collapse_empty_segments":true} |
| cdn_policy | jsonb | CDN behaviors. | {"cache_control":"public,max-age=604800","signing_required":false,"ttl_seconds":604800} |
| visibility_scope | text (enum) | Access semantics aligned with SSSR. | admin,user,system,public |
| owner_module | text | Owning engine/team for ops. | ZHIF, SIS |
| maintainer_contact | text | Email/Slack/Group for break‑glass. | cto@viroway.com |
| versioning_mode | text | pinned, latest, or inherit. Affects {{version}} handling. | latest |
| localization_mode | text | none, optional, required. Drives {{locale}}. | optional |
| tenancy_mode | text | global, tenant_overrides_allowed. | tenant_overrides_allowed |
| override_rules | jsonb | Declarative per‑tenant/per‑region overrides. | {"tenant:eco-196-...":{"folder":"tenants/acme/hazards"}} |
| compliance_tags | text[] | Legal or standard tags for audit. | {"GHS","CLP"} |
| license_info | jsonb | License/source metadata. | {"source":"UNECE","license":"permitted-use"} |
| telemetry_flags | jsonb | Emit resolver telemetry, sample rates, alerting. | {"emit_resolution_events":true} |
| deprecation_policy | jsonb | Sunset plan for class. | {"requires_migration_ticket":true,"replacement_class":"hazard_pictograms_v2"} |
Validation & constraints (practical guardrails)
- Template requirements: reject rows where path_template lacks
{{cdn_base}}or{{code}}. Prefer{{ext}}; if omitted, engine must supply it. - Defaults integrity: defaults.folder must be non‑empty;
defaults.fallback_ordermust be a non‑empty array whose values appear inintegrity_rules.allowed_ext(if provided). - Enum checks: constrain
availability_status,visibility_scope,tenancy_mode, etc. - Indexing: PK on asset_class; add BTREE index on
availability_statusand GIN index ondefaults/integrity_rules(jsonb_path_ops) for ops queries.
Example row (hazard pictograms)
{
"asset_class": "hazard_pictograms",
"description": "GHS/CLP hazard pictograms for SDS printing and UI.",
"path_template": "{{cdn_base}}/{{folder}}/{{code}}.{{ext}}",
"defaults": {
"folder": "library/images/pictograms/hazard_pictograms",
"fallback_order": ["svg","gif","tif"],
"preferred_format_by_context": {
"web": ["svg","gif"],
"print": ["tif","eps"]
}
},
"integrity_rules": {
"allowed_ext": ["svg","gif","tif","eps"],
"path_regex": "^library/images/pictograms/hazard_pictograms/.*"
},
"resolver_policy": {
"collapse_empty_segments": true,
"variant_selection": {"print":["tif","eps"],"web":["svg","gif"]},
"locale_inheritance": "optional",
"versioning": "latest"
},
"cdn_policy": {"cache_control":"public,max-age=604800","signing_required":false},
"visibility_scope": "public",
"owner_module": "ZHIF",
"maintainer_contact": "cto@zayaz.io",
"availability_status": "active"
}
sssr_global_config schema (for system‑wide defaults inherited by many asset classes)
| Column | Type | Purpose / Notes | Example |
|---|---|---|---|
| key (PK) | text | Config item name | cdn_base |
| value | jsonb | Arbitrary JSON to allow rich settings | {"url":"https://lib.zayaz.io"} |
| scope | text enum | global | tenant | region | env (prod/stage) — lets you override per context | global |
| selector | jsonb | When scope != global, the selector describing “who” this applies to | {"tenant":"ECO-196-000-000-000"} |
| description | text | Human description so ops knows why it exists | “Primary CDN for public assets” |
| availability_status | text enum | active | draft | deprecated | active |
| updated_by | text | Change accountability | [cto@viroway.com](mailto:cto@viroway.com) |
| updated_at | timestamptz | Governance | auto |
| effective_from / effective_to | timestamptz | Time‑boxed rollouts, blue/green | optional |
| integrity_rules | jsonb | Optional checks (regexes, URL allowlists) | \`{“url_regex”:”^https://(lib |
Common keys used day‑to‑day
cdn_base:{"url":"https://lib.zayaz.io"}default_locale:{"value":"en"}default_version:{"value":"latest"}theme_defaults:{"value":"light"}asset_resolution_policy: e.g.{"web":["svg","gif"],"print":["tif","eps"]}region_overrides: e.g.{"EU":{"cdn_base":"https://cdn-eu.zayaz.net"}}
This preserves the SSSR pattern: global → class → instance; engines read from the registry rather than hardcoding.
How consumers should store references
- In tables like
code_registry.symbol_url, store only the SSSR reference, not a URL:- "
[SSSR_REF: sssr:asset.hazard_pictograms.GHS01.gif @ sssr:code_registry.code_type @ CR-00213]".
- "
- Engines (FOGE/SEM/DAIM/exports) call assets-url-resolver with:
asset_id, optional hints{context:"print" | "web", preferred_format:"gif", locale:"en", theme:"light"}, and it returns the best URL.
- This matches the doc’s cross-table link idiom and lets engines auto-pick formats without hardcoding.
URL resolution logic (tiny, robust)
- Fetch asset_id → registry entry.
- Apply path_template with precedence: request hints → tenant overrides → registry defaults.
- Choose variant by rules:
preferred_formatorusage_hint(web/print) → fallback_order. - Emit URL + integrity metadata; log to ALTD for traceability.
- If missing, trigger SEM/ops fallback and raise a registry notification (keeps auditability intact).
Ops conveniences
- One-click base switch:
cdn_baseis kept in a global SSSR config row (sssr_global_config); changing it flips every link. - Folder refactors: do them by editing path_template once.
- Version pinning: support version="latest" vs. explicit v2025.08 per tenant/report.
- Localization/theming: drive
{{locale}}/{{theme}}from user/session—no schema churn.
5. Migration plan
- Register assets in
assets_registrywith variants (e.g. pictograms with GIF/EPS/TIFF/SVG variants). - Add cdn_base + default version to
sssr_global_config. - Replace direct URLs in tables with
[SSSR_REF: …]strings (Excel CONCAT works great). - Point UI/export code to assets-url-resolver.
- Turn on telemetry: log every resolution event for audits.
This keeps ZAYAZ consistent with SSSR’s purpose—semantic, traceable indirection for anything other modules consume—so links gain the same durability and auditability as data signals.
5.1. The path_template - {{cdn_base}}/{{folder}}/{{version}}/{{locale}}/{{code}}.{{ext}}
The path_template is just a parameterized blueprint for generating the actual file URL at runtime.
It works like a URL “recipe” where placeholders (inside {{...}}) get replaced with values from the SSSR entry, the global config, or runtime context.
Example:
https://lib.zayaz.io/library/images/pictograms/hazard_pictograms/1-1.tif
5.1.1. Breaking it into components
Using the SSSR asset registry pattern, we’d map it to:
| Placeholder | Value in this case | Where it comes from |
|---|---|---|
| cdn_base | https://lib.zayaz.io | Global SSSR config (sssr_global_config) so we can change CDN easily. |
| folder | library/images/pictograms/hazard_pictograms | Asset registry entry for this asset class. |
| version | omitted in this case (could be v1, 2025.08 etc.) | Optional — can be blank if no versioning is in the URL. |
| locale | none here (could be en, fr, etc.) | Runtime context (user/report locale). |
| code | 1-1 | The asset’s logical code inside the hazard pictograms category. |
| ext | tif | Variant/format selection (chosen by engine: print/web/etc.). |
5.1.2. For this specific URL, a working template could be:
{{cdn_base}}/{{folder}}/{{code}}.{{ext}}
To support versioning and localization later without breaking current URLs, we can plan ahead with:
{{cdn_base}}/{{folder}}/{{version}}/{{locale}}/{{code}}.{{ext}}
And set version="" and locale="" when not in use.
5.1.3. Resolution process
When a consumer (UI, export engine, API) requests the asset:
- Look up SSSR asset entry — e.g.,
sssr:asset.hazard_pictograms.1-1.tiff_label. - Get template:
{{cdn_base}}/{{folder}}/{{code}}.{{ext}}. - Pull defaults from:
- Global config (sssr_global_config): cdn_base = https://lib.zayaz.io
- Asset class config (sssr_asset_class_registry): folder = library/images/pictograms/hazard_pictograms
- Asset record (sssr_asset_registry): code = 1-1
- Format/variant mapping: ext = tif
- Substitute placeholders into the template →
https://lib.zayaz.io/library/images/pictograms/hazard_pictograms/1-1.tif
- Serve the file, with optional integrity hash for audit.
5.1.4. Why this matters
- Base URL change? → Update
cdn_basein one SSSR config row. - Folder restructure? → Update folder for the asset class in one place.
- Format change? → Update ext mapping; all consuming modules get the right variant.
- Localization/versioning in future? → Fill in locale / version without rewriting URLs in the data tables.
Summary
- Three-layer fill-in process
-
Layer 1 – Global defaults (
sssr_global_config)- Values that apply to all assets, stored in global config rows in SSSR.
- Examples:
cdn_base= https://lib.zayaz.iodefault_locale= endefault_theme= lightversion= latest
- These can be swapped in one place to move all assets to a new CDN or switch a default locale.
-
Layer 2 – Asset class defaults
- Stored in the sssr_asset_class_registry entry for the asset class.
- Examples:
- folder = library/images/pictograms/hazard_pictograms
- fallback_order = ["svg", "gif", "tiff_label"]
- This ensures all hazard pictograms share the same base folder and format fallback rules.
-
Layer 3 – Asset instance values
- tored in the sssr_asset_registry entry for that specific asset.
- Examples:
- code = 1-1
- variants = gif, svg, eps, tiff
- primary_format = tif
- These define what makes that specific pictogram unique.
- Example resolution step-by-step
-
To resolve:
[SSSR_REF: sssr:asset.hazard_pictograms.1-1.tif] -
Step 1 – Get path_template
- From sssr_asset_class_registry for hazard_pictograms:
path_template = {{cdn_base}}/{{folder}}/{{code}}.{{ext}}
- From sssr_asset_class_registry for hazard_pictograms:
-
Step 2 – Pull values
- cdn_base → https://lib.zayaz.io (global config)
- folder → library/images/pictograms/hazard_pictograms (class default)
- code → 1-1 (asset instance)
- ext → tif (from request or asset default)
-
Step 3 - Substitute placeholders
https://lib.zayaz.io/library/images/pictograms/hazard_pictograms/1-1.tif
- Why this is powerful
- Moving the hazard pictogram library to /assets/icons/hazards? → Update 1 row in the asset class config.
- Switching CDN from lib.zayaz.io to cdn.zayaz.net? → Update 1 global config row.
- Adding a localization? → Add
{{locale}}in template and populate it from user session.
5.2. assets-url-resolver
The assets-url-resolver is the “URL kitchen” that cooks up the final link using the “recipe” (path_template) and the “ingredients” (metadata from global config, asset class defaults, and asset instance values). It’s a:
- A microservice (or internal module) in the ZAYAZ backend.
- Could be implemented as:
- A REST endpoint →
/resolve_asset_url?sssr_id=sssr:asset.hazard_pictograms.1-1.tif - A library function inside the platform →
resolveAssetURL(sssr_id, options).
- A REST endpoint →
- Uses the SSSR registry tables as its data source — it doesn’t store URLs itself.
5.3.SSSR Assets URL Resolver — Architecture & Flow
The following shows how the assets-url-resolver service (logic) works with the SSSR assets registry tables (data). It includes a component map, data model, API contract, and the step-by-step URL resolution flow.
5.3.1. Components (High-Level)
Key idea: Tables hold metadata; the resolver composes the final URL at runtime.
5.3.2. Minimal Data Model
- Global Config —
sssr_global_config
- key:
cdn_base,default_locale,default_version,default_theme - value
- Asset Class Registry — sssr_asset_class_registry
asset_class(PK): e.g., hazard_pictograms, docs, logos, templatespath_template: e.g.,{{cdn_base}}/{{folder}}/{{version}}/{{locale}}/{{code}}.{{ext}}defaults: JSON (e.g.,{ "folder": "library/images/pictograms/hazard_pictograms", "fallback_order": [“svg","gif","tif"] })
- Assets Registry —
sssr_assets_registry
asset_id(PK): e.g., GHS01, 1-1,company_logoasset_class: FK →sssr_asset_class_registry.asset_classvariants: JSON array of objects, e.g.,[ {"ext":"svg"}, {"ext":"gif"}, {"ext":"tif","usage_hint":"label_print"} ]template_vars: JSON (overrides), e.g.,{ "code": "1-1" }license,hash,availability_status,created_at,updated_at
5.3.3. Resolver API Contract (Service)
HTTP (REST) option
GET /api/assets/resolve?sssr_id=sssr:asset.hazard_pictograms
Library (in-process) option
/**
* Types
*/
export type AssetContext = 'web' | 'print' | 'pdf';
export type AssetTheme = 'light' | 'dark';
export interface ResolveAssetOptions {
preferredFormat?: string; // e.g. 'svg' | 'gif' | 'tif'
locale?: string; // e.g. 'en', 'no'
version?: string; // e.g. 'latest', 'v2025.08'
context?: AssetContext; // influences variant choice
theme?: AssetTheme;
}
export interface ResolvedAsset {
url: string;
format: string;
locale?: string;
version?: string;
context?: AssetContext;
theme?: AssetTheme;
}
/**
* Example implementation
*/
export function resolveAssetURL(
sssrId: string, // e.g. 'sssr:asset.hazard_pictograms.1-1.tif'
opts: ResolveAssetOptions = {}
): ResolvedAsset {
const {
preferredFormat = 'svg',
locale = 'en',
version = 'latest',
context = 'web',
theme = 'light'
} = opts;
// Example base CDN path (adjust to your system)
const baseURL = 'https://cdn.example.com/assets';
const url = `${baseURL}/${version}/${locale}/${context}/${theme}/${sssrId}.${preferredFormat}`;
return {
url,
format: preferredFormat,
locale,
version,
context,
theme
};
}
Response
{
"url": "https://lib.zayaz.io/library/images/pictograms/hazard_pictograms/1-1.tif",
"resolved": {
"cdn_base": "https://lib.zayaz.io",
"folder": "library/images/pictograms/hazard_pictograms",
"code": "1-1",
"ext": "tif",
"version": "",
"locale": ""
},
"variant": {"ext":"tif","usage_hint":"label_print"},
"integrity": {"hash":"sha256:…"},
"meta": {"asset_class":"hazard_pictograms","asset_id":"1-1"}
}
5.3.4. URL Resolution Flow (Step-by-Step)
The resolution process works as follows:
- The consumer (UI, FOGE, Exporter, etc.) calls the resolver with an sssr:asset ID and optional options.
- The resolver parses the ID into its parts (asset class, code, extension).
- It retrieves global defaults (like cdn_base, default locale, version, theme).
- It looks up the class template and defaults for the given asset class.
- It loads the asset record (variants, template variables, integrity, etc.).
- It decides the best variant to serve based on the options, context, and fallback order.
- It composes the URL by substituting variables into the path template.
- It returns the resolved URL and metadata to the consumer, which then fetches the asset from the CDN.
Variant choice logic (simplified):
- If opts.preferredFormat available → use it.
- Else if context=print → prefer tif/eps if available.
- Else → prefer svg/gif.
- Else → follow fallback_order from class defaults.
5.3.5. Path Template Examples
Example of simple flat structure:
{{cdn_base}}/{{folder}}/{{code}}.{{ext}}
Works for:
Future-ready with version & locale:
{{cdn_base}}/{{folder}}/{{version}}/{{locale}}/{{code}}.{{ext}}
- With version="latest" or v2025.08, and locale="en"|"no".
- If empty, resolver collapses empty segments to avoid double slashes.
Tenant-branding override:
{{cdn_base}}/{{tenant}}/{{folder}}/{{code}}.{{ext}}
tenant comes from auth context; lets us theme per client.
5.3.6. Example SQL Sketches
Illustrative only — adapt types/indexes to the DB.
-- Global config
CREATE TABLE sssr_global_config (
key text PRIMARY KEY,
value jsonb NOT NULL,
updated_at timestamptz DEFAULT now()
);
-- Asset classes
CREATE TABLE sssr_asset_class_registry (
asset_class text PRIMARY KEY,
path_template text NOT NULL,
defaults jsonb NOT NULL,
updated_at timestamptz DEFAULT now()
);
-- Assets
CREATE TABLE sssr_assets_registry (
asset_id text PRIMARY KEY,
asset_class text NOT NULL REFERENCES sssr_asset_class_registry(asset_class),
variants jsonb NOT NULL,
template_vars jsonb,
license text,
hash text,
availability_status text DEFAULT 'active',
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now()
);
CREATE INDEX ON sssr_assets_registry(asset_class);
5.3.7. Caching & Performance
- In-memory cache for class templates and global config (TTL 5–15 min).
- Per-asset cache for resolved URLs keyed by (
sssr_id,locale,version,preferredFormat,theme,context). - Cache busting when
updated_atchanges on relevant rows.
5.3.8. Error Handling & Telemetry
- If asset/variant missing → return structured error with fallback_attempted=true and candidates list.
- Emit telemetry event:
asset_resolve_failedwithasset_id,asset_class,requested_ext,fallback_path,reason. - Optionally route a task to “Registry Ops” queue to fix broken entries; link to ALTD audit trail.
5.3.9. Governance & Audit (Why this is SSSR-aligned)
- Traceability: Every URL comes from registry metadata → auditable.
- Swap safety: Change cdn_base or folder once → all consumers updated.
- Version pinning: Reports can pin version for reproducibility.
- Locale-aware: Localized assets can be served without altering data tables
5.3.10. Quick Usage Cheat Sheet
- Store in tables:
[SSSR_REF: sssr:asset.<class>.<asset_id>.<ext> @ <row_id>]. - Call resolver from UI/engines with optional
preferredFormat,locale,version,context. - Get back a stable URL + integrity + metadata.
5.4. Resolver variable mapping (DB → template vars → URL)
The resolver variable-mapping table shows exactly how DB columns → template vars → final URLs.
| Source | Field (DB / runtime) | Template var | Notes / Precedence |
|---|---|---|---|
| sssr_global_config | cdn_base.url | cdn_base | Global default; can be overridden by region/tenant config at runtime. |
| sssr_asset_class_registry.defaults | folder | folder | Class-level folder; typically the only place you change folder structure. |
| sssr_asset_class_registry.defaults | version (optional) | version | Class default; can be empty. Runtime can override (e.g., v2025.08). |
| sssr_asset_class_registry.defaults | locale (optional) | locale | Class default; runtime/user session can override (e.g., no, en). |
| sssr_assets_registry | asset_code | code | Asset code → mapped to {{code}} (keep DB name as asset_code). |
| sssr_assets_registry.template_vars | arbitrary keys | same key name | Per-asset overrides for any var (e.g., folder, version, locale, code). |
| Runtime (resolver input) | preferredFormat or ext | ext | Highest precedence for format if provided explicitly. |
| Policy (class defaults / resolver_policy) | fallback_order | ext (chosen) | Used when ext not explicit; picks first available variant. |
| sssr_assets_registry.variants[] | ext, color_space, dpi, … | n/a | Source of available formats and technical constraints. |
Example: vars bag assembly Inputs
- Template (class):
{{cdn_base}}/{{folder}}/{{version}}/{{locale}}/{{code}}.{{ext}} - Global: cdn_base = https://lib.zayaz.io
- Class defaults: folder = library/images/pictograms/hazard_pictograms, fallback_order =
["svg","gif","tif"] - Asset row: asset_code = "1-1", variants =
[svg, gif, tif] - template_vars (asset):
{ "locale": "no" } - Runtime: context = "web" (no explicit ext, no version)
Resolver chooses ext
- From policy (web): prefer svg, else fallback order.
- svg exists → pick ext = “svg".
Final variable bag
{
"cdn_base": "https://lib.zayaz.io",
"folder": "library/images/pictograms/hazard_pictograms",
"version": "",
"locale": "no",
"code": "1-1",
"ext": "svg"
}
Rendered URL
https://lib.zayaz.io/library/images/pictograms/hazard_pictograms/no/1-1.svg
(Empty version collapses cleanly if the renderer removes empty path segments.)
Pseudocode (the tiny mapper)
def build_vars(sssr_id, runtime):
cls = load_class(sssr_id.asset_class) # path_template, defaults, policy
asset = load_asset(sssr_id.asset_class, sssr_id.asset_code, runtime.eco_number)
vars = {}
# 1) global
vars["cdn_base"] = get_global("cdn_base.url")
# 2) class defaults
vars.update(cls.defaults) # folder, version?, locale?, etc.
# 3) asset specifics
vars["code"] = asset.asset_code
if asset.template_vars:
vars.update(asset.template_vars)
# 4) ext selection
ext = sssr_id.ext or runtime.preferredFormat or choose_ext(cls.policy, asset.variants, runtime.context)
vars["ext"] = ext
# 5) runtime overrides (last write wins)
for k in ("version","locale"):
if getattr(runtime, k, None):
vars[k] = getattr(runtime, k)
return vars, cls.path_template
5.4. Quick validator checks (nice guardrails)
- Template must reference required vars:
{{cdn_base}},{{code}},{{ext}}(reject row otherwise). - Folder non-empty: class.defaults.folder must be present.
- Variant sanity: chosen ext must exist in asset.variants[] (or after fallback selection).
- Collapse empties: strip double slashes when version/locale are empty.
5.5. How variants[] is chosen (EPS/TIFF for print, GIF/SVG for web)
Use a deterministic policy the resolver applies; AI is optional seasoning, not the core rule.
Where the choice lives
- Explicit in the ID (highest priority): sssr:asset.hazard_pictograms.1-1.tif → use TIF, no questions asked.
- Runtime options from the caller (UI/export): preferredFormat="svg" or context="print" / context="web".
- Class policy (in sssr_asset_class_registry.resolver_policy or defaults):
{"variant_selection":{"web":["svg","gif"],"print":["tif","eps"]}} - Fallback order (class defaults):
{"fallback_order":["svg","gif","tif","eps"]}
variants[] shape (per asset instance)
{
"variants": [
{"ext":"svg", "usage_hint":"web", "color_space":"RGB"},
{"ext":"gif", "usage_hint":"web", "color_space":"RGB"},
{"ext":"tif", "usage_hint":"print","color_space":"CMYK","dpi":300},
{"ext":"eps", "usage_hint":"print"}
]
}
5.6. Selection algorithm (resolver)
- If ID includes
.<ext>→ pick that. - Else if preferredFormat supplied and present in variants → pick it.
- Else if context supplied → consult class
variant_selection[context], pick first available. - Else → use class fallback_order.
- If multiple candidates remain, match technical constraints (e.g.,
color_space="CMYK"anddpi>=300for print). If none match, fall back and log a structured warning.
This keeps the decision declarative and auditable (aligned with SSSR’s design), while still allowing an AI helper to suggest alternatives when constraints aren’t met. The canonical link remains the [SSSR_REF: …] form so lineage and routing are preserved across modules.
5.7. Concrete URL conversion (sanity check)
Current:
https://lib.zayaz.io/library/images/pictograms/hazard_pictograms/1-1.gif
After populating:
cdn_base(global) =https://lib.zayaz.io- Class defaults: folder =
library/images/pictograms/hazard_pictograms - Template:
{{cdn_base}}/{{folder}}/{{code}}.{{ext}}
Use either:
- Explicit:
[SSSR_REF: sssr:asset.hazard_pictograms.1-1.gif]→ exact same GIF URL resolves. - Auto:
[SSSR_REF: sssr:asset.hazard_pictograms.1-1]→ web might resolve to SVG, print to TIF, depending on policy.
5.8. API additions
- Implement an endpoint over the existing resolver logic, e.g.
*GET /api/assets/resolve?sssr_id=sssr:asset.hazard_pictograms.1-1.tif&locale=en&context=web - Return
{ url, variant, meta, integrity }plus HTTP cache headers (ETag, Cache-Control, Last-Modified). - Enforce auth (JWT/OAuth) and scope checks in the service, using visibility_scope from class and asset (logical AND).
Schema Tweaks
sssr_asset_class_registry
| Column Name | Type | Description |
|---|---|---|
| api_enabled | bool, default true | Allow/deny class via API. |
| api_slug | text | Friendly path fragment (e.g., hazards). |
| cors_policy | jsonb | { "origins": ["\*"] } or stricter. |
| rate_limit_bucket | text | Link to gateway settings. |
| signing_required | bool | Require pre‑signed or tokenized URLs. |
| content_disposition | text | Inline | attachment (default per class). |
| filename_template | text | E.g., {{asset_class}}-{{code}}.{{ext}}. |
sssr_assets_registry (asset-level)
| Column Name | Type | Description |
|---|---|---|
| api_enabled | bool | Per‑asset override (default null = inherit). |
| legal_restrictions | jsonb | Export constraints (regions, licenses). |
| download_filename | text | Override for Content-Disposition. |
| integrity | jsonb | { "hash":"sha256:…", "size":12345 }. |
sssr_global_config (global)
| Column Name | Type | Description |
|---|---|---|
| asset_api_defaults | jsonb | Global API behaviors. {“cache_control":"public,max-age=604800","etag_strategy":"hash"} |
| token_signing | jsonb | Key ids, TTL for pre‑signed URLs. |
5.9. API shapes (minimal and clean)
5.9.1. Resolution API (metadata forwarder)
GET /api/assets/resolve
?sssr_id=sssr:asset.hazard_pictograms.1-1
&context=print&locale=no&preferredFormat=tif
5.9.2. 200 JSON
{
"url": "https://lib.zayaz.io/library/images/pictograms/hazard_pictograms/1-1.tif",
"variant": {"ext":"tif","usage_hint":"print","color_space":"CMYK","dpi":300},
"meta": {"asset_class":"hazard_pictograms","asset_id":"1-1","locale":"no"},
"integrity": {"hash":"sha256:...","size": 481233}
}
Headers: ETag, Cache-Control, Last-Modified, optional x-sssr-id.
5.9.3. Direct fetch API (proxy with policy)
GET /api/assets/fetch/hazards/1-1
?context=web&locale=en
Authorization: Bearer …
Service chooses variant, applies signing if required, sets Content-Type and Content-Disposition from class/asset rules, streams the bytes (or 302 to CDN).
5.9.3. Selection logic (where “EPS/TIFF vs GIF/SVG” is decided)
Priority order:
- Explicit ext in ID → use it.
- Caller hint → preferredFormat or context (web/print).
- Class policy →
resolver_policy.variant_selection. - Fallback →
defaults.fallback_order. - Constraints → choose variant matching color_space, dpi (from variants).
This keeps decisions declarative in tables, not in opaque code or “AI magic”. AI helper can be added to suggest alternatives, but the authoritative source remains the registry.
5.9.4. Security & governance touches
- Enforce
visibility_scope(public/user/admin/system) andapi_enabledat both class and asset; deny if either blocks. - Optional tenancy/region guards using scope/selector in global config.
- Log every API resolution with SSSR ID, variant chosen, and caller scope for audits.
- Use pre‑signed URLs if
signing_required=trueat class/asset.
5.9.5. Migration checklist
- Populate resolver_policy, fallback_order, and rich variants.
- Stand up /api/assets/resolve (metadata) and optionally /api/assets/fetch/:class/:id (proxy).
- Wire gateway: auth scopes, rate limits, CORS, caching.
- Record structured telemetry for audits and SLA.
6. Keeping Assets with the Same Name (code) Unique
6.1. Keys & uniqueness (safe schema)
- Primary key (surrogate): asset_uid UUID→ Opaque, never exposed to users or in SSSR IDs.
- Natural key (human/semantic): asset_code TEXT (e.g., "1-1", "logo")
- Tenant scoping: eco_number UUID NULL (NULL = global/shared asset)
- Class scoping: class_id UUID (FK → sssr_asset_class_registry)
- Uniqueness constraint:UNIQUE(eco_number, class_id, asset_code)
→ Allows each tenant to have e.g. a "logo" in client_logos without clashing; also allows a global "logo" when tenant_id IS NULL.
6.2. What the SSSR ID looks like (no collisions)
Keep the SSSR ID stable and simple:
sssr:asset.<asset_class>.<asset_code>[.<ext>]
Examples:
- Global hazard pictogram: sssr:asset.hazard_pictograms.1-1
- Client logo: sssr:asset.client_logos.logo
How tenant is handled: the resolver takes the tenant from auth/context (session, API token) and first tries:
(eco_number = caller.eco_number, class_id = <class>, asset_code = <code>)
If not found, it falls back to global:
(eco_number IS NULL, class_id = <class>, asset_code = <code>)
This gives each tenant its own "logo" while preserving a global default.
6.3. Version pinning (if e.g. multiple dated logos is needed)
If multiple versions per tenant/class/code (e.g., a rebrand) is needed/wanted:
- Add version TEXT to the unique key or create a separate table for versions.
- Recommended uniqueness when enabling versions:UNIQUE(
tenant_id,class_id,asset_code, COALESCE(version,'latest')) - Resolution order:
-
- If SSSR ID (or request) specifies version, use it.
-
- Else use class policy
default_version(often "latest").
- Else use class policy
-
- Fall back to the newest active version.
-
6.4. Concrete examples
Two clients, both want client_logos.logo:
- Row A: (
eco_number = ECO-197-123-456-789, class = client_logos, asset_code = 'logo', variants = […]) - Row B: (
eco_number = ECO-197-987-654-321, class = client_logos, asset_code = 'logo', variants = […]) - Global fallback (optional): (
eco_number = NULL, class = client_logos, asset_code = 'logo', variants = […])
Resolution:
- ECO-197-123-456-789 (ACME) calling
sssr:asset.client_logos.logo→ gets ACME’s logo. - ECO-197-987-654-321 (BRAVO) calling the same ID → gets BRAVO’s logo. A public/anon context → gets the global logo (if present).
Hazard pictograms (global library):
Keep tenant_id = NULL, class = hazard_pictograms, asset_code = '1-1'.
Tenants can override specific pictograms by inserting tenant‑scoped rows; otherwise they inherit the global set.
6.5. Why not encode tenant into the SSSR ID?
We could do sssr:asset.client_logos. ECO-197-123-456-789.logo, but we don’t need to:
- Tenant (
eco_number) is already in auth context; keeping IDs tenant‑agnostic makes references portable across sandboxes and easier to move/merge tenants. - If you ever need to resolve “as another tenant”, you can pass tenantOverride to the resolver (ops/admin use).
6.6. Migration checklist
- For global items, set
eco_number = NULLor ECO-197-000-000-000 (Viroway Ltd). - For client items, set
eco_numberfrom the tenant/org table. - Create the unique index on (
eco_number,class_id,asset_code). - Keep SSSR IDs unchanged (
sssr:asset.<class>.<code>[.<ext>]). - Ensure resolver reads tenant from auth and applies the two‑step lookup with global fallback.
7. fallback_order[]
We keep fallback_order[] in both sssr_asset_registry and sssr_asset_class_registry because they operate at different scopes — one is the class-level default policy, and one is the per-asset override.
7.1. Class-level (sssr_asset_class_registry.defaults.fallback_order)
- Purpose: define the standard order for selecting variants when no asset-specific rule exists.
- Scope: applies to all assets in that class unless overridden.
- **Example:**In
hazard_pictograms, most assets might be small vector or raster icons — so it might default to:
{ "folder": "library/images/pictograms/hazard_pictograms",
"fallback_order": ["svg","gif","tif"] }
- Usage: Resolver checks class.defaults.fallback_order only if the asset doesn’t have its own fallback_order.
7.2. Asset-level (sssr_assets_registry.fallback_order)
- Purpose: override the class policy for this specific asset.
- Scope: only affects that one row in
sssr_assets_registry. - Example: One pictogram is a high-detail photo, not a vector. For that asset, we might say:
json
["tif","eps","gif"]
so the resolver tries high-resolution print formats first.
7.3. Resolution order in practice
When the resolver needs to pick a format (no explicit ext in the SSSR_REF):
- Check asset-level fallback_order[]
- If present → use it directly.
- If not present → go to class level.
- If class has fallback_order[] → use it.
- If neither is present → use system/global default from
sssr_global_config.asset_resolution_policy.
7.4. Why not only keep it in the asset table?
Because most assets don’t need their own override — 90%+ will follow the class default.
If we only stored it in the asset table:
- We’d have to copy the same list into hundreds of rows.
- Changing the default for a class would require bulk updates instead of a single class row edit.
By having both:
- We set once at class level.
- We override only when needed at asset level.
- The resolver has a clear hierarchy:
asset-level override → class default → global default
8. AI autonomy
Below is the schema design that lets us move from AI-assisted to AI-autonomous asset management without losing control.
8.1. Clear Override Hierarchy
Because we’ve separated:
- Global default (in
sssr_global_config) - Class default (in
sssr_asset_class_registry.defaults) - Per-asset override (in
sssr_assets_registry)
…an AI agent can:
- Detect patterns in overrides (e.g., “80% of assets in
hazard_pictogramshave['tif','eps']instead of the default”). - Suggest updating the class default rather than touching 100 individual rows.
- Roll back a bad change easily — just restore the default at the right scope.
8.2. Self-healing on broken links
If a variant is missing (404 or checksum mismatch):
- AI can try the next item in fallback_order[] without human intervention.
- It can log the failure, suggest updating variants[] or the fallback order in the right place.
- It can also spot global issues (e.g., “All .gif variants in hazard_pictograms are failing — check CDN folder path in defaults.folder”).
8.3. Autonomous optimization
Given usage telemetry:
- AI can see which variant is actually being served most often for a context.
- It can propose re-ordering fallback_order[] to match real-world demand.
- For example:“In web context, svg load times are consistently better — moved svg to top of fallback list for
hazard_pictograms.”
8.4. Controlled learning scope
The AI doesn’t have to “guess” where to change things:
- Global change → update
sssr_global_config. - Class change → update
sssr_asset_class_registry.defaults. - One-off fix → update
sssr_assets_registryfor that asset.
This scoping means autonomous changes can be reviewed, sandboxed, and rolled back in a predictable way — no silent drift.
8.5. Easy audit trail
Because all overrides are explicit fields, not hidden in code:
- Every AI “healing” action becomes a data mutation with timestamp, user/agent ID, and diff.
- We can run a periodic SSSR diff to see AI-driven changes vs. human changes.
9. Self-Healing Asset Resolver – AI Autonomy Sketch
This sketch shows how the resolver + AI agent collaborate to detect issues, auto-fallback, propose or apply fixes at the right scope (global → class → asset), and keep the whole loop auditable and safe.
9.1. High-Level Flow
9.2. Resolution + Self-Heal Sequence
9.3. AI Autonomy Loop (Detect → Plan → Act → Verify → Rollback)
9.4. Decision Tree (Where to Change?)
- Is the problem systemic? (e.g., CDN base 5xx globally) → change
sssr_global_config.cdn_base. - Is it class-wide? (e.g., all
hazard_pictograms.gif404) → updatesssr_asset_class_registry.defaults.folderorfallback_order. - Is it isolated to one asset? → update
sssr_assets_registry.variantsor per-assetfallback_order. - Only context-specific regression? → adjust
resolver_policy.variant_selectionfor that class/context.
Always prefer the highest applicable scope to minimize per-row churn and maximize consistency.
9.5. Policy & Constraints (Declarative)
- Variant selection (class):
{
"variant_selection": { "web": ["svg","gif"], "print": ["tif","eps"] },
"fallback_order": ["svg","gif","tif","eps"],
"constraints": {
"print": { "color_space": "CMYK", "min_dpi": 300 }
}
}
Per-asset variants:
{
"variants": [
{"ext":"svg","usage_hint":"web","size":18345},
{"ext":"gif","usage_hint":"web"},
{"ext":"tif","usage_hint":"print","color_space":"CMYK","dpi":300}
]
}
Resolver applies: explicit ext → caller hint → class policy → asset fallback → global default.
9.6. Telemetry Events (for Learning & Audits)
- asset_resolution_success
{ sssr_id, eco_number, class, asset_code, ext, latency_ms, cache_hit, etag } - asset_resolution_fallback
{ from_ext, to_ext, reason, frequency } - asset_resolution_failed
{ class, asset_code, tried_exts[], error_codes[], last_seen } - asset_policy_change_proposed
{ scope, diff } - asset_policy_change_applied
{ scope, diff, actor, ticket } - asset_policy_change_rolled_back
{ scope, reason, rollback_id }
Stream to the MON/DET components. Use thresholds (e.g., >2% failure over 15 min) to trigger autonomous plans.