Step 4/17 av Playground v3-leveransen.
CATALOG-konstant med alle 24 commands per kanonisk archetype-routing-tabell.
Driver:
- Step 5/8: skjema-render via input_fields[]
- Step 9: katalog-UI gruppert på category
- Step 11: parser-routing via report_archetype
- Step 12: renderer-routing via renderer-feltet
- __buildCommand: pipeline-string-bygging per command (Step 8)
Per command-entry:
{ id, category, label, description, argument_hint, calls_agent, kb_files,
produces_report, report_archetype, report_root_class, renderer,
input_fields[] }
input_fields støtter: text, textarea, select, multiSelect, boolean, number.
Felles felter har from='shared' + shared_path (oppslag mot state.shared.*);
lokale felter har from='local' og lagres i project.reports[id].input.
SHARED-shorthand-objekt (9 felles felter brukt på tvers — sektor, virksomhet,
sky-plattform, lisens, AI-tjenester, dataklassifisering, DPIA-praksis, AI-budsjett,
regulatoriske krav). Sikrer eksakt samme label/type på tvers av commands som
deler felt.
Kategori-fordeling per canonical routing-tabell:
regulatory(6): classify, requirements, transparency, frimpact, conformity, dpia
security(3): security, ros, review
economy(2): cost, license
documentation(6): migrate, adr, summary, poc, utredning, compare
tool(7): architect, help, research, diagram, onboard, generate-skills, export
Verktøy-commands har produces_report=false og null for archetype/root/renderer
— Step 11/12 hopper over dem.
Verify-asserts (i nettleser-konsoll):
window.__CATALOG.commands.length === 24
window.__CATALOG.commands.filter(c => c.produces_report).length === 17
window.__CATALOG.commands.find(c => c.id === 'classify').report_archetype === 'aiact'
Eksponerte globals: __CATALOG, __SHARED_FIELDS, __FIELD_TYPES.
Plan: .claude/projects/2026-05-03-playground-v3-architecture/plan.md (Step 4)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1050 lines
48 KiB
HTML
1050 lines
48 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="nb" data-theme="dark">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>ms-ai-architect — Playground v3</title>
|
||
|
||
<!-- Vendored design-system. Kilden er shared/playground-design-system/ — synces via
|
||
scripts/sync-design-system.mjs ved marketplace-rot. Aldri rediger filer under
|
||
playground/vendor/ direkte; endringer går i shared/ + re-sync. -->
|
||
<link rel="stylesheet" href="vendor/playground-design-system/fonts.css">
|
||
<link rel="stylesheet" href="vendor/playground-design-system/tokens.css">
|
||
<link rel="stylesheet" href="vendor/playground-design-system/base.css">
|
||
<link rel="stylesheet" href="vendor/playground-design-system/components.css">
|
||
<link rel="stylesheet" href="vendor/playground-design-system/components-tier2.css">
|
||
<link rel="stylesheet" href="vendor/playground-design-system/components-tier3.css">
|
||
<link rel="stylesheet" href="vendor/playground-design-system/components-tier3-supplement.css">
|
||
</head>
|
||
<body>
|
||
<!-- Walking-skeleton: 4 placeholder-overflater. Step 5-7 fyller dem ut.
|
||
Bare én av disse er aktiv om gangen via state.activeSurface. -->
|
||
<main id="app">
|
||
<section id="surface-onboarding" data-surface="onboarding" hidden></section>
|
||
<section id="surface-home" data-surface="home" hidden></section>
|
||
<section id="surface-catalog" data-surface="catalog" hidden></section>
|
||
<section id="surface-project" data-surface="project" hidden></section>
|
||
</main>
|
||
|
||
<!--
|
||
Klassisk script (ikke type="module") av to grunner:
|
||
1. External <script type="module" src="..."> feiler på file:// i Chrome+Firefox
|
||
(ref WHATWG html#8121, Chromium 41378227).
|
||
2. Single-file deployment per brief Constraints — ingen build-step.
|
||
Kommende steps utvider IIFE-en under: Step 2 (state-modul), Step 3 (eksport/import),
|
||
Step 4 (CATALOG), osv.
|
||
-->
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
|
||
// localStorage-nøkkel og schema-versjon. Endring av STATE_KEY krever migrasjons-steg
|
||
// (se Step 3 — MIGRATIONS-pipeline). SCHEMA_VERSION bumpes ved breaking endringer
|
||
// i state-form og driver eager migrations ved import.
|
||
const STATE_KEY = 'ms-ai-architect-state-v1';
|
||
const SCHEMA_VERSION = 1;
|
||
|
||
// Eksponer som globals for Verify-asserts og DevTools-debugging. Senere steps
|
||
// utvider window.__-namespace med __store, __CATALOG, __PARSERS, __RENDERERS,
|
||
// __buildCommand, __buildEnvelope, __handlePasteImport.
|
||
window.__STATE_KEY = STATE_KEY;
|
||
window.__SCHEMA_VERSION = SCHEMA_VERSION;
|
||
|
||
// ============================================================
|
||
// STATE MODULE (Step 2)
|
||
// ============================================================
|
||
//
|
||
// Reactivity-skjelett: Proxy + EventTarget. set-trap batcher dispatchEvent
|
||
// via queueMicrotask, så N synkrone mutasjoner gir bare én 'change'-event
|
||
// per mikrotask-tick. Bruker dyp wrap (Proxy rekursivt på objekt-properties)
|
||
// så nestede oppdateringer (state.shared.organization.name = ...) fanges.
|
||
//
|
||
// Persistens: IDB primær (~ubegrenset for 1-5 MB), localStorage fallback
|
||
// (5 MiB cap). Open-DB pattern bruker Promise-wrapper og synkrone
|
||
// migrasjoner i onupgradeneeded — async cursor-iterasjon er forbudt
|
||
// (ref w3c/IndexedDB#282: korrupsjons-risiko ved async i upgrade-tx).
|
||
//
|
||
// Multi-tab: db.onversionchange = () => db.close() defensivt på alle
|
||
// koblinger så en versjon-bump i en annen tab ikke stuck-blokkerer denne.
|
||
|
||
class StateBus extends EventTarget {}
|
||
|
||
const sharedBus = new StateBus();
|
||
const projectBus = new StateBus();
|
||
|
||
// Initial state-form. Step 5+ utvider shared.* etter onboarding-skjema;
|
||
// Step 7 utvider projects[]. preferences.theme settes i Step 13.
|
||
const INITIAL_STATE = {
|
||
schemaVersion: SCHEMA_VERSION,
|
||
shared: {
|
||
organization: {},
|
||
technology: {},
|
||
security: {},
|
||
architecture: {},
|
||
business: {}
|
||
},
|
||
projects: [],
|
||
activeProjectId: null,
|
||
activeSurface: 'home',
|
||
preferences: { theme: 'dark' }
|
||
};
|
||
|
||
// Microtask-batched event dispatcher. Mange synkrone set-traps i samme
|
||
// tick → én 'change'-event neste mikrotask. Forhindrer N renders ved
|
||
// batch-mutasjoner (f.eks. import-flow).
|
||
function makeBatchedDispatcher(bus) {
|
||
let pending = false;
|
||
const changedPaths = new Set();
|
||
return function dispatch(path) {
|
||
changedPaths.add(path);
|
||
if (pending) return;
|
||
pending = true;
|
||
queueMicrotask(function () {
|
||
pending = false;
|
||
const paths = Array.from(changedPaths);
|
||
changedPaths.clear();
|
||
bus.dispatchEvent(new CustomEvent('change', { detail: { paths: paths } }));
|
||
});
|
||
};
|
||
}
|
||
|
||
// Dyp Proxy-wrap. Lazy: wrapper child-objekter ved første read, så cost
|
||
// er bare betalt for grener brukeren faktisk berører. set-trap returnerer
|
||
// boolean (Proxy spec-invariant) og dispatcher batched 'change'-event.
|
||
// Path tracking gjør at subscribers kan filtrere på relevante grener.
|
||
function deepProxy(target, dispatch, path) {
|
||
path = path || '';
|
||
const cache = new WeakMap();
|
||
const handler = {
|
||
get: function (obj, key) {
|
||
const value = obj[key];
|
||
if (value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
|
||
if (cache.has(value)) return cache.get(value);
|
||
const childPath = path ? path + '.' + String(key) : String(key);
|
||
const wrapped = new Proxy(value, makeHandler(childPath));
|
||
cache.set(value, wrapped);
|
||
return wrapped;
|
||
}
|
||
if (Array.isArray(value)) {
|
||
// Array-mutasjoner via push/splice trigger set på indekser; wrap likt.
|
||
if (cache.has(value)) return cache.get(value);
|
||
const childPath = path ? path + '.' + String(key) : String(key);
|
||
const wrapped = new Proxy(value, makeHandler(childPath));
|
||
cache.set(value, wrapped);
|
||
return wrapped;
|
||
}
|
||
return value;
|
||
},
|
||
set: function (obj, key, value) {
|
||
obj[key] = value;
|
||
dispatch(path ? path + '.' + String(key) : String(key));
|
||
return true;
|
||
},
|
||
deleteProperty: function (obj, key) {
|
||
delete obj[key];
|
||
dispatch(path ? path + '.' + String(key) : String(key));
|
||
return true;
|
||
}
|
||
};
|
||
function makeHandler(p) {
|
||
return {
|
||
get: function (o, k) { return new Proxy(target, handler).constructor === Proxy ? handler.get(o, k) : o[k]; },
|
||
set: function (o, k, v) { o[k] = v; dispatch(p ? p + '.' + String(k) : String(k)); return true; },
|
||
deleteProperty: function (o, k) { delete o[k]; dispatch(p ? p + '.' + String(k) : String(k)); return true; }
|
||
};
|
||
}
|
||
return new Proxy(target, handler);
|
||
}
|
||
|
||
function createStore(initial, bus) {
|
||
const dispatch = makeBatchedDispatcher(bus);
|
||
const proxied = deepProxy(initial, dispatch, '');
|
||
return {
|
||
state: proxied,
|
||
raw: initial, // referanse til underliggende objekt — for serialisering
|
||
subscribe: function (handler) { bus.addEventListener('change', handler); },
|
||
unsubscribe: function (handler) { bus.removeEventListener('change', handler); }
|
||
};
|
||
}
|
||
|
||
// Throttled persistens: debounce 300 ms etter siste mutasjon, så bursts
|
||
// (import-flow, batch-form-submit) committer bare én gang.
|
||
function makeThrottledWriter(persist) {
|
||
let timer = null;
|
||
return function schedule() {
|
||
if (timer) clearTimeout(timer);
|
||
timer = setTimeout(function () {
|
||
timer = null;
|
||
persist().catch(function (err) {
|
||
console.error('[playground v3] persist failed:', err);
|
||
});
|
||
}, 300);
|
||
};
|
||
}
|
||
|
||
// ============================================================
|
||
// PERSISTENCE LAYER
|
||
// ============================================================
|
||
//
|
||
// IDB primær. Én DB ('ms-ai-architect-playground-v1') med to object-stores:
|
||
// - 'shared': nøkkel 'shared' → { organization, technology, ... }
|
||
// - 'projects': nøkkel 'projectId' → project-objekt
|
||
//
|
||
// Migrasjoner i onupgradeneeded er SYNKRONE per spec — ingen await på
|
||
// cursor.continue(); bruk callback-stil. async cursor-iterasjon i en
|
||
// upgrade-tx kan korruptere DB (w3c/IndexedDB#282).
|
||
|
||
function openDB(name, version) {
|
||
return new Promise(function (resolve, reject) {
|
||
if (typeof indexedDB === 'undefined') {
|
||
reject(new Error('IndexedDB ikke tilgjengelig'));
|
||
return;
|
||
}
|
||
const req = indexedDB.open(name, version);
|
||
req.onupgradeneeded = function (ev) {
|
||
const db = req.result;
|
||
const oldVersion = ev.oldVersion;
|
||
// Synkrone migrasjoner — opprette stores per oldVersion-guard.
|
||
if (oldVersion < 1) {
|
||
if (!db.objectStoreNames.contains('shared')) {
|
||
db.createObjectStore('shared');
|
||
}
|
||
if (!db.objectStoreNames.contains('projects')) {
|
||
db.createObjectStore('projects', { keyPath: 'id' });
|
||
}
|
||
if (!db.objectStoreNames.contains('meta')) {
|
||
db.createObjectStore('meta');
|
||
}
|
||
}
|
||
// Senere bump-er legges til som "if (oldVersion < N)"-blokker.
|
||
};
|
||
req.onsuccess = function () {
|
||
const db = req.result;
|
||
// Defensiv multi-tab: hvis annen tab åpner med høyere versjon,
|
||
// lukk denne så de ikke blokkerer hverandre.
|
||
db.onversionchange = function () {
|
||
db.close();
|
||
console.warn('[playground v3] IDB versionchange — closed for upgrade');
|
||
};
|
||
resolve(db);
|
||
};
|
||
req.onerror = function () { reject(req.error); };
|
||
req.onblocked = function () {
|
||
// En annen tab holder en eldre versjon åpen; usannsynlig i v3
|
||
// (én DB-versjon per release), men logg likevel.
|
||
console.warn('[playground v3] IDB open blocked — another tab holds older version');
|
||
};
|
||
});
|
||
}
|
||
|
||
// Primær persistens: IDB. Ved feil (Safari private mode, kvote-overflow)
|
||
// fallback til localStorage. Returnerer adapter med lik API — kallere
|
||
// trenger ikke vite hvilken backend som er i bruk.
|
||
async function makePersistence() {
|
||
const DB_NAME = 'ms-ai-architect-playground-v1';
|
||
const DB_VERSION = 1;
|
||
try {
|
||
const db = await openDB(DB_NAME, DB_VERSION);
|
||
return {
|
||
backend: 'idb',
|
||
load: function () {
|
||
return new Promise(function (resolve, reject) {
|
||
const tx = db.transaction(['shared', 'projects', 'meta'], 'readonly');
|
||
const sharedReq = tx.objectStore('shared').get('shared');
|
||
const projectsReq = tx.objectStore('projects').getAll();
|
||
const metaReq = tx.objectStore('meta').get('meta');
|
||
tx.oncomplete = function () {
|
||
resolve({
|
||
schemaVersion: (metaReq.result && metaReq.result.schemaVersion) || SCHEMA_VERSION,
|
||
shared: sharedReq.result || INITIAL_STATE.shared,
|
||
projects: projectsReq.result || [],
|
||
activeProjectId: (metaReq.result && metaReq.result.activeProjectId) || null,
|
||
activeSurface: (metaReq.result && metaReq.result.activeSurface) || 'home',
|
||
preferences: (metaReq.result && metaReq.result.preferences) || INITIAL_STATE.preferences
|
||
});
|
||
};
|
||
tx.onerror = function () { reject(tx.error); };
|
||
});
|
||
},
|
||
save: function (state) {
|
||
return new Promise(function (resolve, reject) {
|
||
const tx = db.transaction(['shared', 'projects', 'meta'], 'readwrite');
|
||
tx.objectStore('shared').put(state.shared, 'shared');
|
||
const projectStore = tx.objectStore('projects');
|
||
// Clear-and-rewrite er enkelt og atomær for moderate volum.
|
||
// Ved >100 prosjekter bør dette switch-es til diff-write.
|
||
projectStore.clear();
|
||
for (let i = 0; i < state.projects.length; i++) {
|
||
projectStore.put(state.projects[i]);
|
||
}
|
||
tx.objectStore('meta').put({
|
||
schemaVersion: state.schemaVersion,
|
||
activeProjectId: state.activeProjectId,
|
||
activeSurface: state.activeSurface,
|
||
preferences: state.preferences
|
||
}, 'meta');
|
||
tx.oncomplete = function () { resolve(); };
|
||
tx.onerror = function () { reject(tx.error); };
|
||
});
|
||
}
|
||
};
|
||
} catch (err) {
|
||
console.warn('[playground v3] IDB ikke tilgjengelig, faller tilbake til localStorage:', err && err.message);
|
||
return makeLocalStorageFallback();
|
||
}
|
||
}
|
||
|
||
function makeLocalStorageFallback() {
|
||
return {
|
||
backend: 'localStorage',
|
||
load: function () {
|
||
try {
|
||
const raw = localStorage.getItem(STATE_KEY);
|
||
if (!raw) return Promise.resolve(JSON.parse(JSON.stringify(INITIAL_STATE)));
|
||
return Promise.resolve(JSON.parse(raw));
|
||
} catch (err) {
|
||
console.error('[playground v3] localStorage parse-feil, returnerer initial state:', err);
|
||
return Promise.resolve(JSON.parse(JSON.stringify(INITIAL_STATE)));
|
||
}
|
||
},
|
||
save: function (state) {
|
||
try {
|
||
const payload = JSON.stringify(state);
|
||
// Cap-advarsel: localStorage 5 MiB cap. Ved ~4.5 MB warn brukeren.
|
||
if (payload.length > 4.5 * 1024 * 1024) {
|
||
console.warn('[playground v3] State nærmer seg localStorage 5 MiB cap. Bruk en moderne nettleser med IDB-støtte.');
|
||
}
|
||
localStorage.setItem(STATE_KEY, payload);
|
||
return Promise.resolve();
|
||
} catch (err) {
|
||
return Promise.reject(err);
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
// ============================================================
|
||
// BOOTSTRAP
|
||
// ============================================================
|
||
//
|
||
// Initialiser persistens, last lagret state, opprett store, hook opp
|
||
// throttled writer. Eksponer __store på window for Verify-asserts og
|
||
// DevTools — Step 3 utvider med __buildEnvelope, Step 4 med __CATALOG.
|
||
|
||
let store = null;
|
||
let persistence = null;
|
||
let scheduleWrite = null;
|
||
|
||
async function bootstrap() {
|
||
persistence = await makePersistence();
|
||
const loaded = await persistence.load();
|
||
// Sørg for at schemaVersion finnes (cold start kan returnere uten).
|
||
if (!loaded.schemaVersion) loaded.schemaVersion = SCHEMA_VERSION;
|
||
store = createStore(loaded, sharedBus);
|
||
scheduleWrite = makeThrottledWriter(function () {
|
||
return persistence.save(store.raw);
|
||
});
|
||
store.subscribe(function () { scheduleWrite(); });
|
||
window.__store = store;
|
||
window.__persistence = persistence;
|
||
// Step 6+ vil trigger første render her.
|
||
}
|
||
|
||
// ============================================================
|
||
// EXPORT / IMPORT (Step 3)
|
||
// ============================================================
|
||
//
|
||
// Brukeren kan eksportere hele state som JSON-fil og re-importere på en
|
||
// annen enhet (eller etter localStorage.clear()). Format er en envelope
|
||
// med schemaVersion + appId — så fremtidige versjoner kan lese gamle
|
||
// eksporter via MIGRATIONS-pipeline.
|
||
//
|
||
// File System Access API krever HTTPS (secure context) og er ikke
|
||
// tilgjengelig på file:// — vi bruker Blob + URL.createObjectURL +
|
||
// <a download> for eksport, og <input type="file"> + File.text() for
|
||
// import. Begge fungerer på file:// i alle target-browsers.
|
||
//
|
||
// MIGRATIONS er en eager pipeline: ved import (eller cold-load fra
|
||
// gammel state) kjøres alle migrasjoner sekvensielt fra fil-versjon til
|
||
// gjeldende SCHEMA_VERSION. Aldri hopp over et steg — selv tomme
|
||
// migrasjoner skal være registrert (no-op) for å bevise at hoppet er
|
||
// håndtert.
|
||
|
||
const APP_ID = 'ms-ai-architect-playground';
|
||
|
||
function buildEnvelope() {
|
||
// Snapshot av rå state. JSON.stringify(JSON.parse(...)) sørger for
|
||
// at Proxy-er er stripped; vi vil ikke at envelopet skal beholde
|
||
// wrapper-referanser.
|
||
const snapshot = store ? JSON.parse(JSON.stringify(store.raw)) : JSON.parse(JSON.stringify(INITIAL_STATE));
|
||
return {
|
||
appId: APP_ID,
|
||
schemaVersion: snapshot.schemaVersion || SCHEMA_VERSION,
|
||
exportedAt: new Date().toISOString(),
|
||
shared: snapshot.shared,
|
||
projects: snapshot.projects,
|
||
activeProjectId: snapshot.activeProjectId,
|
||
activeSurface: snapshot.activeSurface,
|
||
preferences: snapshot.preferences
|
||
};
|
||
}
|
||
|
||
function exportState() {
|
||
const envelope = buildEnvelope();
|
||
const json = JSON.stringify(envelope, null, 2);
|
||
const blob = new Blob([json], { type: 'application/json' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
const stamp = envelope.exportedAt.replace(/[:.]/g, '-');
|
||
a.href = url;
|
||
a.download = APP_ID + '-' + stamp + '.json';
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
// revokeObjectURL etter at klikket har skjedd; setTimeout 0 er nok i
|
||
// alle target-browsers (Blob URL holdes så lenge nedlastningen står
|
||
// for å initieres).
|
||
setTimeout(function () { URL.revokeObjectURL(url); }, 0);
|
||
return envelope;
|
||
}
|
||
|
||
// MIGRATIONS-pipeline. Nøkkel-format: 'N->M' der N og M er fortløpende
|
||
// SCHEMA_VERSION-tall. Funksjon tar et state-objekt og returnerer et
|
||
// nytt state-objekt på neste versjon. Aldri muter input.
|
||
//
|
||
// Når SCHEMA_VERSION bumpes til 2: legg til '1->2'-funksjon som
|
||
// transformerer v1-state til v2-form. importState plukker opp
|
||
// migrasjonen automatisk.
|
||
const MIGRATIONS = {
|
||
// Eksempel for fremtid (no-op stub):
|
||
// '1->2': function (state) { return Object.assign({}, state, { schemaVersion: 2 }); }
|
||
};
|
||
|
||
function migrateState(state) {
|
||
let current = state;
|
||
let from = current.schemaVersion || 1;
|
||
while (from < SCHEMA_VERSION) {
|
||
const key = from + '->' + (from + 1);
|
||
const fn = MIGRATIONS[key];
|
||
if (!fn) {
|
||
throw new Error('[playground v3] mangler migrasjon ' + key + ' — kan ikke trygt oppgradere import-fil');
|
||
}
|
||
current = fn(current);
|
||
if (current.schemaVersion !== from + 1) {
|
||
throw new Error('[playground v3] migrasjon ' + key + ' satte ikke schemaVersion til ' + (from + 1));
|
||
}
|
||
from = current.schemaVersion;
|
||
}
|
||
return current;
|
||
}
|
||
|
||
async function importState(file) {
|
||
// file er File-objekt fra <input type="file"> change-event.
|
||
// file.text() er Promise<string> — fungerer på file:// uten secure context.
|
||
const text = await file.text();
|
||
let envelope;
|
||
try {
|
||
envelope = JSON.parse(text);
|
||
} catch (err) {
|
||
throw new Error('Ugyldig JSON: ' + err.message);
|
||
}
|
||
if (envelope.appId !== APP_ID) {
|
||
throw new Error('Fil-en er ikke en ' + APP_ID + '-eksport (appId=' + envelope.appId + ')');
|
||
}
|
||
if (typeof envelope.schemaVersion !== 'number') {
|
||
throw new Error('Mangler schemaVersion i envelope');
|
||
}
|
||
// Migrer envelope opp til gjeldende SCHEMA_VERSION før vi commit-er.
|
||
const migrated = migrateState({
|
||
schemaVersion: envelope.schemaVersion,
|
||
shared: envelope.shared || INITIAL_STATE.shared,
|
||
projects: envelope.projects || [],
|
||
activeProjectId: envelope.activeProjectId || null,
|
||
activeSurface: envelope.activeSurface || 'home',
|
||
preferences: envelope.preferences || INITIAL_STATE.preferences
|
||
});
|
||
// Skriv direkte til persistens for å unngå at debounce-vinduet
|
||
// svelger import-en ved en samtidig page-unload.
|
||
if (persistence) {
|
||
await persistence.save(migrated);
|
||
}
|
||
// Erstatt store-state in-place. Vi kan ikke bytte ut store.raw
|
||
// sin referanse fordi Proxy-en er bundet til den; muter feltvis.
|
||
const target = store.raw;
|
||
target.schemaVersion = migrated.schemaVersion;
|
||
target.shared = migrated.shared;
|
||
target.projects = migrated.projects;
|
||
target.activeProjectId = migrated.activeProjectId;
|
||
target.activeSurface = migrated.activeSurface;
|
||
target.preferences = migrated.preferences;
|
||
// Trigger en change-event manuelt så subscribers re-rendrer.
|
||
sharedBus.dispatchEvent(new CustomEvent('change', { detail: { paths: ['*'] } }));
|
||
return migrated;
|
||
}
|
||
|
||
// Eksponer for UI-handlere (Step 5+) og DevTools-debugging.
|
||
window.__buildEnvelope = buildEnvelope;
|
||
window.__exportState = exportState;
|
||
window.__importState = importState;
|
||
window.__MIGRATIONS = MIGRATIONS;
|
||
|
||
// ============================================================
|
||
// COMMAND CATALOG (Step 4)
|
||
// ============================================================
|
||
//
|
||
// Kanonisk single-source-of-truth for alle 24 commands. Driver:
|
||
// - Step 5/8: skjema-render via input_fields[]
|
||
// - Step 9: katalog-UI gruppert på category
|
||
// - Step 11: parser-routing via report_archetype
|
||
// - Step 12: renderer-routing via renderer-feltet
|
||
// - __buildCommand: pipeline-string-bygging per command
|
||
//
|
||
// Felles-state-felter har from='shared' + shared_path='group.field'
|
||
// (oppslag mot state.shared.<group>.<field>). Lokale felter har
|
||
// from='local' og lagres i project.reports[id].input.
|
||
//
|
||
// Verktøy-commands (architect, help, research, diagram, onboard,
|
||
// generate-skills, export) har produces_report=false og null for
|
||
// archetype/root/renderer — Step 11/12 hopper over dem.
|
||
|
||
const FIELD_TYPES = {
|
||
TEXT: 'text',
|
||
TEXTAREA: 'textarea',
|
||
SELECT: 'select',
|
||
MULTI_SELECT: 'multiSelect',
|
||
BOOLEAN: 'boolean',
|
||
NUMBER: 'number'
|
||
};
|
||
|
||
// Felles felt-shorthands. Holder CATALOG kompakt og sikrer at samme
|
||
// felles-felt har eksakt samme label/type på tvers av alle commands
|
||
// som bruker det.
|
||
const SHARED = {
|
||
organisation_name: { id: 'organisation_name', label: 'Virksomhet', type: 'text', from: 'shared', shared_path: 'organization.name' },
|
||
sector: { id: 'sector', label: 'Sektor', type: 'select', from: 'shared', shared_path: 'organization.sector', options: ['Statlig', 'Kommunal', 'Fylkeskommune', 'Helseforetak', 'Undervisning', 'Annet'] },
|
||
regulatory_requirements: { id: 'regulatory_requirements', label: 'Regulatoriske krav', type: 'multiSelect', from: 'shared', shared_path: 'organization.regulatory_requirements', options: ['Personopplysningsloven/GDPR', 'Sikkerhetsloven', 'Arkivloven', 'Forvaltningsloven', 'Offentleglova', 'Helseregisterloven', 'Annet'] },
|
||
cloud_platform: { id: 'cloud_platform', label: 'Skyplattform', type: 'multiSelect', from: 'shared', shared_path: 'technology.cloud_platform', options: ['Azure', 'M365', 'Power Platform', 'On-prem', 'Hybrid', 'Annet'] },
|
||
license_type: { id: 'license_type', label: 'Lisenstype', type: 'select', from: 'shared', shared_path: 'technology.license_type', options: ['E3', 'E5', 'F1/F3', 'A3/A5', 'G3/G5', 'Annet'] },
|
||
ai_services_in_use: { id: 'ai_services_in_use', label: 'AI-tjenester i bruk', type: 'multiSelect', from: 'shared', shared_path: 'technology.ai_services_in_use', options: ['Azure OpenAI', 'Copilot for M365', 'Copilot Studio', 'AI Builder', 'Azure AI Search', 'Azure AI Services', 'Ingen', 'Annet'] },
|
||
data_classification: { id: 'data_classification', label: 'Dataklassifisering', type: 'multiSelect', from: 'shared', shared_path: 'security.data_classification', options: ['Åpen', 'Intern', 'Fortrolig', 'Strengt fortrolig', 'Hemmelig'] },
|
||
dpia_practice: { id: 'dpia_practice', label: 'DPIA-praksis i organisasjonen', type: 'select', from: 'shared', shared_path: 'security.dpia_practice', options: ['Systematisk', 'Ad hoc', 'Ikke etablert', 'Usikker'] },
|
||
annual_ai_budget: { id: 'annual_ai_budget', label: 'Årlig AI-budsjett', type: 'select', from: 'shared', shared_path: 'architecture.annual_ai_budget', options: ['<500k', '500k-2M', '2M-10M', '>10M', 'Ikke definert'] }
|
||
};
|
||
|
||
const PLATFORMS = ['Azure AI Foundry', 'Copilot Studio', 'M365 Copilot', 'Power Automate', 'AI Builder', 'Azure OpenAI', 'Azure AI Search', 'Annet'];
|
||
const RISK_LEVELS = ['minimal', 'limited', 'high', 'forbidden', 'ukjent'];
|
||
const ORG_ROLES = ['provider', 'deployer', 'distributor', 'importer', 'usikker'];
|
||
|
||
const CATALOG = {
|
||
version: '1.0',
|
||
generated_for_schema: SCHEMA_VERSION,
|
||
categories: [
|
||
{ id: 'regulatory', label: 'Regulatorisk', count: 6 },
|
||
{ id: 'security', label: 'Sikkerhet', count: 3 },
|
||
{ id: 'economy', label: 'Økonomi', count: 2 },
|
||
{ id: 'documentation', label: 'Dokumentasjon', count: 6 },
|
||
{ id: 'tool', label: 'Verktøy', count: 7 }
|
||
],
|
||
commands: [
|
||
// ===== REGULATORY (6) =====
|
||
{
|
||
id: 'classify',
|
||
category: 'regulatory',
|
||
label: 'EU AI Act — Klassifisering',
|
||
description: 'Klassifiser AI-system etter EU AI Act-risikonivå (forbidden/high/limited/minimal) og bestem rolle.',
|
||
argument_hint: '[system-beskrivelse]',
|
||
calls_agent: 'ai-act-assessor',
|
||
kb_files: ['ai-act-classification-methodology.md', 'ai-act-annex-iii-checklist.md', 'ai-act-compliance-guide.md'],
|
||
produces_report: true,
|
||
report_archetype: 'aiact',
|
||
report_root_class: 'pyramide',
|
||
renderer: 'renderAiActPyramid',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'system_name', label: 'Systemnavn', type: 'text', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'users', label: 'Brukere', type: 'text', from: 'local' },
|
||
{ id: 'interaction_type', label: 'Interaksjonstype', type: 'select', from: 'local', options: ['chatbot', 'beslutningsstøtte', 'automatisering', 'anbefaling', 'annet'] },
|
||
{ id: 'data_sources', label: 'Datakilder', type: 'textarea', from: 'local' },
|
||
{ id: 'risk_level_assumption', label: 'Risk-level (forhåndsvurdering)', type: 'select', from: 'local', options: RISK_LEVELS }
|
||
]
|
||
},
|
||
{
|
||
id: 'requirements',
|
||
category: 'regulatory',
|
||
label: 'EU AI Act — Krav per risiko + rolle',
|
||
description: 'Konkrete AI Act-forpliktelser basert på klassifisering og rolle (provider/deployer).',
|
||
argument_hint: '[system-beskrivelse el. klassifisering]',
|
||
calls_agent: 'ai-act-assessor',
|
||
kb_files: ['ai-act-provider-obligations.md', 'ai-act-deployer-obligations.md', 'ai-act-microsoft-tools-mapping.md'],
|
||
produces_report: true,
|
||
report_archetype: 'requirements-list',
|
||
report_root_class: 'findings',
|
||
renderer: 'renderRequirements',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'system_name', label: 'Systemnavn', type: 'text', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'risk_classification', label: 'Risikoklassifisering', type: 'select', from: 'local', options: RISK_LEVELS },
|
||
{ id: 'org_role', label: 'Rolle', type: 'select', from: 'local', options: ORG_ROLES }
|
||
]
|
||
},
|
||
{
|
||
id: 'transparency',
|
||
category: 'regulatory',
|
||
label: 'Transparensnotis (Art. 13/50)',
|
||
description: 'Generer Art. 13/50-transparensnotis på norsk for AI-system.',
|
||
argument_hint: '[system-beskrivelse]',
|
||
calls_agent: 'ai-act-assessor',
|
||
kb_files: ['ai-act-transparency-notices.md'],
|
||
produces_report: true,
|
||
report_archetype: 'text-document',
|
||
report_root_class: 'markdown-fallback',
|
||
renderer: 'renderTransparency',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'system_name', label: 'Systemnavn', type: 'text', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'interaction_type', label: 'Interaksjonstype', type: 'select', from: 'local', options: ['chatbot', 'beslutningsstøtte', 'automatisering', 'anbefaling', 'annet'] },
|
||
{ id: 'target_audience', label: 'Målgruppe', type: 'text', from: 'local' },
|
||
{ id: 'risk_classification', label: 'Risikoklassifisering', type: 'select', from: 'local', options: RISK_LEVELS }
|
||
]
|
||
},
|
||
{
|
||
id: 'frimpact',
|
||
category: 'regulatory',
|
||
label: 'FRIA (Art. 27)',
|
||
description: 'Fundamental Rights Impact Assessment — obligatorisk for offentlig sektor som deployer.',
|
||
argument_hint: '[system-beskrivelse]',
|
||
calls_agent: 'ai-act-assessor',
|
||
kb_files: ['ai-act-fria-template.md', 'ai-act-deployer-obligations.md'],
|
||
produces_report: true,
|
||
report_archetype: 'fria',
|
||
report_root_class: 'rights-matrix',
|
||
renderer: 'renderFria',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'system_name', label: 'Systemnavn', type: 'text', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'affected_groups', label: 'Berørte grupper', type: 'textarea', from: 'local' },
|
||
{ id: 'decisions_affected', label: 'Beslutninger som påvirkes', type: 'textarea', from: 'local' },
|
||
{ id: 'risk_classification', label: 'Risikoklassifisering', type: 'select', from: 'local', options: RISK_LEVELS }
|
||
]
|
||
},
|
||
{
|
||
id: 'conformity',
|
||
category: 'regulatory',
|
||
label: 'Samsvarsvurdering (Art. 43)',
|
||
description: 'Annex IV-sjekkliste og EU-erklæring for høyrisiko AI-systemer.',
|
||
argument_hint: '[system-beskrivelse]',
|
||
calls_agent: 'ai-act-assessor',
|
||
kb_files: ['ai-act-conformity-assessment.md', 'ai-act-provider-obligations.md'],
|
||
produces_report: true,
|
||
report_archetype: 'conformity-checklist',
|
||
report_root_class: 'findings',
|
||
renderer: 'renderConformity',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'system_name', label: 'Systemnavn', type: 'text', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'risk_classification', label: 'Risikoklassifisering', type: 'select', from: 'local', options: RISK_LEVELS },
|
||
{ id: 'org_role', label: 'Rolle', type: 'select', from: 'local', options: ORG_ROLES },
|
||
{ id: 'existing_documentation', label: 'Eksisterende dokumentasjon', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'dpia',
|
||
category: 'regulatory',
|
||
label: 'DPIA / PVK',
|
||
description: 'Personvernkonsekvensvurdering for AI-system med risikomatrise og tiltakstabell.',
|
||
argument_hint: '[system-beskrivelse]',
|
||
calls_agent: 'dpia-agent',
|
||
kb_files: ['dpia-norwegian-methodology-ai.md', 'gdpr-compliance-ai-systems.md', 'ai-impact-assessment-framework.md'],
|
||
produces_report: true,
|
||
report_archetype: 'matrix-risk',
|
||
report_root_class: 'matrix',
|
||
renderer: 'renderDpia',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
SHARED.dpia_practice,
|
||
{ id: 'system_name', label: 'Systemnavn', type: 'text', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'personal_data_types', label: 'Typer personopplysninger', type: 'textarea', from: 'local' },
|
||
{ id: 'data_subjects', label: 'Registrerte (data subjects)', type: 'text', from: 'local' },
|
||
{ id: 'legal_basis', label: 'Behandlingsgrunnlag (GDPR Art. 6)', type: 'select', from: 'local', options: ['Samtykke', 'Avtale', 'Rettslig forpliktelse', 'Vitale interesser', 'Allmenn interesse', 'Berettiget interesse'] },
|
||
{ id: 'data_sources', label: 'Datakilder', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
|
||
// ===== SECURITY (3) =====
|
||
{
|
||
id: 'security',
|
||
category: 'security',
|
||
label: 'Sikkerhetsvurdering (6×5)',
|
||
description: 'Sikkerhetsvurdering på 6 dimensjoner med 1-5 score, OWASP LLM Top 10.',
|
||
argument_hint: '[plattform] for [bruksscenario]',
|
||
calls_agent: 'security-assessment-agent',
|
||
kb_files: ['security-scoring-rubrics-6x5.md', 'ai-security-scoring-framework.md', 'ai-threat-modeling-stride.md'],
|
||
produces_report: true,
|
||
report_archetype: 'matrix-risk-6x5',
|
||
report_root_class: 'matrix',
|
||
renderer: 'renderSecurity',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
SHARED.cloud_platform,
|
||
SHARED.data_classification,
|
||
{ id: 'platform', label: 'Plattform', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'use_case', label: 'Bruksscenario', type: 'textarea', from: 'local' },
|
||
{ id: 'citizen_facing', label: 'Eksponert for innbyggere?', type: 'boolean', from: 'local' },
|
||
{ id: 'data_sources', label: 'Datakilder', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'ros',
|
||
category: 'security',
|
||
label: 'ROS-analyse (NS 5814 / ISO 31000)',
|
||
description: 'Risiko- og sårbarhetsanalyse med 7 dimensjoner og 49-trussel-bibliotek.',
|
||
argument_hint: '[system-beskrivelse] [--quick]',
|
||
calls_agent: 'ros-analysis-agent',
|
||
kb_files: ['ros-ai-threat-library.md', 'ros-scoring-rubrics-7x5.md', 'ros-methodology-ns5814-iso31000.md'],
|
||
produces_report: true,
|
||
report_archetype: 'matrix-risk',
|
||
report_root_class: 'matrix',
|
||
renderer: 'renderRos',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'system_name', label: 'Systemnavn', type: 'text', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'complexity', label: 'Kompleksitet', type: 'select', from: 'local', options: ['ENKEL', 'MIDDELS', 'KOMPLEKS'] },
|
||
{ id: 'quick_mode', label: 'Hurtig-modus (mal A)', type: 'boolean', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'review',
|
||
category: 'security',
|
||
label: 'Arkitekturgjennomgang',
|
||
description: 'Gjennomgang mot Digdir, AI Act, NSM, Schrems II og norsk offentlig sektor-krav.',
|
||
argument_hint: '[arkitektur el. kontekst]',
|
||
calls_agent: 'architecture-review-agent',
|
||
kb_files: ['decision-trees.md', 'security.md', 'public-sector-checklist.md'],
|
||
produces_report: true,
|
||
report_archetype: 'findings',
|
||
report_root_class: 'findings',
|
||
renderer: 'renderReview',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'architecture_description', label: 'Arkitekturbeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'review_stage', label: 'Stadium', type: 'select', from: 'local', options: ['Pre-implementering', 'POC', 'Produksjon'] }
|
||
]
|
||
},
|
||
|
||
// ===== ECONOMY (2) =====
|
||
{
|
||
id: 'cost',
|
||
category: 'economy',
|
||
label: 'Kostnadsestimat (P10/P50/P90 NOK)',
|
||
description: 'Kostnadsestimering med konfidensgradering og TCO-sammenligning.',
|
||
argument_hint: '[plattform] med [antall brukere], [volum/dag]',
|
||
calls_agent: 'cost-estimation-agent',
|
||
kb_files: ['deterministic-cost-calculation-model.md', 'azure-ai-foundry-cost-governance.md', 'cost-models.md'],
|
||
produces_report: true,
|
||
report_archetype: 'cost-distribution',
|
||
report_root_class: 'distribution',
|
||
renderer: 'renderCost',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.license_type,
|
||
SHARED.cloud_platform,
|
||
{ id: 'platform', label: 'Plattform', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'users', label: 'Antall brukere', type: 'number', from: 'local' },
|
||
{ id: 'volume_per_day', label: 'Volum per dag (transaksjoner/forespørsler)', type: 'text', from: 'local' },
|
||
{ id: 'region', label: 'Region', type: 'select', from: 'local', options: ['Norge (Norway East/West)', 'EU/EØS', 'Globalt'] }
|
||
]
|
||
},
|
||
{
|
||
id: 'license',
|
||
category: 'economy',
|
||
label: 'Lisens → AI-kapabiliteter',
|
||
description: 'Map lisenstype mot inkluderte AI-kapabiliteter og identifiser gap.',
|
||
argument_hint: '[lisenstype]',
|
||
calls_agent: 'license-mapper-agent',
|
||
kb_files: ['licensing-matrix.md'],
|
||
produces_report: true,
|
||
report_archetype: 'capability',
|
||
report_root_class: 'capability-matrix',
|
||
renderer: 'renderLicense',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.license_type,
|
||
SHARED.ai_services_in_use,
|
||
{ id: 'license_types', label: 'Lisenser å vurdere', type: 'multiSelect', from: 'local', options: ['E3', 'E5', 'F1/F3', 'A3/A5', 'G3/G5', 'Copilot for M365', 'Power Platform Premium'] }
|
||
]
|
||
},
|
||
|
||
// ===== DOCUMENTATION (6) =====
|
||
{
|
||
id: 'migrate',
|
||
category: 'documentation',
|
||
label: 'Migreringsplan',
|
||
description: 'Plan for migrasjon mellom Microsoft AI-plattformer.',
|
||
argument_hint: 'fra [kilde] til [mål]',
|
||
calls_agent: null,
|
||
kb_files: ['migration-patterns.md'],
|
||
produces_report: true,
|
||
report_archetype: 'phased-plan',
|
||
report_root_class: 'aiact-timeline',
|
||
renderer: 'renderMigrate',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
{ id: 'source_platform', label: 'Fra (kildeplattform)', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'target_platform', label: 'Til (målplattform)', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'timeline_weeks', label: 'Tidslinje (uker)', type: 'number', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'adr',
|
||
category: 'documentation',
|
||
label: 'ADR (MADR v3.0)',
|
||
description: 'Architecture Decision Record i MADR v3.0-format.',
|
||
argument_hint: '[valgfritt: tittel]',
|
||
calls_agent: 'adr-writer-agent',
|
||
kb_files: ['adr-template.md'],
|
||
produces_report: true,
|
||
report_archetype: 'markdown',
|
||
report_root_class: 'markdown-fallback',
|
||
renderer: 'renderAdr',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
{ id: 'decision_title', label: 'Beslutningstittel', type: 'text', from: 'local' },
|
||
{ id: 'decision_context', label: 'Kontekst', type: 'textarea', from: 'local' },
|
||
{ id: 'alternatives', label: 'Alternativer vurdert', type: 'textarea', from: 'local' },
|
||
{ id: 'decision', label: 'Valgt løsning', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'summary',
|
||
category: 'documentation',
|
||
label: 'Teknisk sammendrag + beslutningsnotat',
|
||
description: 'Aggregerer .work/-rapporter til teknisk sammendrag og beslutningsnotat.',
|
||
argument_hint: '[løsningsnavn]',
|
||
calls_agent: 'summary-agent',
|
||
kb_files: [],
|
||
produces_report: true,
|
||
report_archetype: 'verdict',
|
||
report_root_class: 'verdict-block',
|
||
renderer: 'renderSummary',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
{ id: 'solution_name', label: 'Løsningsnavn', type: 'text', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'poc',
|
||
category: 'documentation',
|
||
label: 'POC-plan',
|
||
description: 'POC-plan med suksesskriterier, tidslinje, risiko og Go/No-Go.',
|
||
argument_hint: '[plattform] for [use case]',
|
||
calls_agent: null,
|
||
kb_files: ['poc-template.md'],
|
||
produces_report: true,
|
||
report_archetype: 'phased-plan',
|
||
report_root_class: 'pipeline-cockpit',
|
||
renderer: 'renderPoc',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
SHARED.annual_ai_budget,
|
||
{ id: 'platform', label: 'Plattform', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'use_case', label: 'Use case', type: 'textarea', from: 'local' },
|
||
{ id: 'team_size', label: 'Team-størrelse', type: 'number', from: 'local' },
|
||
{ id: 'team_level', label: 'Team-nivå', type: 'select', from: 'local', options: ['Junior', 'Mid', 'Senior', 'Mixed'] },
|
||
{ id: 'timeline_weeks', label: 'Tidslinje (uker)', type: 'number', from: 'local' },
|
||
{ id: 'stakeholders', label: 'Interessenter', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'utredning',
|
||
category: 'documentation',
|
||
label: 'AI-arkitekturutredning (off. sektor)',
|
||
description: 'Full S0–S9 arkitekturutredning for norsk offentlig sektor.',
|
||
argument_hint: '[scenario]',
|
||
calls_agent: null,
|
||
kb_files: ['ai-utredning-template.md'],
|
||
produces_report: true,
|
||
report_archetype: 'markdown',
|
||
report_root_class: 'markdown-fallback',
|
||
renderer: 'renderUtredning',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
SHARED.regulatory_requirements,
|
||
{ id: 'scenario_name', label: 'Scenario-navn', type: 'text', from: 'local' },
|
||
{ id: 'scenario_description', label: 'Scenario-beskrivelse', type: 'textarea', from: 'local' },
|
||
{ id: 'system_description', label: 'Systembeskrivelse', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'compare',
|
||
category: 'documentation',
|
||
label: 'Sammenlign plattformer',
|
||
description: 'Side-by-side sammenligning av Microsoft AI-plattformer for et use case.',
|
||
argument_hint: '[plattform A] vs [plattform B] for [use case]',
|
||
calls_agent: 'research-agent',
|
||
kb_files: ['decision-trees.md'],
|
||
produces_report: true,
|
||
report_archetype: 'comparison',
|
||
report_root_class: 'diff',
|
||
renderer: 'renderCompare',
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
{ id: 'platform_a', label: 'Plattform A', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'platform_b', label: 'Plattform B', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'use_case', label: 'Use case', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
|
||
// ===== TOOL (7) — ingen rapport, kun skjema + output-kopiering =====
|
||
{
|
||
id: 'architect',
|
||
category: 'tool',
|
||
label: 'Start Cosmo-rådgivning',
|
||
description: 'Start strukturert AI-arkitekturrådgivning med Cosmo Skyberg-persona.',
|
||
argument_hint: '[beskriv ditt forretningsproblem]',
|
||
calls_agent: null,
|
||
kb_files: [],
|
||
produces_report: false,
|
||
report_archetype: null,
|
||
report_root_class: null,
|
||
renderer: null,
|
||
input_fields: [
|
||
SHARED.organisation_name,
|
||
SHARED.sector,
|
||
{ id: 'business_problem', label: 'Forretningsproblem', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'help',
|
||
category: 'tool',
|
||
label: 'Hjelp',
|
||
description: 'Vis kommando-/agent-/KB-oversikt eller detaljer for et emne.',
|
||
argument_hint: '[emne for detaljer]',
|
||
calls_agent: null,
|
||
kb_files: [],
|
||
produces_report: false,
|
||
report_archetype: null,
|
||
report_root_class: null,
|
||
renderer: null,
|
||
input_fields: [
|
||
{ id: 'topic', label: 'Emne (valgfritt)', type: 'text', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'research',
|
||
category: 'tool',
|
||
label: 'Plattform-research',
|
||
description: 'Siste-nytt-research for en Microsoft AI-plattform.',
|
||
argument_hint: '[plattformnavn] [tidsperiode]',
|
||
calls_agent: 'research-agent',
|
||
kb_files: [],
|
||
produces_report: false,
|
||
report_archetype: null,
|
||
report_root_class: null,
|
||
renderer: null,
|
||
input_fields: [
|
||
{ id: 'platform', label: 'Plattform', type: 'select', from: 'local', options: PLATFORMS },
|
||
{ id: 'time_period', label: 'Tidsperiode', type: 'select', from: 'local', options: ['siste uke', 'siste måned', 'siste kvartal', 'siste år'] }
|
||
]
|
||
},
|
||
{
|
||
id: 'diagram',
|
||
category: 'tool',
|
||
label: 'Generer arkitekturdiagram',
|
||
description: 'Generer arkitekturdiagram med Imagen 3 (mcp-image).',
|
||
argument_hint: '[type] for [scenario]',
|
||
calls_agent: 'diagram-generation-agent',
|
||
kb_files: ['diagram-prompt-templates.md'],
|
||
produces_report: false,
|
||
report_archetype: null,
|
||
report_root_class: null,
|
||
renderer: null,
|
||
input_fields: [
|
||
{ id: 'diagram_type', label: 'Diagramtype', type: 'select', from: 'local', options: ['arkitektur', 'sikkerhet', 'dataflyt', 'problem', 'roadmap'] },
|
||
{ id: 'scenario', label: 'Scenario', type: 'text', from: 'local' },
|
||
{ id: 'component_list', label: 'Komponenter (valgfritt)', type: 'textarea', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'onboard',
|
||
category: 'tool',
|
||
label: 'Onboard plugin',
|
||
description: 'Onboard pluginen med virksomhetsspesifikk kontekst (5-fase intervju).',
|
||
argument_hint: '[--status]',
|
||
calls_agent: 'onboarding-agent',
|
||
kb_files: [],
|
||
produces_report: false,
|
||
report_archetype: null,
|
||
report_root_class: null,
|
||
renderer: null,
|
||
input_fields: [
|
||
{ id: 'status_only', label: 'Bare vis status', type: 'boolean', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'generate-skills',
|
||
category: 'tool',
|
||
label: 'Generer KB-filer (batch)',
|
||
description: 'Generer kunnskapsfiler med MCP-research i batch.',
|
||
argument_hint: '[antall]',
|
||
calls_agent: null,
|
||
kb_files: [],
|
||
produces_report: false,
|
||
report_archetype: null,
|
||
report_root_class: null,
|
||
renderer: null,
|
||
input_fields: [
|
||
{ id: 'count', label: 'Antall filer å generere', type: 'number', from: 'local' }
|
||
]
|
||
},
|
||
{
|
||
id: 'export',
|
||
category: 'tool',
|
||
label: 'Eksporter til PDF',
|
||
description: 'Eksporter et arkitekturdokument til PDF.',
|
||
argument_hint: '[filsti til markdown]',
|
||
calls_agent: null,
|
||
kb_files: [],
|
||
produces_report: false,
|
||
report_archetype: null,
|
||
report_root_class: null,
|
||
renderer: null,
|
||
input_fields: [
|
||
{ id: 'file_path', label: 'Filsti til markdown', type: 'text', from: 'local' }
|
||
]
|
||
}
|
||
]
|
||
};
|
||
|
||
// Eksponer for Step 5/8/9/11/12 og DevTools.
|
||
window.__CATALOG = CATALOG;
|
||
window.__SHARED_FIELDS = SHARED;
|
||
window.__FIELD_TYPES = FIELD_TYPES;
|
||
|
||
// Auto-bootstrap. Kjør så snart DOM er parsed; vi er på slutten av <body>
|
||
// så DOM er allerede klar.
|
||
bootstrap().catch(function (err) {
|
||
console.error('[playground v3] bootstrap failed:', err);
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|