Plan de contrôle
Accès MCP production et stdio local au plan de contrôle pour clients MCP et automatisations.
Vue D'Ensemble
Yayaw expose un plan de contrôle production via MCP. Codex est un client pris en charge, mais l'endpoint est conçu pour tout client MCP ou runner d'automatisation de confiance.
- La production et le staging utilisent Streamable HTTP sur
/api/mcp. - Le développement local peut utiliser stdio avec
bun --conditions react-server --env-file=.env src/lib/scripts/mcp/yayaw-mcp-server.ts. - Les requêtes MCP production peuvent s'authentifier avec une clé API Better Auth ou un access token OAuth Better Auth. Les deux chemins utilisent des permissions explicites de plan de contrôle.
- Les requêtes runtime data-plane utilisent le même format d'acteur bearer, mais
peuvent être bornées à la ressource de clé API plus étroite
dynamic-data-runtime. - Le réglage de site
control-plane-mcp-enabledest le kill switch serveur. - L'autorisation Yayaw sous-jacente passe toujours par
can(...)ou des checks équivalents de membership borné.
Permissions Des Clés API
Les clés API utilisent les permissions Better Auth API key sous la ressource
control-plane:
read: statut, discovery, ressources, outils list/get.write: mutations brouillon/données/média/design-token.publish: outils de cycle de vie publish/archive.admin: administration d'audit et de feature flags.
admin ne contourne pas l'autorisation de ressource Yayaw. Il satisfait
seulement la porte de permission du plan de contrôle.
Les appelants runtime chauds devraient préférer la ressource plus étroite
dynamic-data-runtime:
read: exécuter les routes runtime dynamiques déployées en lecture seule.write: exécuter les routes runtime dynamiques d'écriture comme heartbeat, mises à jour de statut, claims de pairing ou ingestion de metrics.
dynamic-data-runtime:write implique l'accès runtime read lorsque l'UI
Developer normalise les permissions. Les clés MCP complètes existantes restent
compatibles avec le data-plane: control-plane:read peut exécuter les routes
runtime en lecture, tandis que control-plane:write ou control-plane:admin
peut exécuter les routes runtime d'écriture. Tous les chemins exécutent encore
l'autorisation Yayaw dynamic-data après les checks de permission API key, donc
les scopes bearer ne remplacent jamais l'autorisation par groupes.
Connexion MCP Production
Créez une clé MCP production, stockez-la dans l'environnement du client comme
YAYAW_MCP_API_KEY, puis configurez le client MCP. Codex peut utiliser:
[mcp_servers.yayaw-prod]
url = "https://yayaw.app/api/mcp"
bearer_token_env_var = "YAYAW_MCP_API_KEY"Avant la connexion, activez le réglage de site:
bun run seedPuis passez control-plane-mcp-enabled à true depuis les réglages Admin ou
avec l'outil de flag du plan de contrôle en utilisant une clé admin existante.
Connexion MCP OAuth
Yayaw expose aussi une discovery OAuth 2.1 compatible MCP pour ChatGPT Apps et les autres clients qui suivent le flux d'autorisation MCP. OAuth supporte authorization code avec PKCE et refresh tokens; l'accès machine-to-machine doit continuer à utiliser les clés API MCP.
- Métadonnées de ressource protégée:
/.well-known/oauth-protected-resource/api/mcp - Métadonnées du serveur d'autorisation:
/.well-known/oauth-authorization-server/api/auth - Métadonnées OpenID:
/.well-known/openid-configuration/api/auth - Base du serveur d'autorisation:
/api/auth - Ressource/audience MCP:
/api/mcp
L'endpoint MCP renvoie un challenge WWW-Authenticate sur les requêtes OAuth
non authentifiées ou invalides afin que les clients découvrent les métadonnées
de ressource protégée. La politique CORS du MCP distant autorise exactement
l'origine https://chatgpt.com et expose le challenge aux clients navigateur,
tout en refusant les origines sans rapport. Les tokens OAuth sont vérifiés avec
le JWKS Better Auth, l'issuer, l'audience, l'expiration et les scopes de plan de
contrôle avant l'exécution d'un outil.
Le fournisseur OAuth initialise au démarrage la ressource RFC 8707 canonique
/api/mcp. Les clients dynamiques peuvent demander cette ressource active sans
liaison client-ressource préconfigurée, mais ils doivent toujours terminer
l'authentification et le consentement utilisateur avant que Better Auth émette
un token limité par ses scopes.
Le fournisseur OAuth annonce volontairement toute la surface de permissions MCP:
control-plane:read control-plane:write control-plane:publish control-plane:adminCela garde la surface d'outils MCP inchangée pour les clients liés de confiance. Les handlers appliquent toujours la permission de plan de contrôle requise et l'autorisation Yayaw sous-jacente pour chaque opération; les scopes OAuth ne remplacent donc jamais l'autorisation applicative.
Pour les ChatGPT Apps publiées, vérifiez que NEXT_PUBLIC_BASE_URL est
l'origine HTTPS publique avant de générer les métadonnées. ChatGPT redirige vers
https://chatgpt.com/connector/oauth/{callback_id}; le client OAuth enregistré
doit autoriser cette redirect URI.
Connexion MCP Locale
Le stdio local est utile en développement et ne nécessite pas d'ouvrir un endpoint public. Codex peut l'enregistrer avec:
codex mcp add yayaw-local -- bun --conditions react-server --env-file=.env src/lib/scripts/mcp/yayaw-mcp-server.tsPour le stdio local, définissez YAYAW_MCP_API_KEY pour tester une vérification
de clé API comme en production. Sans clé, le lanceur crée un acteur de
développement local avec YAYAW_MCP_LOCAL_USER_ID ou local-codex.
Pour les runs smoke dynamic-data, cet acteur local stdio de confiance peut
utiliser ses permissions locales control-plane:admin pour passer les contrôles
control-plane dynamic-data sans utilisateur stocké en base. Les acteurs HTTP,
OAuth, clé API, preview et production requièrent toujours la décision
d'autorisation Yayaw normale pour chaque opération dynamic-data.
Lancez le smoke MCP dynamic-data générique avant ou après un travail de bundle:
bun run mcp:dynamic-data-smokeLa commande démarre le serveur MCP stdio local, liste les outils, appelle
yayaw_dynamic_data_ui_capabilities, et vérifie le contrat de checklist
bundle.postInstall.verification ainsi que des capacités représentatives Yayaw
Table, formulaire généré, panneau détail, navigation dynamique et
dashboard.operatorReadiness. La sortie du smoke inclut la racine control-plane
Admin de readiness opérateur, le pattern de route des sections dynamiques, les
états d'attention, champs de résumé et endpoints de passage MCP/runtime afin que
les runs déployés montrent où les surfaces dashboard sont visibles. Le loader
dashboard lit aussi cette même readiness de schéma cible avant de lister les
modèles, affiche les tables control-plane requises manquantes dans le panneau
opérateur, et évite les requêtes registry tant que le schéma est bloqué. Définissez
YAYAW_MCP_SMOKE_TRANSPORT=http, YAYAW_MCP_URL=https://yayaw.app/api/mcp, et
YAYAW_MCP_API_KEY pour lancer le même smoke contre un endpoint déployé.
Définissez YAYAW_MCP_SMOKE_VALIDATE_BUNDLE=1 pour parser, normaliser et
valider localement la fixture de bundle configurée sans nécessiter de
connectivité DB ni d'authz dynamic-data sur l'endpoint cible.
Définissez YAYAW_MCP_SMOKE_BUNDLE_DRY_RUN=1 seulement quand l'environnement
cible dispose de la DB et de l'état authz nécessaires au dry-run du modèle
resource générique inclus et de ses modèles liés. Par défaut, ce dry-run optionnel charge
src/lib/scripts/mcp/fixtures/dynamic-data-resource-bundle.json; définissez
YAYAW_MCP_SMOKE_BUNDLE_FILE pour pointer vers une autre entrée JSON complète
de yayaw_dynamic_data_bundle_apply. Pour les bundles produit externes conservés
hors du dépôt, préférez les scripts génériques mcp:dynamic-data-bundle-*:
mcp:dynamic-data-bundle-smoke, mcp:dynamic-data-bundle-target-readiness,
mcp:dynamic-data-bundle-dry-run, mcp:dynamic-data-bundle-apply,
mcp:dynamic-data-bundle-installed-smoke et
mcp:dynamic-data-bundle-seeded-smoke. Ces scripts définissent
YAYAW_MCP_SMOKE_REQUIRE_BUNDLE_FILE=1, donc le run échoue si
YAYAW_MCP_SMOKE_BUNDLE_FILE manque au lieu de retomber accidentellement sur la
fixture intégrée. Lancez
bun run mcp:dynamic-data-foundation-smoke pour valider la fixture foundation
générique dans
src/lib/scripts/mcp/fixtures/dynamic-data-foundation-bundle.json. Ce script
définit YAYAW_MCP_SMOKE_VALIDATE_FOUNDATION=1, ce qui exige qu'un bundle
couvre tables natives, UI dashboard et navigation, registre de principals
dynamique, références et grants par principal, contrainte de grant actif, routes
runtime, modèles enfants d'endpoints de ressource, trust bindings de ressource,
heartbeat avec clés d'idempotence composites, routes de statut de lifecycle de
workflow avec effets terminaux, claim pairing avec secret hashé et compteur de
tentatives, ingestion metrics avec rollups, rate limit par
principal, guards scopés au principal, effets cross-model, topics realtime,
projections et échantillons bruts à rétention courte. Lancez
bun run mcp:dynamic-data-foundation-dry-run quand l'endpoint cible dispose des
tables DB dynamic-data et de l'authz dynamic-data:manage pour l'acteur MCP. La
commande utilise la même fixture foundation et les mêmes validations, puis
appelle yayaw_dynamic_data_bundle_apply avec dryRun=true pour confirmer que
la cible est prête avant un vrai apply publish/deploy. Lancez
bun run mcp:dynamic-data-target-readiness avant le premier vrai apply sur un
endpoint cible. La commande charge la fixture foundation générique, valide le
contrat local, découvre les outils MCP, lit les capabilities dynamic-data,
appelle yayaw_dynamic_data_target_readiness pour le schéma DB cible, liste les
modèles dans le scope du bundle seulement quand ce préflight de schéma est
ready, puis dry-run yayaw_dynamic_data_bundle_apply. Elle imprime un rapport
targetReadiness avec statut ready ou blocked et sort en non-zéro quand une
étape requise échoue, afin de rendre visibles l'authz manquante, les migrations,
les tables DB ou la compatibilité du bundle avant toute mutation. Lancez
bun run mcp:dynamic-data-foundation-apply seulement après un rapport readiness
ready. La commande réutilise la même fixture foundation, garde la readiness
activée, exige la confirmation de vrai apply
YAYAW_MCP_SMOKE_BUNDLE_APPLY_CONFIRM=publish-deploy, envoie une raison
d'audit, puis appelle yayaw_dynamic_data_bundle_apply avec dryRun=false,
publish=true et deploy=true. Si la readiness est blocked, elle indique
bundleApply.status: skipped et sort avant toute mutation. Le script apply
définit aussi YAYAW_MCP_SMOKE_POST_INSTALL_VERIFY=1; après un apply réussi,
il exécute les appels MCP sûrs de postInstall.verification.checklist et classe
les étapes manuelles ou dépendantes de données produit comme skipped avec des
raisons telles que manual_step, requires_input ou requires_values. Lancez
bun run mcp:dynamic-data-foundation-installed-smoke après un apply réussi pour
vérifier toute la foundation déployée. La commande dérive la liste de modèles
attendus depuis la même fixture générique, vérifie que
yayaw_dynamic_data_models_list indique chaque modèle comme publié, déployé et
valide, lit le manifest runtime, vérifie les routes dashboard générées, la
navigation, les réglages Yayaw Table normalisés pour table, formulaire et
détail, ainsi que les bindings d'extensions d'entités, compare les ids de
contrôles UI déclarés au manifest runtime installé, compare les contrats de
routes runtime déclarés à yayaw_dynamic_data_runtime_routes, y compris les
rollups de metrics-ingest, compare les contrats de feeds d'événements realtime
déclarés aux runtimeEventFeeds du manifest installé, query chaque modèle, lit
les événements runtime et résout chaque route runtime déclarée. Elle appelle aussi
yayaw_dynamic_data_entity_extensions pour chaque target d'extension déclarée,
afin de prouver que la découverte d'extensions correspond au manifest déployé.
Définissez
YAYAW_MCP_SMOKE_MODEL_SLUG,
YAYAW_MCP_SMOKE_MODEL_SCOPE, et optionnellement YAYAW_MCP_SMOKE_ORG_ID pour
vérifier un modèle dynamique déjà déployé via le manifest, la route dashboard
générée, les champs normalisés du manifest, les colonnes système, ui.table,
ui.form, ui.detail, ui.nav, une query record bornée, le contrat de feed
d'événements runtime, une lecture d'events runtime et la résolution des routes
runtime. Ajoutez
YAYAW_MCP_SMOKE_RUNTIME_ROUTE_EXAMPLES_JSON ou
YAYAW_MCP_SMOKE_RUNTIME_ROUTE_EXAMPLES_FILE pour exécuter en dry-run certaines
routes runtime pendant le smoke de modèle installé. Chaque exemple est un objet
JSON avec modelSlug plus routeKey, routeId, templatePath ou method,
et un path concret, avec éventuellement recordId, values, filters,
sort, search, recordLimit, recordOffset, expectedDataVersion ou
reason. Les payloads spécifiques au produit restent ainsi hors du dépôt,
tandis que MCP peut prouver que les routes génériques déployées sont résolubles
et exécutables.
Les fixtures de bundle externes peuvent aussi inclure
smoke.runtimeRouteExamples au top-level avec le même format d'exemple. Le
runner smoke lit ces exemples pour la vérification de modèle installé et de
bundle installé, tout en retirant smoke avant d'appeler
yayaw_dynamic_data_bundle_apply; les exemples fournis via
YAYAW_MCP_SMOKE_RUNTIME_ROUTE_EXAMPLES_JSON ou
YAYAW_MCP_SMOKE_RUNTIME_ROUTE_EXAMPLES_FILE gardent la priorité sur les
exemples intégrés au bundle.
Les fixtures externes peuvent aussi inclure smoke.recordSeeds, un tableau
ordonné d'objets avec modelSlug, recordId optionnel, scope/orgId
optionnels, un label optionnel et values. Définissez
YAYAW_MCP_SMOKE_RECORD_SEEDS=1 pour préflight ces seeds via
yayaw_dynamic_data_record_create avec dryRun=true. Pour créer les records
sur une cible de smoke end-to-end, définissez aussi
YAYAW_MCP_SMOKE_RECORD_SEEDS_APPLY=1,
YAYAW_MCP_SMOKE_RECORD_SEEDS_CONFIRM=create-records et
YAYAW_MCP_SMOKE_RECORD_SEEDS_REASON pour le journal d'audit. Les bundles
produit peuvent ainsi porter leurs propres données de test tout en gardant le
dépôt Yayaw neutre.
Définissez YAYAW_MCP_SMOKE_RECORD_SEEDS_RESET=1 pour supprimer les records de
seed existants avant création, en utilisant les recordId stables du bundle
dans l'ordre inverse des seeds. Le reset reste en dryRun=true par défaut,
ignore les records déjà absents et requiert
YAYAW_MCP_SMOKE_RECORD_SEEDS_RESET_APPLY=1,
YAYAW_MCP_SMOKE_RECORD_SEEDS_RESET_CONFIRM=delete-records et
YAYAW_MCP_SMOKE_RECORD_SEEDS_RESET_REASON pour les suppressions réelles.
Définissez YAYAW_MCP_SMOKE_RECORD_SEEDS_CLEANUP=1 pour exécuter
yayaw_dynamic_data_record_delete sur les records créés par le même run smoke.
Le cleanup reste en dryRun=true par défaut; un cleanup réel requiert
YAYAW_MCP_SMOKE_RECORD_SEEDS_CLEANUP_APPLY=1,
YAYAW_MCP_SMOKE_RECORD_SEEDS_CLEANUP_CONFIRM=delete-records et
YAYAW_MCP_SMOKE_RECORD_SEEDS_CLEANUP_REASON, afin que les bundles smoke
produit puissent vérifier les chemins d'écriture sans laisser de records de
validation derrière eux.
La fixture embarquée dynamic-data-foundation-bundle.json inclut des
smoke.recordSeeds neutres produit pour ses modèles ressource, endpoint,
trust, session et métriques, afin que le contrat foundation générique dispose
d'un exemple réutilisable de chemin d'écriture. Elle inclut aussi des
smoke.runtimeRouteExamples neutres produit pour les routes déterministes de
heartbeat, trust, statut de session et ingestion de métriques. Le runner smoke
exécute ces exemples de routes embarqués uniquement quand la création réelle
des record seeds est activée avec YAYAW_MCP_SMOKE_RECORD_SEEDS_APPLY=1, ou
quand un opérateur définit explicitement
YAYAW_MCP_SMOKE_RUNTIME_EXAMPLES_FROM_BUNDLE=1, afin que les vérifications
simples de bundle installé ne dépendent pas de records de seed préexistants.
La même fixture foundation relie des ui.table.rowActions et
ui.detail.actions neutres produit aux routes runtime heartbeat, check
endpoint, trust-seen et fin de session. Le validateur foundation vérifie
maintenant runtimeUiActions, afin que le bundle générique continue de prouver
que MCP peut décrire tables natives, routes runtime et UI backend actionnable
sans code repository.
Après déploiement du bundle foundation sur une cible avec les tables
dynamic-data, lancez bun run mcp:dynamic-data-foundation-seeded-smoke pour
combiner vérification du bundle installé, reset des seeds, préflight de
création des seeds et préflight de cleanup dans une seule commande neutre
produit. Ajoutez les variables d'environnement *_APPLY, *_CONFIRM et
*_REASON documentées uniquement lorsque vous voulez volontairement écrire et
supprimer ces records de seed sur cette cible.
Définissez YAYAW_MCP_SMOKE_ENTITY_TARGET avec les options
YAYAW_MCP_SMOKE_ENTITY_SCOPE, YAYAW_MCP_SMOKE_ENTITY_ORG_ID,
YAYAW_MCP_SMOKE_ENTITY_TARGET_ID,
YAYAW_MCP_SMOKE_ENTITY_BINDING_KINDS,
YAYAW_MCP_SMOKE_ENTITY_INCLUDE_COUNTS=1, et
YAYAW_MCP_SMOKE_ENTITY_MIN_EXTENSIONS pour vérifier
yayaw_dynamic_data_entity_extensions sur des modèles dynamiques déployés qui
extends ou references des entités natives ou produit. C'est le chemin
générique pour afficher des extensions sur les pages dashboard natives sans
code React produit.
Gestion Des Clés
Pour une clé opérateur MCP totalement de confiance, utilisez l'ensemble complet de permissions:
control-plane:read,write,publish,adminLa page Developer settings du dashboard utilise le bloc de clé API custom Yayaw
plutôt que l'UI Better Auth générique, parce que Better Auth traite les
permissions de clés API comme des champs server-only. Utilisez ce bloc pour
créer des clés MCP avec scopes explicites ou réparer une clé existante avec le
preset Accès API complet. Le preset écrit:
control-plane:read,write,publish,admin
dynamic-data-runtime:read,writePour une clé runtime data-plane seule, laissez les permissions control-plane vides et accordez:
dynamic-data-runtime:read,writePour une clé qui peut à la fois configurer les modèles via MCP et appeler les routes runtime chaudes, combinez les deux ressources:
control-plane:read,write,publish,admin
dynamic-data-runtime:read,writeL'UI peut mettre à jour les permissions d'une clé existante sans révéler ou faire tourner le secret. Utilisez cela seulement lorsque le secret actuel est déjà stocké en sécurité, parce que Better Auth n'affiche le secret qu'une fois.
Les clés détenues par l'organisation sont le choix par défaut pour les clients MCP qui opèrent sur un périmètre client/workspace. Elles sont émises pour l'organisation active, gérables par les propriétaires, admins et managers Better Auth, portent des métadonnées d'émetteur et sont limitées à leur organisation pour les opérations bornées sur pages, données CMS et médias.
Les clés personnelles sont superadmin-only et destinées à l'administration globale de Yayaw. Utilisez-les pour le contenu global, les feature flags ou les opérations inter-organisations uniquement lorsque c'est intentionnel.
Émettre une clé API MCP détenue par utilisateur:
bun run mcp:key -- issue --email admin@example.com --permissions read,write,publish,adminÉmettre une clé API MCP détenue par organisation:
bun run mcp:key -- issue --email owner@example.com --organization-slug acme --permissions read,write,publish,adminLister les clés d'un utilisateur et leurs permissions stockées:
bun run mcp:key -- list --email admin@example.comLister les clés d'une organisation et leurs permissions stockées:
bun run mcp:key -- list --organization-slug acmeInspecter une clé par id:
bun run mcp:key -- inspect --key-id <api-key-id>Inspecter le secret configuré pour un client MCP local:
YAYAW_MCP_API_KEY=<secret> bun run mcp:key -- inspect --token-env YAYAW_MCP_API_KEYRévoquer une clé:
bun run mcp:key -- revoke --key-id <api-key-id>La commande issue affiche le secret une seule fois. Stockez-le dans un
gestionnaire de secrets ou l'environnement du client MCP local; ne le commitez
pas.
Outils
Noyau:
yayaw_capabilitiesyayaw_statusyayaw_audit_listyayaw_deploy_status
CMS et pages:
yayaw_pages_list,yayaw_pages_get,yayaw_pages_validate,yayaw_pages_diffyayaw_pages_publish,yayaw_pages_archiveyayaw_pages_create_draft,yayaw_pages_save_draft(bypass de maintenance désactivés)yayaw_components_list,yayaw_component_get,yayaw_component_import,yayaw_component_recipe_importyayaw_components_sync_registry,yayaw_component_recompileyayaw_component_publish,yayaw_component_deleteyayaw_sections_list,yayaw_sections_get,yayaw_sections_create_draftyayaw_sections_validate,yayaw_sections_save_draftyayaw_sections_publish,yayaw_sections_archiveyayaw_kibo_sections_generateyayaw_cms_design_prepare,yayaw_cms_design_resume,yayaw_cms_design_guidance_get,yayaw_cms_design_context_renew,yayaw_cms_design_inventory_refreshyayaw_cms_prototypes_register,yayaw_cms_prototype_runtime_previews_get,yayaw_cms_prototype_runtime_review_complete,yayaw_cms_prototype_selectyayaw_cms_page_design,yayaw_cms_page_rework,yayaw_cms_page_review,yayaw_cms_page_review_completeyayaw_cms_page_design_legacy_admin(bypass de compatibilité admin désactivé)yayaw_cms_personalization_requests_list,yayaw_cms_personalization_request_get,yayaw_cms_personalization_request_status_update,yayaw_cms_personalization_request_delete,yayaw_cms_personalization_requests_prune(admin uniquement)yayaw_data_models_list,yayaw_data_schema_get,yayaw_data_entry_getyayaw_data_entry_upsert,yayaw_data_entry_publishyayaw_dynamic_data_ui_capabilitiesyayaw_dynamic_data_target_readinessyayaw_dynamic_data_reference_optionsyayaw_dynamic_data_models_list,yayaw_dynamic_data_model_getyayaw_dynamic_data_model_revisions,yayaw_dynamic_data_model_revision_diff,yayaw_dynamic_data_model_revision_restoreyayaw_dynamic_data_entity_extensionsyayaw_dynamic_data_runtime_manifestyayaw_dynamic_data_runtime_routesyayaw_dynamic_data_runtime_eventsyayaw_dynamic_data_runtime_route_resolveyayaw_dynamic_data_runtime_executeyayaw_dynamic_data_model_save,yayaw_dynamic_data_model_publishyayaw_dynamic_data_model_deployment_plan,yayaw_dynamic_data_model_deployyayaw_dynamic_data_bundle_applyyayaw_dynamic_data_records_query,yayaw_dynamic_data_record_getyayaw_dynamic_data_record_create,yayaw_dynamic_data_record_update,yayaw_dynamic_data_record_deleteyayaw_email_templates_list,yayaw_email_template_getyayaw_email_template_save,yayaw_email_templates_sync_systemyayaw_org_domains_list,yayaw_org_domain_add,yayaw_org_domain_checkyayaw_org_domain_set_primary,yayaw_org_domain_archive
Opérations site:
yayaw_billing_products_list,yayaw_billing_product_updateyayaw_stripe_discounts_list,yayaw_stripe_discounts_syncyayaw_media_list,yayaw_media_search,yayaw_media_generate,yayaw_media_upload,yayaw_media_import_url,yayaw_media_upload_prepare,yayaw_media_upload_completeyayaw_design_tokens_get,yayaw_design_tokens_saveyayaw_flags_list,yayaw_flags_update
Les outils de données dynamiques natives pilotent des tables Postgres propres
au projet. Ils stockent des définitions versionnées, valident les contrats de
champs et d'index, publient les révisions approuvées, puis déploient seulement
de la DDL additive générée par le serveur.
yayaw_dynamic_data_model_revisions liste les révisions immuables avec les
marqueurs latest, published et deployed, ainsi que les documents complets en
option pour les clients MCP qui doivent comparer des définitions avant de
publier, déployer ou restaurer un contrat précédent.
yayaw_dynamic_data_model_revision_diff compare deux révisions immuables, par
défaut de deployed vers latest, et renvoie les résumés par section, les chemins
modifiés, les valeurs before/after ou documents complets en option, ainsi que
les mêmes métadonnées d'évolution de déploiement que le planner.
yayaw_dynamic_data_model_revision_restore restaure une révision immuable
précédente comme dernier draft uniquement ; les opérateurs doivent explicitement
publier puis déployer ce draft restauré après revue des diagnostics et diffs.
Les réponses de plan et de déploiement exposent aussi evolution.status,
evolution.canDeploy, les changements par champ, et les compteurs de champs
ajoutés, conservés, retirés, renommés, changés de type et bloquants. Les agents
doivent traiter blocked comme un signal de préparer une migration de données
séparée plutôt que de tenter une DDL destructive via dynamic-data. Utilisez
yayaw_dynamic_data_bundle_apply pour les manifests
d'extension produit qui doivent installer plusieurs modèles ensemble. L'outil
accepte une liste bornée de définitions de modèles dans un même scope global ou
organisation, vérifie dynamic-data:manage pour chaque modèle cible,
sauvegarde chaque draft via le même validateur, et peut optionnellement publier
et déployer les révisions résultantes dans l'ordre. Il valide aussi les champs
de référence vers modèles dynamiques sur tout le bundle avant de sauvegarder un
draft: relation.modelSlug doit pointer vers un autre modèle du bundle ou vers
un modèle existant lisible dans le scope cible résolu, et les valeurs déclarées
displayField ou valueField doivent exister sur le modèle cible ou être des
colonnes système d'enregistrement sûres. Les metricRollups, recordEffects,
recordGuards et relatedRecordFilters runtime doivent aussi cibler des
modèles du bundle ou des modèles existants lisibles; chaque champ écrit ou
filtré doit exister sur le modèle cible avant que le bundle puisse être
sauvegardé. Il requiert
control-plane:admin, car deploy=true peut appliquer du DDL de tables
natives généré; lancez d'abord dryRun=true pour valider le manifest sans
écriture. Le détail modèle du dashboard utilise le même générateur de plan pour
les gestionnaires, afin que les opérateurs puissent vérifier le SQL exact, le
nom de table, les index, les diagnostics et le hash des statements avant
d'appliquer la DDL générée. Quand deploy=true réussit hors dry-run, la réponse inclut
manifest.models pour les modèles déployés du bundle afin que les clients MCP
puissent inspecter immédiatement leurs champs, colonnes système, routes runtime
et UI dashboard sans appel manifest séparé. Chaque modèle de bundle déployé
retourne aussi deploy.evolution, afin que les installateurs batch puissent
s'arrêter sur les changements non-additifs bloqués avec le même rapport par
champ que l'outil de plan de déploiement unitaire. Chaque réponse de bundle
inclut aussi postInstall, un guide de passage de relais MCP sûr pour le
dashboard.
Une fois déployé, postInstall.models liste la route dashboard canonique de
chaque modèle installé, le template de route record, les métadonnées
d'extension, les routes runtime avec des blueprints d'entrée
yayaw_dynamic_data_runtime_execute, les topics realtime avec des blueprints
yayaw_dynamic_data_runtime_events plus des handoffs data-plane JSON/SSE, et
les blueprints des outils record query/get/create/update/delete pour les smoke
checks. L'inventaire
ui.surfaces de chaque modèle liste les surfaces navigation, table, formulaire
et détail générées avec les routes dashboard, colonnes de table ordonnées,
champs et sections de formulaire, modes d'affichage, IDs d'actions ligne/détail,
panels d'enregistrements liés, cartes métriques, timelines d'événements et IDs
d'items de résumé. Chaque route runtime inclut aussi un handoff dataPlane
avec l'endpoint HTTP générique sous /api/dynamic-data/runtime/*, les query
params requis, l'action de clé API dynamic-data-runtime attendue, l'action
d'autorisation Yayaw sous-jacente, la politique de rate limit de la route, et
si la route exige une raison. Ce guide n'expose jamais les noms de tables
physiques et permet à un client MCP de passer de l'installation à la
vérification spécifique produit sans coder les internes Yayaw en dur. Quand le
déploiement a été lancé,
postInstall.verification ajoute une checklist ordonnée qui commence par un
refresh du manifest puis pointe le client MCP vers la route dashboard générée,
la query record, le dry-run create record, les dry-runs de routes runtime, et
la lecture des events runtime pour chaque modèle installé. La réponse
yayaw_dynamic_data_ui_capabilities expose les kinds de checklist supportés,
les valeurs de statut, l'ordre recommandé et les noms d'outils de smoke test
sous bundle.postInstall.verification. Chaque réponse de bundle inclut
aussi un objet summary global et des données models[].summary par modèle.
Ces résumés sont disponibles en dry-run comme en apply réel et indiquent les
modèles créés ou mis à jour, les modèles ready ou bloqués, les compteurs de
routes runtime, de surfaces UI, de champs, colonnes, formulaires et actions, les
compteurs de diagnostics par sévérité, ainsi que les étapes publish/deploy qui
seraient exécutées. Les clients MCP devraient utiliser ce résumé d'impact comme
smoke check rapide avant de demander l'approbation opérateur d'un manifest
produit. La validation de bundle en dry-run
peut omettre reason; les écritures réelles de bundle continuent d'en exiger
un pour l'auditabilité. Les
définitions de modèles peuvent porter un bloc extension pour
l'ownership produit, les capacités et les bindings d'entités, un bloc
constraints pour les contraintes uniques générées, un bloc runtime pour la
surface data-plane attendue, et un bloc ui validé.
Les control planes pour devices, agents, remote-control, workflows ou analytics
doivent être modélisés comme des extensions dynamic data bornées à
l'organisation, pas comme des forks backend spécifiques à un produit. Un
manifeste peut définir des registres de principals, des registres de ressources
avec statuts calculés côté serveur depuis heartbeat et workflows, des modèles
enfants runtime ou endpoint, des trust bindings, des flows de pairing à code
hashé et TTL court, des grants bornés à un principal, des enregistrements de
cycle de vie, des samples metrics bruts à rétention courte, des rollups
minute/hour/day et des événements d'audit. Ces modèles doivent rester des
documents yayaw_dynamic_data_bundle_apply normaux avec métadonnées
extension, runtime.api, runtime.realtime, runtime.projections,
ui.nav, et réglages Yayaw Table ui.table/ui.form.
Les clients MCP possèdent les documents de bundle spécifiques à chaque produit
et peuvent les dry-run, publier et déployer via
yayaw_dynamic_data_bundle_apply. Le repository Yayaw ne doit pas embarquer de
manifestes produit ni de wrappers dédiés: il expose les contrats génériques de
modèles, runtime, UI, authz et audit que ces manifestes fournis par MCP
utilisent. Les appelants produit chauds doivent utiliser
/api/dynamic-data/runtime/* ou de futurs endpoints data-plane dédiés pour les
heartbeats, claims de pairing, changements de statut de session et ingestion de
metrics, plutôt que de router du trafic haute fréquence via MCP.
Par exemple, un registre de ressources peut exposer des heartbeats dashboard
sur record sélectionné et une route heartbeat externe idempotente comme
/devices/heartbeat; les appelants transmettent un slug stable et les champs
de registre à publier, puis l'exécuteur heartbeat générique crée ou met à jour
la ligne native. Les lectures list peuvent utiliser relatedRecordFilters pour
que /devices ne renvoie que les ressources ayant un grant actif, non révoqué
et non expiré pour le principal runtime courant. Les modèles enfants endpoint
ou trust-binding peuvent utiliser le même contrat heartbeat générique avec des
clés d'idempotence composites comme
resource_id + endpoint_kind + host + port ou
resource_id + hostname + fingerprint. Les routes de statut et révocation
peuvent utiliser patch_status, des preconditions, et des assignments serveur
afin que les actions de ligne dashboard et les clients MCP n'envoient pas de
timestamps de cycle de vie fiables.
extension.entityBindings permet à un modèle dynamique de déclarer qu'il
represents, extends ou references une entité produit durable comme
organization, user, principal, resource, workflow, ou tout slug
spécifique au produit. organization reste la cible canonique Yayaw pour une
team ou un workspace; les manifests et appels de discovery peuvent utiliser
team comme alias et Yayaw le normalise en organization. Les bindings
extends et references doivent pointer vers un champ déclaré, tandis que
represents peut décrire le modèle lui-même. Ces bindings servent de métadonnée
pour MCP, la navigation dashboard, l'orchestration runtime et la discovery
d'extensions; ils ne donnent jamais d'accès et ne remplacent pas l'autorisation
Yayaw can(...). Le builder no-code de modèle dans le dashboard peut authorer
la même clé d'extension, libellé, description, version, capacités, tags et
bindings d'entités pour les brouillons en table native; les bundles MCP restent
le chemin pour les manifests d'extension multi-modèles plus larges. Les clients
MCP peuvent appeler yayaw_dynamic_data_entity_extensions avec une target comme
user, organization, team, principal, ou tout slug produit pour découvrir les
modèles déployés qui represents, extends ou references cette target.
Lorsque targetId et includeRecordCounts=true sont fournis, Yayaw compte les
enregistrements natifs liés seulement après filtrage de chaque modèle par
dynamic-data:list; les bindings basés sur un champ comptent les lignes via
leur champ déclaré, tandis que represents compte l'enregistrement représenté
par id.
constraints génère aujourd'hui des indexes uniques sûrs, y compris des
indexes uniques partiels depuis des prédicats de champs comme
revoked_at is null, sans accepter de SQL arbitraire. runtime.api,
runtime.realtime et runtime.projections documentent les routes runtime,
topics d'événements realtime durables et champs dérivés consommés par les
handlers serveur. MCP reste le control plane et ne doit pas se trouver dans les
chemins chauds de heartbeat, sessions, metrics ou ingestion d'événements.
runtime.projections supporte
last_seen_status pour les projections de présence sur le même enregistrement:
la projection lit des inputs timestamp, applique un seuil configurable
staleAfterSeconds avec un défaut de 120 secondes, et utilise les deux
premières values comme libellés offline/reachable. Il supporte aussi
record_status pour les projections de statut génériques comme les ressources,
workflows, tickets ou inventaires: values[0] est le fallback, les
inputs booléens true et numériques positifs résolvent vers le
values[inputIndex + 1] correspondant, et les inputs texte passent à travers
quand ils correspondent à une valeur autorisée. state utilise la même
résolution priorisée pour les champs d'état de workflow, cycle de vie ou
disponibilité. count additionne des inputs du même enregistrement en traitant
les tableaux comme leur longueur, les nombres finis comme leur valeur, les
booléens true et scalaires non vides comme 1, et les valeurs vides ou
manquantes comme 0. Les projections on_read s'appliquent aux valeurs
retournées sans muter le stockage. Les champs projetés sont toujours retirés des
payloads create/update des appelants et ne sont pas exigés des callers record
même si la définition du champ est marquée required. Les projections on_write
sont matérialisées pendant create/update après retrait des valeurs client pour
le champ projeté, afin que le modèle, pas
l'appelant, possède la valeur calculée. yayaw_dynamic_data_runtime_manifest
renvoie les déclarations runtime normalisées et déployées pour la discovery par
les futures couches API, realtime, projections, UI et orchestration
d'extensions. Les entrées incluent les fields normalisés, fieldKeys,
runtimeApiRoutes effectives, ui.dashboardRoute résolu ainsi que les configs
ui.dashboardRecordRouteTemplate, ui.dashboardSection, systemColumns,
ui.nav, ui.table, ui.form et ui.runtimeActions normalisées, pour que
les clients MCP puissent découvrir les interfaces backend sans reconstruire les
réglages Yayaw Table depuis les documents modèle bruts. ui.runtimeActions
résume les actions ligne/détail, leurs surfaces dashboard, leur mode de
rafraîchissement, leur route key, la méthode/le chemin résolus, le comportement
record-id et verrou optimiste, ainsi que les champs de bindings littéraux et de
chemin. runtimeApiRoutes
filtre les routes désactivées et résout les defaults de méthode, chemin, action
d'authz, idempotence, rate limits, filtres, assignments et preconditions pour
le modèle. Le builder de modèle no-code peut configurer des déclarations de
routes runtime bornées avec chemin de base, type, méthode, chemin, action
d'authz, clé stable, champs d'idempotence, champs client acceptés, mapping de
paramètres vers champs, préconditions par champ, assignations de valeurs de
route, conditions de valeurs client, filtres d'enregistrements de route,
incréments de valeurs de route, transformations de valeurs de route, rate limits et exigence de reason
avant publication/déploiement. Les champs d'idempotence et les champs
client acceptés utilisent des pickers structurés dérivés des champs du brouillon;
les conditions de valeurs client utilisent des contrôles bornés champ, opérateur
et valeur littérale; le mapping de paramètres vers champs utilise des lignes de bindings bornées; et
les préconditions utilisent des contrôles bornés champ, opérateur, valeur
littérale ou now, tandis que les filtres d'enregistrements de route utilisent
des contrôles bornés champ, opérateur, valeur littérale, paramètre de chemin,
organisation ou acteur, et les assignations de valeurs peuvent définir des champs
depuis une valeur littérale, now, un paramètre de chemin, l'id
d'organisation ou l'identité acteur, avec des listes générales, spécifiques à la
création et spécifiques à la mise à jour. L'entrée MCP peut toujours envoyer les mêmes
valeurs comme arrays, objets ou chaînes séparées par des virgules. Les
incréments de valeurs peuvent ajuster atomiquement des champs numériques avec
bornes min/max optionnelles. Les
transformations de valeurs peuvent hasher des champs de valeur ou paramètres de
chemin vers des champs cibles avec SHA-256 ou HMAC SHA-256, encodage hex ou
Base64URL, trimming optionnel et suppression optionnelle de la source. Les effets de
route plus avancés, guards, rollups et effets cross-model restent disponibles via les
bundles MCP. Le même builder no-code peut lier des actions runtime bornées à ces
clés de routes avec libellé, icône, style, comportement de rafraîchissement,
inclusion de l'ID d'enregistrement, verrou optimiste, reason d'audit optionnelle
et placement
optionnel dans la vue détail; les conditions avancées, valeurs littérales,
paramètres de chemin et bindings de valeurs restent du ressort des bundles MCP.
La même surface dashboard peut rouvrir un modèle manageable comme nouveau
brouillon seulement lorsque le document courant est entièrement représentable
par le formulaire no-code borné; elle envoie expectedRevisionId pour détecter
les conflits et laisse les documents avancés MCP-only sous contrôle MCP jusqu'à
l'existence de contrôles visuels équivalents. Une route peut
déclarer une key stable (ou les alias manifest
routeKey ou id), normalisée et renvoyée avec la route afin que les clients
MCP et les appelants chauds ciblent une route déployée précise sans dépendre de
l'ordre du tableau de manifest. yayaw_dynamic_data_runtime_routes utilise les
mêmes entrées de routes du manifest et ajoute le signalement de conflits
cross-model avant l'activation d'un endpoint runtime générique. Les entrées de
route incluent un handoff dataPlane avec le template d'endpoint HTTP, les
query params requis, l'action de clé API runtime, l'action d'autorisation
Yayaw, la politique de rate limit, et l'exigence de reason, afin que les
clients MCP puissent découvrir les routes chaudes déjà installées sans rejouer
l'installation de bundle.
Pour les modèles déployés, le détail de contrat du dashboard et les actions
record/detail générées lisent ce même manifest runtime normalisé; les cartes de
routes déployées affichent aussi l'endpoint data-plane générique, les query
params, l'action de clé API runtime, l'action d'autorisation Yayaw, la
politique de rate limit et l'exigence de reason depuis ce manifest. L'interface
affiche donc les routes et ui.runtimeActions réellement installées, pas un
document draft non publié.
yayaw_dynamic_data_runtime_route_resolve matche une méthode et un chemin avec
les déclarations de routes déployées et renvoie les paramètres de chemin pour
les tests MCP à sec. Les outils manifest, route-list, resolve et execute
acceptent routeKey comme filtre optionnel lorsque plusieurs routes génériques
partagent volontairement une forme méthode/chemin. yayaw_dynamic_data_runtime_execute
exécute les routes runtime record supportées via les services génériques après
résolution de la route, check d'authz Yayaw dynamic-data, et reason
obligatoire pour les mutations MCP réelles. Sa réponse MCP inclut l'action
d'authz effective et les éventuels conflicts de routes observés pendant la
résolution en plus du résultat d'exécution. L'exécuteur runtime partagé ne
demande un reason que si la route matchée déclare reasonRequired, afin que les
futurs endpoints data-plane chauds puissent exécuter heartbeat et metrics sans
hériter de la friction d'audit MCP. L'exécuteur supporte aujourd'hui list,
get, create,
update, patch_status, delete, heartbeat, metrics_ingest et
pairing_claim. heartbeat et metrics_ingest peuvent faire un upsert quand
la route déclare idempotencyKeyField ou une liste composite
idempotencyKeyFields; sinon l'ingestion metrics crée un enregistrement.
pairing_claim exige des champs d'idempotence, trouve exactement un
enregistrement existant via ces champs, puis le met à jour avec les paramètres
de route et valeurs de requête fusionnés. Les clés composites servent aux
modèles enfants runtime identifiés par des champs stables comme
resource_id + hostname. Les routes
metrics_ingest peuvent aussi déclarer des metricRollups pour alimenter des
agrégats en tables natives à côté du modèle de samples bruts. Chaque rollup
cible un autre modèle dynamique, choisit une fenêtre minute, hour ou day,
écrit un champ bucket normalisé, copie des dimensions déclarées comme
workflow_id ou resource_id, et peut incrémenter un countField ainsi que des
champs de valeurs count, sum, min, max, avg ou last. Les rollups
s'exécutent après les params de route, transforms de valeurs et assignments
serveur, donc MCP peut lier un paramètre comme :workflowId une seule fois puis
réutiliser le champ obtenu pour les samples bruts et les dimensions d'agrégat.
Les modèles cibles sont résolus via les mêmes services record dynamic-data
déployés et restent dans le scope de la route source sauf si targetScope est
déclaré. Le builder de routes du dashboard peut configurer les cibles de
rollup, fenêtres, champs bucket/count, dimensions et valeurs agrégées des
routes metrics_ingest. L'exécution des rollups est reportée dans le résultat
runtime et le payload d'événement metric, tandis que l'ingestion brute reste la
source de vérité durable pour le debug et l'analyse à rétention courte. Les
modèles de samples bruts peuvent déclarer runtime.retention avec
maxAgeSeconds, un
timestampField optionnel par défaut à createdAt, et un pruneBatchSize
optionnel plafonné à 1 000. Les opérateurs MCP de confiance peuvent appeler
yayaw_dynamic_data_retention_prune avec dryRun d'abord, une reason, et une
limit optionnelle plus basse; le tool vérifie l'authz Yayaw
dynamic-data:manage, résout la politique du modèle déployé, requête les
records expirés via le service record générique, puis supprime chaque ligne
expirée par le même chemin de delete record afin que les politiques de
référence continuent de s'appliquer. Cela donne aux data planes chauds une
table raw-metric bornée pour le debug tout en gardant les modèles de rollup
minute/hour/day disponibles pour des fenêtres d'analytics plus longues. Les routes peuvent
déclarer recordFilters: des filtres contrôlés serveur ajoutés avant les
filtres fournis par l'appelant sur les lectures list/get et les lookups de
mutation. Ils supportent les mêmes opérateurs de comparaison que les
preconditions, avec une value littérale ou les sources now,
actor_user_id, actor_api_key_id, actor_principal, organization_id et
param. Ils supportent aussi in et not_in avec un tableau littéral non
vide, ainsi que is_null_or_greater_than pour les timestamps de validité
nullable. Utilisez-les pour limiter les routes runtime génériques à des records
comme les grants du principal courant, les ressources possédées par un principal,
les lignes d'organisation ou les ressources possédées par un paramètre de
route. Ils complètent l'authz Yayaw dynamic-data; ils ne remplacent pas
l'autorisation serveur.
Les routes peuvent aussi déclarer relatedRecordFilters: des filtres
d'existence cross-model contrôlés serveur, appliqués à la requête du record
source elle-même. Un filtre déclare un sourceField, un modèle cible et un
targetField, puis des filters cibles optionnels; le runtime compile cela en
condition Postgres exists, afin que la pagination list et totalCount restent
corrects. Les filtres cibles supportent les mêmes sources que les
record guards/effects, dont actor_principal, param, source: "value" et
source: "record". Utilisez ces filtres pour des contrats de lecture comme
"lister uniquement les ressources qui ont un grant actif pour le principal
courant" sans handler query spécifique au produit. Les cibles restent dans le
scope de la route source sauf si targetScope est déclaré, et le bundle apply
valide chaque modèle cible ainsi que targetField et les champs filtrés avant
d'enregistrer les drafts. Le builder de routes du dashboard peut configurer le
mapping de base champ source, slug de modèle cible, champ cible, scope cible et
mode d'existence; les filters cibles imbriqués restent disponibles via les
manifests MCP pour les contrats avancés.
Les routes peuvent aussi déclarer recordGuards: des vérifications d'existence
cross-model en lecture seule, évaluées avant que la route runtime lise ou mute
des records. Les filtres de guard supportent les sources de filtres normales
plus source: "value" avec sourceField, qui lit une valeur normalisée depuis
le payload de la route, et source: "record" avec sourceField, qui lit une
valeur depuis le record source existant sur les routes de type update.
Utilisez-les pour exprimer des contrats produit comme
"le principal courant doit avoir un grant control/admin actif sur cette
ressource" sans ajouter de handler backend codé en dur. Les cibles de guard
restent dans le scope de la route source sauf si targetScope est déclaré, et
le bundle apply valide chaque modèle cible et chaque champ filtré avant
d'enregistrer les drafts. Le builder de routes du dashboard peut configurer les
modèles cibles de guard, scopes cibles, mode d'existence et filtres bornés avec
les mêmes sources exposées par MCP.
Les routes d'écriture peuvent déclarer recordEffects pour des effets de bord
cross-model bornés après la réussite de la mutation source. Le premier kind
supporté est update: il résout exactement un record cible par filters, puis
applique des valueAssignments et valueIncrements contrôlés serveur via le
même service dynamic-data record. Les filtres d'effet supportent les sources de
filtres normales plus source: "value" avec sourceField, qui lit une valeur
normalisée depuis le payload de mutation source, par exemple resource_id pour
mettre à jour la ressource liée à un workflow. Les routes de type update peuvent
utiliser source: "record" avec sourceField pour lire des champs immuables
ou sensibles depuis le record source existant, par exemple terminer un workflow
et mettre à jour la ressource depuis le resource_id stocké sans accepter ce
champ depuis le client. Les effects peuvent déclarer
conditions, évaluées sur le payload source normalisé avant le lookup de la
cible. Utilisez les opérateurs equals, not_equals, in, not_in,
is_empty et is_not_empty pour des effets de workflow comme décrémenter le
compteur de workflows actifs d'une ressource seulement quand le nouveau statut
du workflow est terminal. Les cibles d'effet restent dans le scope de la route
source sauf si targetScope est déclaré, les écritures utilisent la
dataVersion courante comme garde optimiste, et le résultat d'effet est exposé
dans la réponse runtime et le payload d'événement.
Le builder de routes du dashboard peut configurer les effets update avec
modèle/scope cible, filtres cibles bornés, assignments contrôlés serveur et
incréments numériques; les manifests MCP restent le meilleur chemin pour les
gros bundles générés.
Les routes d'écriture
peuvent aussi déclarer preconditions: des filtres de champs bornés qui
doivent matcher l'enregistrement cible avant la mutation. Elles supportent
equals, not_equals, greater_than, greater_or_equal, less_than,
less_or_equal, in, not_in, is_null et is_not_null; les opérateurs de
comparaison acceptent une value littérale ou valueSource: "now" pour des
gardes de timestamp comme expires_at > now, tandis que les opérateurs de set
exigent un tableau littéral non vide. Les routes génériques update, delete,
heartbeat, metrics et pairing_claim évaluent toutes le même contrat via le
service record, afin que les extensions produit puissent modéliser des claims
uniquement pending, des grants non révoqués, des sessions non expirées ou des
transitions de workflow sans ajouter de handlers backend codés en dur.
Les routes d'écriture peuvent déclarer clientValueFields pour n'accepter que
certains champs bruts du payload client avant l'application des params,
transforms, assignments, increments, guards et de la validation record. Quand
clientValueFields est omis, le comportement historique reste inchangé; quand
la liste est vide, aucun champ brut client n'est accepté.
Le builder dashboard présente cette liste comme des checkboxes de champs plutôt
que comme un texte libre séparé par des virgules. Les valueTransforms déclarés
avec sourceType: "value" acceptent implicitement
leur champ source comme entrée transitoire, afin qu'une route puisse hasher un
code court sans persister ni allowlister séparément la valeur en clair. Utilisez
cette primitive pour les endpoints de workflow étroits comme /status, les
routes de révocation, les payloads heartbeat et les contrats d'ingestion de
metrics.
Les routes peuvent aussi déclarer clientValueConditions avec les mêmes
opérateurs bornés que les assignments et les effects. Ces conditions sont
évaluées sur le payload client normalisé après allowlist et transforms, mais
avant les assignments contrôlés serveur. Si elles ne matchent pas, la route
échoue avant tout lookup ou write; utilisez-les pour contraindre des entrées de
cycle de vie, par exemple un endpoint de statut de session qui n'accepte que
active, ended ou failed.
Les routes d'écriture peuvent déclarer valueAssignments pour des valeurs de
champs contrôlées serveur, appliquées après paramFieldMap et
valueTransforms, et qui écrasent toute valeur client pour le même champ
déclaré. Les sources d'assignments sont literal, now, actor_user_id,
actor_api_key_id, actor_principal, organization_id et param.
actor_principal stocke le format de référence principal générique utilisé par
les références dynamic data, en préférant principal:api_key:<id> pour les
acteurs API key et en retombant sur principal:user:<id> pour les acteurs
session ou OAuth. Utilisez ces assignments pour des champs comme claimed_at,
claimed_by_principal_id, des timestamps de heartbeat, des états de workflow
et des valeurs de route organisationnelles qui ne doivent pas être confiées à
l'appelant. Les assignments source: "now" peuvent déclarer un
offsetSeconds borné, par exemple 300 pour un expires_at à cinq minutes,
afin que les TTL de pairing sessions, grants temporaires ou endpoints expirants
restent contrôlés par le serveur.
Les assignments peuvent aussi déclarer conditions, avec les mêmes opérateurs
bornés que les record effects, afin qu'une route conserve l'état de workflow
fourni par l'appelant tout en assignant des champs compagnons serveur seulement
pour certaines valeurs entrantes, par exemple ended_at seulement quand
status in ["ended", "failed"].
Utilisez valueAssignments pour les valeurs qui doivent s'appliquer sur toutes
les branches d'écriture. Les routes qui peuvent se diviser entre create/update
peuvent aussi déclarer createValueAssignments et updateValueAssignments: les
routes de création, les metrics append-only et la branche création des upserts
heartbeat/metrics appliquent la liste create, tandis que les routes update,
pairing_claim et la branche update des upserts heartbeat/metrics appliquent
la liste update. C'est utile pour des valeurs comme les timestamps trust
first_seen_at qui doivent être définis une seule fois à la création, ou des
champs serveur accepted_at/updated_by_principal_id qui doivent changer
uniquement lorsqu'un record existant est muté.
valueTransforms
s'exécute avant le lookup d'idempotence et la validation de l'enregistrement:
les routes peuvent donc transformer params.code ou une valeur de requête en
champ déclaré comme code_hash sans écrire la valeur source en clair. Les
sources transformées sont supprimées par défaut des valeurs persistées, des
params de réponse, des payloads d'événements et des params utilisés pour les
topics de route; définir removeSource: false uniquement lorsqu'une route doit
intentionnellement garder la source brute visible. Les algorithmes supportés
sont sha256 et hmac_sha256; les transforms HMAC requièrent
DYNAMIC_DATA_RUNTIME_TRANSFORM_SECRET.
paramFieldMap peut lier des paramètres de chemin comme :workflowId à des
champs déclarés du modèle avant validation. Le builder dashboard édite ce
mapping comme des lignes de bindings structurées plutôt que comme un texte libre
param=field. Les clés paramFieldMap et les
valueTransforms avec sourceType: "param" doivent référencer un paramètre
déclaré par le path effectif de la route, y compris les paths par défaut
résolus depuis le type de route. Les routes de mutation réussies et non dry-run
ajoutent un événement durable dans dynamic_data_runtime_events
comme outbox générique avant de répondre. Le mapping est
create -> created, update -> updated, patch_status -> status_changed,
delete -> deleted, heartbeat -> heartbeat et
metrics_ingest -> metric, et pairing_claim -> updated; list et get
n'émettent pas d'événements runtime.
Chaque événement stocke le scope, l'organisation, le modèle, l'enregistrement,
la route, le type d'événement, le payload, l'acteur et les topics rendus depuis
runtime.realtime.channels. Les templates de topics peuvent utiliser des
valeurs réservées comme {organizationId}, {modelSlug}, {recordId},
{routeKind} et {eventType}, ainsi que les paramètres de route et les
valeurs primitives du payload. Les channels avec des tokens manquants sont
ignorés. La persistance d'événement est fail-open pour l'appelant: la mutation
d'enregistrement peut réussir avec eventId: null tandis que les topics restent
visibles dans la réponse. Cette outbox est le socle long terme pour SSE,
WebSocket, workers et fan-out analytics.
Les consommateurs d'événements runtime peuvent lire le feed générique sur
/api/dynamic-data/runtime-events. L'endpoint utilise le même modèle d'acteur
session ou bearer que l'exécution runtime, requiert
dynamic-data-runtime:read pour les clés API runtime dédiées, conserve le
fallback control-plane pour les clés MCP existantes, et vérifie toujours
l'authz Yayaw dynamic-data:list pour le scope ou le modèle demandé. Les
clients MCP de confiance peuvent inspecter la même outbox avec
yayaw_dynamic_data_runtime_events pour le debug et les opérations, tandis que
les consommateurs runtime chauds doivent continuer à utiliser HTTP/SSE. Le
polling JSON est le mode par défaut. stream=true ou Accept: text/event-stream ouvre
un stream SSE borné sur l'outbox, avec le curseur d'événement comme id SSE afin
que les clients puissent reprendre avec Last-Event-ID. Les filtres incluent
scope, orgId, topic, modelId, modelSlug, recordId, routeId,
routeKind, eventType, cursor et limit; SSE accepte aussi pollMs et
durationSeconds. yayaw_dynamic_data_ui_capabilities annonce l'endpoint,
les transports supportés, le paramètre de curseur, le header de reprise et les
filtres de query, tandis que chaque manifest runtime déployé expose des entrées
runtimeEventFeeds dérivées de runtime.realtime.channels avec des templates
de query JSON et SSE prêts à utiliser. Les clients remplacent les tokens de
template de topic comme {recordId} ou {organizationId} avant la requête.
Les définitions de modèles peuvent aussi déclarer
ui.detail.eventTimelines pour rendre des timelines dashboard nommées avec
eventType, routeKind, topic, topicTemplate, limit et recordFilter,
afin d'inspecter l'activité ressource, workflow, trust ou métriques sans page
custom. Les timelines qui déclarent routeKind doivent correspondre à un kind
de route runtime activé, et celles qui déclarent topicTemplate doivent
correspondre au template de topic d'un channel realtime déclaré, afin que les
panneaux dashboard générés ne rendent pas silencieusement des feeds vides. Les filtres de containment sur les topics sont couverts par
un index GIN sur l'outbox durable, afin que les feeds par channel restent
viables quand les événements de sessions et metrics augmentent. Le transport
actuel interroge l'outbox durable et émet des événements heartbeat et complete;
une livraison future WebSocket ou pub/sub DB pourra réutiliser le même contrat
d'événement.
Les appelants runtime chauds
doivent utiliser l'endpoint data-plane générique
/api/dynamic-data/runtime/* plutôt que MCP. Par exemple,
POST /api/dynamic-data/runtime/resources/heartbeat résout la route runtime
déployée /resources/heartbeat et l'exécute via le même exécuteur générique.
L'endpoint accepte la session dashboard active ou un acteur bearer Yayaw. Les
clés API devraient utiliser dynamic-data-runtime:read pour les routes en
lecture et dynamic-data-runtime:write pour les routes d'écriture; les clés MCP
existantes restent compatibles via le fallback control-plane décrit plus haut.
Les acteurs organisation ciblent par défaut les routes runtime de leur
organisation, et l'endpoint vérifie toujours l'authz Yayaw dynamic-data avant
toute exécution. Les query params scope=global|organization, orgId,
extensionKey et routeKey peuvent sélectionner explicitement un scope ou
désambiguïser une route d'extension stable quand l'acteur y est autorisé. Les
bodies JSON peuvent transmettre values, filtres, search, sort,
limites/offsets d'enregistrements, expectedDataVersion optimiste, et reason
optionnel pour les routes qui déclarent reasonRequired.
Après un déploiement de bundle, les clients MCP n'ont pas besoin de reconstruire
ces appels HTTP eux-mêmes: postInstall.models[].routes[].dataPlane expose
l'endpoint, les query params, l'action de clé API, l'action d'autorisation
Yayaw, la politique de rate limit, et l'exigence de reason pour chaque route
runtime déployée.
Les channels realtime exposent le même passage de relais via
postInstall.models[].realtime[].dataPlane: json est l'endpoint de polling,
sse ajoute stream=true, et cursorParam plus resumeHeader indiquent
comment reprendre la lecture des événements.
Les réponses HTTP réussies exposent une enveloppe data-plane publique:
result, params, rateLimit, dryRun, runtimeEvent, l'action d'authz
effective, les conflicts de routes et une métadonnée de route sûre (id,
key, kind, method, path, modelSlug, scope, dashboardRoute,
dashboardRecordRouteTemplate et dashboardSection). Les détails internes
d'exécution comme les noms de tables physiques restent côté serveur.
Quand une route déployée déclare rateLimit.maxRequests et
rateLimit.windowSeconds, l'endpoint data-plane applique cette limite avant
exécution avec le sujet configuré (actor, ip, organization, principal
ou record). Les réponses comptées incluent RateLimit-Limit,
RateLimit-Remaining et RateLimit-Reset; les routes dépassées renvoient
429 avec Retry-After.
Les champs reference utilisent le même contrat de modèle natif.
Un champ reference doit déclarer soit relation.modelSlug pour un autre
modèle dynamique, soit relation.targetEntity pour une entité système (user,
organization, team, api_key, ou principal). Il peut aussi déclarer
targetScope, displayField, valueField et l'intention
onDelete pour les références vers modèles dynamiques. La table générée stocke
la référence dans une colonne texte native et indexe les champs reference par
défaut pour garder les modèles riches en relations, comme principals, ressources,
grants et workflows, rapides. Les opérations create et update d'enregistrements
résolvent les modèles dynamiques cibles déployés et refusent les références vers
des enregistrements absents avant l'écriture, avec id par défaut ou le
valueField déclaré lorsqu'il est configuré. Les références système stockent
aujourd'hui des ids et valident user, organization, team et api_key
dans les tables système Yayaw. Les références principal utilisent des valeurs
stables comme principal:user:<userId> et
principal:api_key:<keyId>; les principals user doivent résoudre vers un
utilisateur membre de l'organisation source quand elle existe, et les principals
API key doivent résoudre vers une clé active de cette organisation. Les autres
types de sujet comme resource ou agent résolvent via des modèles dynamiques
déployés dans le même scope: soit un modèle déclare extension.entityBindings
avec kind: "represents" et target égal au type de sujet, puis l'id de la
référence principal est comparé à l'id d'enregistrement de ce modèle, soit un
modèle générique représente principal et stocke le sujet dans des champs
conventionnels type et ref_id. Le builder no-code de brouillons de modèles
du dashboard expose les mêmes réglages de référence, d'index composites, de
contraintes uniques et de présentation UI par champ pour les nouveaux modèles,
afin que les opérateurs puissent créer des contrats de tables natives riches en
relations et des surfaces Yayaw Table formulaire/table utilisables sans éditer
le JSON brut du modèle. Les sélecteurs de champs pour index composite et
contrainte unique sont des pickers structurés dérivés des champs du brouillon,
tandis que les bundles MCP continuent d'utiliser des tableaux de champs
explicites. Le builder inclut les listes d'options finies pour les widgets
select, radio et multiSelect, ainsi que les bindings de champ
value-type pour les éditeurs primitifs dynamiques. Les builders de routes
runtime résolvent les slugs de modèles cibles visibles en selects et dérivent
des pickers de champs cible pour les rollups métriques, les filtres
related-record, les guards et les effects, tout en conservant les valeurs
existantes inconnues pour compatibilité. targetScope: "same" suit le scope du
modèle source pour les cibles dynamiques, tandis que les cibles organization
explicites exigent un contexte organisation. Les suppressions
d'enregistrements appliquent les politiques de référence dynamiques avant de
retirer la cible: l'absence de onDelete se comporte comme restrict,
restrict bloque la suppression tant que des dépendances existent, et
set_null nullifie les champs référents optionnels en incrémentant leur data
version. cascade est volontairement refusé tant que des dépendances existent,
jusqu'à ce que Yayaw ait une suppression transactionnelle de graphe avec garde
de cycles pour les tables générées. Dans le formulaire dashboard des
enregistrements, les champs reference vers modèles dynamiques peuvent devenir
des selects quand le modèle cible est visible pour l'acteur: valueField
fournit la valeur stockée, displayField fournit le libellé d'option, et
targetScope: "same" suit le scope du modèle source. Les références système
vers user, organization, team et api_key peuvent aussi devenir des
selects depuis les tables système Yayaw scopées; principal compile les sujets
user, API key, modèles de sujets dynamiques et enregistrements de registre
principal dynamique en options préfixées quand ils sont disponibles. Les options
principal dynamiques suivent le même scope que la validation runtime: les
modèles déployés avec extension.entityBindings.kind: "represents" peuvent
représenter des types de sujets concrets, tandis qu'un modèle qui représente
principal peut exposer des enregistrements type plus ref_id. Si les
options ne peuvent pas être chargées, le champ retombe sur l'input texte normal
sans affaiblir l'autorisation.
Les clients MCP peuvent appeler yayaw_dynamic_data_reference_options avec un
slug de modèle et une clé de champ pour charger les mêmes options sûres que le
dashboard pour les références système et les références vers modèles
dynamiques, après vérification server-side des permissions du modèle source et
du modèle cible.
La liste des modèles du dashboard dynamic-data expose aussi un flux no-code
borné pour les acteurs avec dynamic-data:manage. Il crée des documents de
modèle draft en table native depuis un nom, un slug, une portée, une section de
navigation et des champs scalaires, puis passe par le même pipeline de
validation, publication et déploiement dynamic-data que MCP avant toute DDL
Postgres générée. Le même builder peut aussi configurer des
runtime.realtime.channels et des ui.detail.eventTimelines bornés, en
validant les types de routes des timelines contre les routes runtime activées et
les templates de topics contre les channels realtime déclarés avant
l'enregistrement du brouillon. Les filtres d'événements des channels realtime
sont rendus comme des checkboxes d'événements structurées, tandis que les
bundles MCP peuvent toujours envoyer des arrays d'événements ou des chaînes
séparées par des virgules. Le détail modèle expose aussi une preview du plan de
déploiement pour la révision publiée et le bouton qui applique ce plan vérifié,
tandis que l'action de ligne de la liste ouvre cette review au lieu d'appliquer
la DDL directement. La preview affiche le même statut d'évolution de schéma et
les mêmes compteurs de changements bloquants que MCP. Le détail affiche aussi un
panneau de cycle des révisions avec les marqueurs dernière, publiée et
déployée, les révisions immuables récentes et le résumé du diff déployée vers
dernière produit par les mêmes services de révisions exposés via MCP. Les
acteurs avec dynamic-data:manage peuvent restaurer une ancienne révision
immuable depuis ce panneau après confirmation de la cible et saisie d'une
raison; la révision restaurée devient le dernier brouillon et nécessite encore
une publication explicite puis une review de déploiement. Ces actions
réutilisent les services dynamic-data partagés et l'autorisation server-side
dynamic-data:manage au lieu de faire confiance à l'état du dashboard.
Admin > Dynamic Data et chaque racine de section dynamic-data générée par MCP
affichent un panneau de readiness opérateur au-dessus de la table des modèles,
avec le résumé des modèles visibles, brouillons bloqués, backlog
publication/déploiement, routes runtime déployées, routes d'ingestion métrique
et compteurs de rollups, feeds d'événements runtime, couverture UI/navigation et
endpoints de passage MCP/runtime à vérifier après application d'un bundle, ainsi
que les compteurs de liaisons d'extensions d'entités et de cibles liées.
Les clients MCP peuvent appeler yayaw_dynamic_data_ui_capabilities pour
découvrir les types de champs, capacités d'extension, kinds de liaisons
d'entités, targets système réservées, kinds/defaults de routes runtime API,
actions d'authz runtime, sujets de rate-limit, événements realtime, kinds de
projections, renderers de colonnes Yayaw Table, aliases sûrs, widgets de
formulaire et aliases de widgets, sources d'options de référence, outils de
discovery d'extensions, sections de navigation supportées, racine control-plane
Admin statique et pattern de route des sections dynamiques avant de générer un
document de modèle. Le même payload de capabilities expose
dashboard.operatorReadiness, avec la racine control-plane statique, le pattern
de sections générées, états d'attention, champs résumés, garanties d'authz et
endpoints de passage MCP/runtime rendus par le panneau de readiness du dashboard. Les
réponses bundle apply et post-install MCP exposent aussi les compteurs de routes
métriques, compteurs de rollups, clés de routes métriques, compteurs de
liaisons d'extensions et cibles liées par modèle pour vérifier le routing
analytics et les extensions natives sans code produit spécifique. Le payload de
capabilities inclut aussi
les defaults de compatibilité par type de champ pour widgets de formulaire,
renderers de table et éditeurs inline, les racines de routes par section, modes
d'affichage, presets de layout, modes de clic ligne, variantes de colonnes,
modes d'affichage URL, éditeurs/triggers d'inline edit, presets galerie,
sources d'assignation, de filtre et de transform runtime, filtres de timelines
de détail, exemples de targets génériques comme ressources et activités, et
colonnes système sûres (id, dataVersion, timestamps, IDs acteurs et ID
organization) utilisables dans la config table, le tri, les filtres et la
recherche globale. La validation de modèle signale des warnings non bloquants
quand les widgets ui.form, renderers de colonnes ui.table ou éditeurs
inline déclarés sortent de ces defaults de compatibilité, afin que les
interfaces générées par MCP restent neutres produit tout en exposant les choix
UI suspects avant déploiement. ui.table
configure la surface Yayaw Table des enregistrements déployés, ui.form
configure le formulaire create/edit généré par Yayaw Table, et ui.nav ajoute les modèles déployés à la
sidebar, aux breadcrumbs, à la navigation de section backend et à la recherche
du command menu quand l'objet ui.nav est présent, sauf si enabled vaut
explicitement false. La navigation utilise la section dashboard configurée,
built-in ou générée par MCP, et pointe vers la route canonique dynamic-data
/dashboard/{section}/dynamic-data/{scope}/{modelSlug} avec le modèle cible
déjà sélectionné. Le créateur no-code de modèle dans le dashboard écrit les
mêmes métadonnées ui.nav que MCP pour enabled, le libellé, l'icône,
l'ordre et la section, afin que les opérateurs puissent promouvoir un brouillon
en table native dans une navigation dashboard propre sans toucher au code.
Quand des liens statiques de section et des liens de modèles dynamiques
coexistent, la sidebar sépare les liens de modèles générés tandis que les
breadcrumbs, la navigation de section et la recherche du command menu n'exposent
que des destinations navigables. Admin > Dynamic Data est la surface statique
de control plane pour gérer les modèles. Les surfaces produit doivent être
déclarées par MCP avec ui.nav.section; toute section normalisée hors du set
built-in devient une section d'app dashboard à
/dashboard/{section}/dynamic-data dès qu'au moins un modèle déployé visible y
est rattaché. Les sections d'app générées mettent d'abord en avant la surface
Yayaw Table d'enregistrements du modèle sélectionné, les métadonnées de détail,
les panneaux d'enregistrements liés et les timelines d'événements runtime ; le
cycle de vie/readiness control-plane du modèle reste disponible depuis Admin >
Dynamic Data. Les bundles peuvent définir ui.nav.sectionLabel,
ui.nav.sectionIcon et ui.nav.sectionOrder pour piloter la section top-level
générée sans changement de code ; définissez ui.nav.enabled=false uniquement
pour les modèles déployés qui doivent rester cachés de la navigation. Les
modèles sans section configurée restent dans Admin > Dynamic Data, et
l'ancienne sélection par query
?model=&scope= reste acceptée pour compatibilité. ui.nav.icon doit faire
partie des valeurs nav.icons renvoyées par
yayaw_dynamic_data_ui_capabilities; les icônes non supportées sont ignorées
pour laisser la sidebar utiliser le fallback
TableProperties. yayaw_dynamic_data_runtime_manifest renvoie aussi les
fields normalisés, runtimeApiRoutes effectives, runtimeEventFeeds, systemColumns,
ui.dashboardRoute, ui.dashboardRecordRouteTemplate,
ui.dashboardSection, ui.table, ui.form, ui.detail et
ui.runtimeActions pour chaque modèle déployé afin que les clients MCP
puissent pointer vers la surface dashboard modèle résolue, ouvrir un
enregistrement sélectionné via le template recordId={recordId}, inspecter les
actions runtime ligne/détail, s'abonner aux feeds d'événements runtime JSON/SSE
déclarés, et rendre les interfaces Yayaw Table configurées sans reconstruire
les règles de schéma, de record-system, de route, d'event-feed ou d'UI. La
checklist de vérification post-install du bundle inclut des étapes
runtime_ui_action_dry_run pour les actions dont la cible runtime se résout
vers une route déployée. La navigation par modèle est filtrée avec l'authz Yayaw sur
dynamic-data:list et le resourceId du modèle déployé: un grant global ou
organisation large expose chaque modèle visible dans ce scope, tandis qu'un
grant resource-scoped n'expose que le modèle correspondant. Les gardes des
routes canoniques de modèle résolvent {scope, modelSlug} vers l'item de
registre déployé puis rejouent le même check dynamic-data:list avec le
resourceId du modèle, afin que les deep links directs suivent le même contrat
resource-scoped que la sidebar au lieu d'exiger un accès dynamic-data large. La
liste de modèles du dashboard utilise le même filtre par modèle, affiche les
labels et capacités d'extension comme colonnes Yayaw Table filtrables, et
dynamic-data:manage par modèle contrôle les actions d'enregistrement
générées. Le filtrage de navigation conserve le parent de section configuré
quand un modèle dynamique autorisé est visible, même si cette section n'a aucun
enfant statique visible pour l'acteur courant. Le détail de modèle du dashboard
dynamic-data expose aussi les clé/label/version d'extension, capacités, tags,
liaisons d'entités, le label, l'icône, la section et la route canonique
ui.nav résolus, les routes API runtime, les feeds d'événements runtime, les
topics realtime, les projections et les métadonnées de surfaces UI déployées,
afin que les contrats produit écrits par MCP soient inspectables en production
sans ouvrir le JSON brut du modèle. Les nouvelles interfaces générées par MCP
peuvent aussi apparaître sur les pages natives via le panneau réutilisable
d'extensions d'entités: les réglages d'organisation le montent pour
l'organisation active, les réglages de compte pour l'utilisateur courant, et ces
panneaux serveur restent cachés tant qu'aucune extension autorisée n'existe afin
de garder les pages natives silencieuses par défaut. Admin > Users monte le
panneau client pour le détail utilisateur sélectionné, et les réglages
développeur le montent sous les clés API listées; ces panneaux client restent
aussi cachés tant qu'aucune extension autorisée n'existe. Les actions générées par MCP
devraient définir ui.table.rowActions[].runtime.routeKey avec la clé stable
de la route runtime.api.routes[].key; le builder no-code du dashboard écrit
le même contrat route-key stable pour les actions bornées et peut aussi les
exposer dans la vue détail d'enregistrement sans ajouter de code repository. La
méthode et le chemin sont optionnels et restent un fallback de résolution et la
source des paramètres de chemin.
Lorsqu'une action déclare directement method/path, ou les surcharge en plus de
routeKey, la cible effective doit correspondre à une route runtime activée
après résolution de runtime.api.basePath. Cette vue de
contrat expose aussi les actions ui.table.rowActions avec routeKey,
méthode/chemin runtime, mode de rafraîchissement, conditions, params de chemin,
bindings, valeurs littérales, inclusion d'ID record et verrou optimiste, puis
liste les boutons ui.detail.actions et l'action ligne qu'ils référencent.
yayaw_dynamic_data_ui_capabilities expose le même contrat d'exécution des
row actions sous forme lisible machine: refresh par défaut, checks serveur,
ordre de résolution de cible, fallback method/path, path params, valeurs
littérales, value bindings, enabledWhen, verrou optimiste et support
route-key.
Les pages natives du dashboard peuvent aussi afficher les extensions écrites
par MCP sans ajouter de React spécifique au produit. Le loader partagé
d'extensions d'entités dashboard découvre les modèles déployés dont
extension.entityBindings cible une entité native comme organization,
interroge seulement le scope global et le scope de l'organisation active de
l'acteur, filtre chaque modèle avec dynamic-data:list ou
dynamic-data:manage avant de charger les compteurs, puis retourne la route
canonique du modèle, le champ de liaison, les métadonnées d'extension et le
compteur autorisé. Les settings d'organisation utilisent ce panneau réutilisable
pour la cible organisation active, les settings de compte utilisent le même
panneau pour la cible utilisateur courant, et Admin > Users monte une version
client pour le détail de l'utilisateur sélectionné. Les settings développeur
montent le même panneau client sous chaque clé API listée quand un modèle
déployé étend api_key, tout en gardant la page silencieuse lorsqu'aucune
extension de clé API n'existe. Les opérateurs peuvent ainsi voir les modèles
dynamiques en tables natives qui étendent des entités natives coeur et ouvrir
leur surface depuis la page native. Quand la page native fournit un
identifiant de cible, le panneau pointe vers la route dynamic-data canonique
avec les filtres de la table Yayaw Table du modèle encodés dans l'URL pour les
bindings basés sur un champ, ou avec recordId pour les bindings represents,
et le preload serveur du dashboard applique cet état avant de rendre la table
d'enregistrements. yayaw_dynamic_data_ui_capabilities expose ces points de
montage natifs sous extension.nativeSurfaces, avec la cible, la route
dashboard, le scope et la source d'identifiant de cible comme
current_user_id, selected_user_id, listed_api_key_id ou
active_organization_id, afin que les clients MCP sachent où un binding
represents, extends ou references deviendra visible avant d'appliquer un
bundle.
ui.form
permet au MCP de piloter
l'ordre, la visibilité et les sections des champs, les libellés, aides,
placeholders, états read-only/désactivés, layout drawer/modal, et des hints de
widgets sûrs comme text, textarea, number, select, radio, switch,
checkbox, url, email, tel, password, date, json, multiSelect,
value-type et collection. ui.form.sections groupe des champs existants
sous des sections titrées sans changer la forme du payload record; les champs
omis de toutes les sections restent rendus après les sections déclarées.
ui.table accepte
les renderers Yayaw Table sûrs (text, number, boolean, date, select,
tag, multiSelect, image, url, code, json, string,
dynamicType) ainsi que des alias comme jsonb, badge, option,
multiOption, currency, decimal, integer et dynamic-type; il peut
aussi piloter les vues sauvegardées, l'inline edit, les empty states, les
formats nombre/date, les couleurs de tags, le nombre d'items de prévisualisation
JSON via columns.<field>.maxItems, l'affichage des guillemets string via
columns.<field>.showQuotes, la participation à la recherche globale via
columns.<field>.enableGlobalSearch et les modes
table/kanban/gallery. Le builder no-code du dashboard peut configurer un
sous-ensemble borné de ces métadonnées de vue, dont le mode de sélection de
lignes, les modes disponibles, le mode par défaut, le
regroupement/titre/colonnes de cartes kanban et la présentation
image/titre/colonnes de cartes galerie, sans éditer directement le document brut
du modèle. Le contrôle de sélection peut garder l'inférence des colonnes
virtuelles MCP en auto, ou persister une valeur Yayaw Table
enableRowSelection explicite. Les modes d'affichage, les références de champs
kanban/gallery et les listes de champs d'index ou de contraintes uniques sont
rendus comme des selects ou multi-selects structurés dérivés des champs du
brouillon courant, tandis que l'entrée MCP peut toujours envoyer les mêmes
valeurs sous forme d'arrays ou de chaînes séparées par des virgules. Il peut
aussi éditer des options bornées au niveau des colonnes Yayaw
Table pour les en-têtes, le tri, les filtres, la recherche globale, le
groupement, les tailles, les calculs par défaut, les presets de date, les
formats numériques, les modes d'affichage URL, les limites d'aperçu JSON, les
couleurs de tags, l'affichage des guillemets string et les clés de type
dynamique.
Les champs select/radio peuvent recevoir des options fournies par MCP, les
champs numériques min, max et step, les champs date minDate et
maxDate sérialisés, les champs textarea/json rows, les champs URL la
prévisualisation de métadonnées, et les widgets value-type doivent déclarer
un valueTypeField valide pour que Yayaw Table choisisse l'éditeur. Les
widgets collection sont supportés sur les champs modèle json via un bloc
collection déclaratif avec champs d'item, colonnes, create item/actions
optionnels, labels d'item, empty labels et validation de champs d'item
obligatoires; le dashboard compile ce bloc vers l'éditeur collection natif de
Yayaw Table sans accepter de renderer React custom depuis MCP. Le builder de
modèle no-code peut configurer les champs d'item, colonnes, libellés et valeurs
par défaut des collections adossées à du JSON sans éditer directement le
document de modèle. Les widgets
multiSelect sont aussi supportés sur les champs modèle json quand le champ
déclare des options finies string ou number; le dashboard les compile vers le
champ formulaire checkbox-list natif de Yayaw Table et stocke les valeurs
sélectionnées comme tableau JSON. Cette
configuration UI ne donne jamais d'accès aux données et ne modifie pas la DDL
Postgres générée. Le champ de formulaire Yayaw Table custom demande encore
des renderers React et ne doit pas être exposé par les documents modèle MCP.
Les tables dashboard d'enregistrements dynamiques envoient la pagination, les
filtres simples, les filtres avancés Yayaw Table supportés et le tri à Postgres
au lieu de découper un échantillon fixe côté client; filtres, recherches et tris
restent limités aux champs déclarés et aux colonnes système sûres. La recherche
globale utilise par défaut id plus les champs textuels déclarés, ou les champs
explicitement marqués enableGlobalSearch dans ui.table.columns. L'outil MCP
yayaw_dynamic_data_records_query accepte soit une chaîne de recherche, soit
{ query, fields } pour une recherche bornée explicite. Les filtres avancés
supportés couvrent texte contains/starts/ends/not-contains/empty, égalités et
ranges nombre/date, inclusion/exclusion select, et une containment textuelle
conservatrice pour les valeurs multi-select stockées comme JSON.
Lors de l'ajout d'un outil, préférez une implémentation service-layer réutilisable par les server actions UI ou les scripts. Les handlers doivent rester fins: valider l'entrée, vérifier les permissions, appeler le service partagé et écrire l'audit.
Ressources
yayaw://statusyayaw://schemasyayaw://pages/{scope}/{slug}yayaw://data/{scope}/{modelSlug}/{entrySlug}yayaw://email-templates/{slug}yayaw://audit/recent
Les workflows facturation, remises, médias, design tokens, feature flags et
domaines d'organisation sont exposés comme outils plutôt que comme ressources
MCP. Les lectures de pages et données scopées organisation doivent utiliser les
outils lorsqu'un orgId explicite est requis.
Règles De Sécurité
Les écritures production exigent tout ce qui suit:
control-plane-mcp-enabledest activé.- Une clé API Better Auth valide ou un access token OAuth est fourni comme
Authorization: Bearer <token>. - La clé ou le token OAuth a la permission de plan de contrôle requise.
- L'acteur passe les checks d'autorisation de ressource Yayaw sous-jacents.
- L'entrée de l'outil inclut
reason.
Les clés détenues par l'organisation sont en plus limitées à leur organisation
pour les opérations contenu/média bornées. Les clés personnelles nécessitent
toujours la permission seedée Yayaw apikey:manage pour être émises depuis le
dashboard.
Les outils de domaines publics d'organisation suivent la même frontière
d'organisation. Les clés d'organisation opèrent uniquement sur leur
organisation; les clés personnelles doivent passer un orgId explicite. Les
mutations de domaines exigent organization:update ou
organization-settings:manage, un reason, et utilisent dryRun pour le
prévol. PUBLIC_DOMAIN_PROVIDER=vercel attache le hostname au projet Vercel
partagé, lit les enregistrements DNS recommandés et vérifie les challenges de
propriété Vercel. PUBLIC_DOMAIN_PROVIDER=manual-dns génère un token TXT de
propriété et vérifie le DNS public pour le TXT attendu plus les indications
CNAME/A configurées.
yayaw_deploy_status renvoie des métadonnées de déploiement neutres vis-à-vis
du fournisseur. Sur Vercel, il expose les champs runtime Vercel; sur les
déploiements auto-hébergés, il expose les valeurs statiques DEPLOYMENT_URL,
DEPLOYMENT_ENV, DEPLOYMENT_GIT_COMMIT_SHA et DEPLOYMENT_GIT_COMMIT_REF
lorsqu'elles sont configurées.
Les écritures de composants sont des imports catalogue ou recompilations en
base depuis des snapshots source stockés. yayaw_component_recipe_import
n'accepte que des recettes déclaratives bornées adossées à des kinds runtime
sûrs présents dans le dépôt, crée une révision draft_ready et laisse la
publication explicite. Les outils MCP composants ne mutent jamais les fichiers
du dépôt en production et n'exécutent jamais de source stockée en base au
runtime. Utilisez yayaw_components_list(includeSchemas=true) avant de composer
des sections ; le contrat annoncé liste aussi les noms de capacités créatives
inférés par le serveur, les types et champs obligatoires des props de chaque
nœud, les rôles explicites de familles typographiques, les surfaces sémantiques
stables et les alias de compatibilité qui ne peuvent pas attester une intention
sémantique. Il expose aussi les handlers de formulaires CMS exacts enregistrés.
Le seul handler enregistré est
POST /api/cms/forms/personalization-request avec un encodage multipart. Il
exige email (email), firstName (text), consent (checkbox) et
photo (upload natif images) ; birthDate (date) et message
(textarea) sont optionnels. Les noms, types de contrôle et indicateurs
obligatoires doivent correspondre exactement. La photo doit être au format
JPEG, PNG ou WebP et ne pas dépasser 8 Mo ; elle est stockée de manière privée.
La soumission crée une demande de personnalisation destinée au suivi par un
opérateur, pas un checkout, une confirmation de commande ou un paiement. Tout
autre handler ou action doit utiliser email-fallback au lieu d'inventer un
comportement POST ou multipart same-origin.
Les opérations réservées aux admins sont
yayaw_cms_personalization_requests_list,
yayaw_cms_personalization_request_get,
yayaw_cms_personalization_request_status_update,
yayaw_cms_personalization_request_delete et
yayaw_cms_personalization_requests_prune. Get émet une URL de photo same-origin
chiffrée et à courte durée, qui n'expose aucune coordonnée de stockage, est
servie comme pièce jointe no-store et expire avec une marge avant la
rétention. La fenêtre terminale ne retourne aucune URL. Delete supprime
définitivement une demande et sa photo privée, et prune prend en charge le
dry-run avant de retirer les demandes dont la rétention de 180 jours est
expirée. Le worker Page AI exécute la rétention bornée et le nettoyage objet
réessayable indépendamment des jobs IA. Ces cinq opérations
sur des données personnelles exigent control-plane:admin, un acteur plateforme
sans portée organisation et l'autorisation Yayaw globale user:manage. Un
administrateur d'organisation ou une claim de scope admin sans ce droit RBAC ne
peut pas y accéder.
Pour creative-composition, les inputs runtime sont limités à propSchema :
apiSchema et harnessSchema doivent être omis ou vides, fixtures doit être
vide, et leurs réglages API, harness ou fixtures associés ne peuvent pas
transporter du contenu runtime caché.
Chaque prototype candidat enregistré déclare un manifeste
requiredCapabilities non vide. Le dry-run bloque la persistance tant que les
révisions publiées exactes placées à la racine des compositions mappées ne
reproduisent pas les manifestes régionaux exacts de structure, d'implémentation
et de rendu.
Le manifeste calculé d'un candidat doit inclure à la fois
display_typography et responsive_recomposition, au moins une signature
structurelle parmi layered_composition, sticky_narrative,
custom_grid_ratios, continuous_canvas et compact_band, ainsi qu'au moins
un traitement de direction artistique parmi image_art_direction,
paper_collage, watercolor_wash, media_texture et decorative_shapes.
text.style: "note" seul n'infère pas paper_collage. Au moins une région
signature doit inférer ensemble la typographie display, la recomposition
responsive, une signature structurelle et un traitement de direction
artistique depuis sa recette exacte.
Une région new_recipe exige une révision créée dans le même contexte dont la
topologie compositionHash n'est pas déjà présente sur une creative-composition
publiée dans l'inventaire préparé, tandis
que reuse_published exige son composant publié exact. Un composant imbriqué,
un arbre différent aux mêmes capacités grossières ou des bindings d'instance
modifiés échouent ; l'auto-déclaration ne compte pas.
Le contrat annoncé inclut canvas.height avec content et band ainsi qu'un
mobileGap borné ; image.mobileAspect, image.desktopAspect et
image.height pour le responsive ; decoration.treatment: "wash" ;
frame.material pour le papier, frame.rotation et placement.rotation
bornées, text.style: "note" ; text.weight ; visibility.show ; le nœud
sémantique disclosure ; section.overflow et frame.overflow explicites ;
ainsi que des valeurs bornées pour image.blend, image.opacity et
l'appearance des contrôles. Un overflow visible sur section/frame n'infère
continuous_canvas que si un placement descendant, une frame tournée ou
scotchée, ou une grande décoration/wash peut réellement traverser la frontière.
Un blend d'image non standard (multiply, screen ou
soft-light) ou une opacité non solide (soft ou faint) infère
media_texture. Actions et submits acceptent bare, outline, paper,
solid ou underline ; les fields acceptent bare, line, outline ou
paper ; les uploads acceptent bare, dashed ou paper. Ces primitives
infèrent selon leur usage
compact_band, responsive_media, watercolor_wash, paper_collage,
typographic_weight, responsive_recomposition et
responsive_navigation. Le client construit un ledger de faisabilité pour
chaque région du prototype depuis ces schémas exacts. Chaque région déclare des
nœuds allowlistés exacts avec chemins canoniques (tree, puis
tree.children.N récursivement), props bornées, recipePropSchema déclaratif
exact, liste recipePropNames possédant les mêmes clés et
recipePropBindings exacts. Les props requises sans défaut exigent un binding,
les clés de binding appartiennent au schéma et chaque image.src doit être
exactement $props.<identifier>, avec cette prop liée comme media_asset ou
localized_media_asset à un média durable de l'inventaire préparé.
mediaAssetIds égale l'ensemble exact et dédupliqué des IDs utilisés par tous
les bindings média. Yayaw reconstruit la recette complète et la valide par le
contrat runtime canonique. Il calcule feasibility.compositionHash depuis les
noms de nœuds, chemins canoniques et props structurelles uniquement, après
exclusion du contenu, des médias, actions, choix de palette surface/tone et
autres props non structurelles, avec normalisation des références runtime. Il
calcule séparément feasibility.implementationHash depuis les
noms/schéma/arbre exacts et feasibility.renderManifestHash depuis cette
implémentation et ses bindings d'instance exacts ; le client ne fournit aucun
de ces hashes.
compact_band exige height: "band",
responsive_media exige des ratios mobile et desktop non-auto distincts sans
hauteur fixe, et responsive_navigation exige un disclosure mobile avec une
branche desktop non vide. Les props d'ordre responsive (desktopOrder ou
mobileOrder) exigent un parent direct grid ou stack, et les props de span
responsive (desktopSpan ou mobileSpan) un parent direct grid ; un ancêtre
compatible ne suffit pas. La grille, les stacks directionnels, les placements
et le sticky partagent le breakpoint de recomposition md. mobileSpan: "2"
est refusé car la grille mobile possède une seule colonne explicite.
Le renderer créatif canonique transmet sans modification les props liées
exactes, y compris les noms historiques de fixtures catalogue comme value,
page, checked, disabled et pressed. Quand un aperçu prototype ou draft
ne dispose pas du contexte de formulaire live, il conserve exactement le
layout et les contrôles du formulaire enregistré dans un wrapper inerte et
impossible à soumettre. Une couverture globale ne rend pas réalisable une
région bloquée.
Chaque texte, lien, label, placeholder, texte alternatif ou message d'upload
visible dans un prototype doit être un sink direct $props.<identifier>. Les
bindings de texte sont des littéraux localisés exacts pour toutes les locales
préparées ; les liens utilisent des cibles littérales ou localisées sûres et
bornées. Les bindings global-data critiques et repeat/$item/$index sont
refusés : chaque élément répété visible est développé en nœuds planifiés
explicites.
Le design de page CMS est un workflow contextuel orchestré par le client.
Lisez et approuvez d'abord toute modification large de design tokens, puis
commencez par yayaw_cms_design_prepare et répondez aux questions utiles par
l'élicitation MCP ou yayaw_cms_design_resume. Son visualIntent durable
porte le niveau d'ambition : une direction nouvelle ou évoluée dérive une
stratégie créative et ne peut pas être abaissée pour éviter les trois
prototypes. Le modèle client utilise
l'inventaire et charge chaque stage lié à l'acteur requis via
yayaw_cms_design_guidance_get : brief, composition, critique, ainsi que
rework pour un rework.
Si une modification large de tokens est découverte après la préparation,
abandonnez le contexte, enregistrez la couche et préparez à nouveau ; un refresh
ne peut pas déplacer un contexte vers un autre hash de tokens.
Avant l'enregistrement des prototypes, finalisez chaque image de récit ou de
recette via yayaw_media_upload_prepare puis
yayaw_media_upload_complete, ou via yayaw_media_import_url, avec le
designContextId actif. Exécutez ensuite un unique
yayaw_cms_design_inventory_refresh de présélection. Il ne peut qu'ajouter des
médias finalisés de ce contexte, sans modifier les baselines de catalogue ni le
hash des design tokens. Les planches candidates et les screenshots runtime
post-enregistrement sont interrogés directement par ID et ne doivent jamais
déclencher ce refresh.
Pour un workflow créatif, les trois planches composites candidates inspectées
sont uploadées avec le designContextId, puis
yayaw_cms_prototypes_register est appelé avec
kind: "generated_set_v2" et exige exactement trois artefacts image distincts
avec hashes vérifiés côté serveur et des dimensions d'au moins 1200 × 900. Un
prototype utilisateur peut ancrer un candidat mais ne réduit jamais le jeu sous
trois. Chaque planche contient toutes les sections prévues à une fidélité
suffisante. Yayaw calcule aussi une empreinte normalisée de matrice d'arêtes en
niveaux de gris 16 × 16 pour chaque planche complète stockée et rejette les
clones ou les planches sous la distance structurelle minimale. Ce garde-fou
grossier de structure normalisée ne prouve jamais à lui seul une direction ou
une fidélité distincte ; il complète les trois signatures produites par le
serveur, la critique par paires, l'inspection visuelle et une Visual QA
substantielle.
Tous les candidats déclarent les mêmes IDs et libellés de région
stables, dans le même ordre, pour les futures preuves
prototypeRegionRef, des preuves d'originalité distinctes pour la silhouette,
la typographie, le rythme narratif, la métaphore et le dispositif signature,
ainsi que les décisions structurées de chaque stage de guidance requis. Chaque
directive unique est classée prototype_applicable, implementation_only ou
not_applicable; une directive applicable aux prototypes porte un emplacement
observé et validé pour chacun des trois candidats. Les IDs soumis doivent
respecter exactement les classifications et garde-fous imposés par le serveur.
Les traces version 2 exposent un verificationCheckId déterministe et
optionnel possédé par le serveur. Les plans ultérieurs ne soumettent que les
IDs exacts des directives d'implémentation via guidanceVerification ; Yayaw
exécute le checker à son garde-fou fixe et conserve la preuve hashée. Le client
ne peut inventer ni checker, ni chemin de preuve, ni section de revue.
not_applicable n'est accepté que pour une règle autorisée côté serveur, avec
une raison précise et dans la limite bornée du stage. Les IDs soumis doivent
couvrir exactement une fois toutes les directives de chaque stage requis et
chargé ; un ID omis, dupliqué ou inventé bloque l'enregistrement. Yayaw les
conserve avec ses propres hashes du contenu exact de guidance. Un rejet ou une
combinaison demandée exige un jeu de remplacement de trois planches enregistrées.
Un candidat dont une région exige une primitive, un asset, une police, une
interaction, un handler, un comportement responsive ou un fallback indisponible
est bloqué avant présentation et enregistrement ; le client remplace le jeu
complet de trois au lieu de promettre une approximation ultérieure.
Le même appel d'enregistrement contient exactement trois comparaisons
structurées : A/B, A/C et B/C. Chaque paire doit être matériellement distincte
par sa silhouette, sa typographie, son rythme narratif, sa métaphore visuelle,
son dispositif signature et sa résolution mobile/thème, avec des preuves
<artifactId>#<regionId> exactes pour les deux candidats dans chaque région
ordonnée.
Le serveur exige aussi trois signatures de structure, d'implémentation et de
rendu A/B/C distinctes. Les compositionHash régionaux ordonnés forment la
signature structurelle du candidat. Le manifeste normalisé du page shell est
épinglé séparément et doit correspondre exactement au candidat sélectionné
dans le plan et le runtime ; il ne fait pas partie de la signature de
distinction entre candidats. Les implementationHash et renderManifestHash
régionaux ordonnés forment les deux autres. Des changements de contenu, média,
palette, défauts de schéma ou bindings ne transforment pas une topologie
régionale inchangée en direction distincte. Si la stratégie préparée est
design_system_extension, chaque candidat mappe au moins une région visible
vers new_recipe ; la région sélectionnée doit ensuite résoudre la recette
publiée du même contexte à la racine de sa section.
Chaque candidat fournit un pageShell exact avec layout et une couche
designTokens de page bornée ou null. Chaque région fournit ses nœuds,
schéma, bindings, médias et artifactBounds entiers exacts. Une région mesure
au moins 200 × 120 pixels et 2 % de la planche ; leur union couvre au moins
40 % ; le chevauchement de deux régions ne dépasse pas 50 % de la plus petite.
Le serveur fingerprint chaque crop. Hors du scope d'une page globale, les tokens
de page valent obligatoirement null. Chaque paire de candidats doit avoir au
moins un crop de région correspondante matériellement distinct, afin que le
chrome de la planche seul ne puisse pas passer. Le client ne fournit jamais les
compositionHash, implementationHash, renderManifestHash, hashes du
pageShell, visualFingerprint ni runtimeHash. Le serveur les calcule depuis
les artefacts, le snapshot de tokens, les manifestes exacts, les médias
finalisés, les locales, le renderer canonique et l'empreinte de déploiement.
En production, la résolution échoue de façon fermée sans fingerprint immuable
fourni par NEXT_DEPLOYMENT_ID, le gitCommitSha résolu tel que
DEPLOYMENT_GIT_COMMIT_SHA, ou le deploymentId résolu ; "local" est un
fallback réservé au hors production. Le fingerprint de déploiement et la
version du contrat renderer entrent dans runtimeHash : un changement de build
renderer invalide donc la revue antérieure. La frontière exacte courante est le
contrat renderer page 7 et le contrat runtime prototype 4.
L'enregistrement renvoie des URLs runtime canoniques signées, un runtimeHash
et le contrat d'upload de preuves pour chaque candidat. Renouvelez une URL
expirée avec yayaw_cms_prototype_runtime_previews_get seulement tant que le
runtime stocké reste courant. Capturez chaque locale préparée en
desktop/mobile et clair/sombre. Chaque image pleine page distincte porte
exactement les designContextId, designPlanHash égal au runtimeHash, et
visualRole renvoyés. Le desktop fait au moins 1024 pixels de large, le mobile
240 à 767. Chaque capture finalisée porte aussi l'attestation de capture
version 1 de son scénario. Elle lie l'artefact et son hash exacts, le hash de
runtime, l'ID du scénario, la hauteur de document observée, la fin du document
et les IDs de région ordonnés. Le ledger est une partition verticale contiguë
et unique de 0 à la hauteur image/document, avec des bandes d'au moins
24 pixels, sans trou ni chevauchement. La hauteur de l'image égale la hauteur
attestée et reste sous le maximum renvoyé. IDs et hashes de fichier sont uniques.
Soumettez la matrice complète à
yayaw_cms_prototype_runtime_review_complete avec
boardMatchesRuntime: true, les huit fidélités à true et aucun blocage ni
écart. reviewerRole: "visual_qa" est un rôle logique lié à l'acteur courant,
pas la preuve d'un reviewer authentifié séparément. La planche et le runtime
restent des artefacts distincts comparés visuellement : hashes, manifestes,
provenance et bindings de capture détectent substitution, troncature, rejeu et
dérive. Le profil courant cms-server-visual-comparison-v3 utilise une
normalisation contenue 192 × 192. Ses distances maximales sont 0.06 au global,
0.12 pour l'aspect, 0.14 pour les contours, 0.12 pour la luminance,
0.12 pour la palette et 0.08 pour la topologie ; retirer un wash ou une
texture signature devient donc bloquant. La frontière explicite
client_observed_server_validated ne prétend pas
prouver cryptographiquement l'exécution du navigateur.
Seule une revue runtime courante débloque
yayaw_cms_prototype_select. Pour exact-v2, le premier appel fournit le
contexte et la raison, sans candidat ni note de décision. L'opération présente
les candidats A/B/C dans l'ordre canonique avec des aperçus runtime signés et
frais. Un client compatible avec l'élicitation MCP enregistre la réponse enum
acceptée dans ce même appel ; un client classique reçoit input_required puis
reprend avec le challengeId borné et le prototypeId choisis. Une
decisionNote écrite par le client est refusée. Le reçu généré par le serveur
lie présentation, challenge, source de réponse, hashes candidat/runtime et
preuve de revue runtime. Le plan et la publication recalculent ce reçu et
refusent toute preuve absente, legacy, issue d'un autre contexte, conflictuelle
ou altérée, tout en autorisant la reprise idempotente de la même réponse
acceptée. Le candidat verrouillé renvoie ensuite le designBasis.
resumable_client est une attestation du client liée à l'acteur, pas une
preuve cryptographique d'un clic humain ;
l'élicitation fournit une preuve d'interaction protocolaire plus forte, mais
reste médiée par le client. Une modification du renderer/déploiement,
d'une recette, d'un
média, d'une capture ou des tokens périme la revue. Un changement de tokens
après l'enregistrement exige un nouveau contexte et trois nouveaux candidats ;
un refresh ne peut pas rebaser le gate. Après sélection, importez, compilez et
publiez les new_recipe retenues avec ce contexte, et finalisez chaque média
supplémentaire que le plan liera, puis exécutez un refresh avec le même hash de
tokens. Il conserve le gate v2, le reçu runtime, la sélection et le
designBasis, tout en ajoutant les révisions et assets liés. Soumettez ensuite
un plan strict version: 2 avec page.layout et ce designBasis à
yayaw_cms_page_design ou yayaw_cms_page_rework. Le layout et la couche de
tokens de page du plan reproduisent exactement le pageShell du candidat
sélectionné.
Le résultat de préparation expose expiresAt. Pendant une longue pause de
prototype, import, correction ou revue, appelez
yayaw_cms_design_context_renew avant cette échéance. Le renouvellement
préserve le contexte complet et reste plafonné à deux heures depuis la création
initiale de la session ; un contexte expiré ne peut pas être réactivé. Les
imports de composants liés au design portent le designContextId actif et ne
sont acceptés qu'après la sélection explicite du prototype. Cette provenance
est stockée sur la révision compilée immuable, jamais prouvée par les
métadonnées mutables du registre. Le garde-fou d'extension rejette un composant
déjà présent dans la baseline initiale, créé par un autre contexte ou référencé
par une autre révision publiée.
Pendant un déploiement progressif, les serveurs compatibles en lecture
comprennent les contextes legacy prototypeSet.kind: "generated_set" et les
contextes prototypeGateV2.kind: "generated_set_v2", et échouent de façon
fermée si les deux champs coexistent. Le champ séparé distingue les formats,
mais ne sécurise pas à lui seul une flotte mixte : une instance antérieure au
reader compatible peut encore opérer sur l'état legacy, l'écrire ou le
sélectionner. La production utilise deux switches d'écriture explicites.
Déployez le build reader avec CMS_LEGACY_PROTOTYPE_WRITES_ENABLED=true et
CMS_PROTOTYPE_RUNTIME_V2_WRITES_ENABLED=false, drainez toutes les instances
antérieures et faites de ce build le rollback floor. Configurez et vérifiez le
staging privé, puis passez les deux switches à false, redémarrez et drainez
les writers legacy. Activez ensuite uniquement la v2 : legacy false, v2
true. Les deux à true échouent de façon fermée. Pour une évolution du
renderer en development, utilisez d'abord le workflow runtime manuel pour geler
le SHA courant, puis relancez le déploiement de la PR cible. Son resolver issu
du main de confiance ne préserve freeze que si la configuration Coolify et
chaque app/worker live le prouvent déjà ; il déploie le nouveau SHA immuable de
PR sous le lock partagé et prouve ce SHA avec freeze avant que l'exécution
exacte manuelle ne consomme le reçu suivant. Il ne peut initier ni freeze ni
exact. Pour une évolution production, gelez d'abord tous les conteneurs live,
puis déclenchez le workflow rolling avec le SHA complet cible appartenant à
l'historique de main et la confirmation
freeze-upgrade:<sha> ; il refuse de construire tant que la flotte n'est pas
live-frozen, exécute migrations et deux rollouts sous ce freeze, puis prouve les
nouvelles images et le SHA. Lancez enfin le workflow exact manuel sur le même
SHA pour créer et consommer le reçu suivant. La consommation fait avancer
l'opération de rollout courante tandis que la première opération du floor reste
immuable. Les contextes exacts existants se mettent à jour normalement ; seul
l'ajout d'un nouveau gate exact exige l'opération courante transactionnelle.
Une fois le floor actif, ne rollbackez jamais en dessous et ne réactivez jamais
les écritures legacy. Les contextes legacy existants restent lisibles et
annulables uniquement. Les assets liés à un grant d'upload direct legacy
doivent être réuploadés via le staging privé avant de servir de prototype exact
ou de preuve de revue. Un writer désactivé renvoie
cms_design_prototype_runtime_rollout_pending. En local et dans les
autres environnements hors production, les écritures v2 sont activées par
défaut lorsque la variable est absente.
La garantie de preuve exacte s'arrête au moment de la publication. Une
évolution comportementale ultérieure du renderer ne recapture pas
automatiquement les pages déjà publiées. Tant qu'un gate de promotion ne sait
pas inventorier les contrats publiés et fournir un rendu versionné
rétrocompatible ou une migration avec nouvelle revue visuelle, ces évolutions
du renderer bloquent la release. Les versions partagées vivent dans
cms-renderer-contract.ts ; les incrémenter trace la frontière, sans remplacer
la sécurité de promotion manquante.
Utilisez dryRun=true avant la persistance, puis
yayaw_cms_page_review pour obtenir des diagnostics statiques et des aperçus
desktop/mobile immuables à durée courte. Un pass Visual QA doit rendre un
PASS explicite pour la révision immuable et le runtime exacts avant tout upload
de preuve navigateur durable. Le premier brouillon immuable et chaque correction
immuable exigent un nouveau pass. Un reviewer séparé est préférable quand il est
disponible, sans que le rôle persistant prouve une identité distincte. La preuve
prototype soumise à yayaw_cms_page_review_complete répète l'identité exacte de
l'artefact, lie
chaque section à un prototypeRegionRef, vérifie les huit dimensions de
fidélité et garde unapprovedDeviations vide.
Le dry-run et la revue revalident chacun le kind image, le même contexte, le
hash enregistré et les dimensions minimales du prototype sélectionné. Les
sections du plan se lient une à une, dans l'ordre, aux régions du prototype sous
la forme <prototypeId>#<regionId> ; une région échangée, inventée ou omise
échoue.
Chaque evidenceRef de scénario
ou de réduction de mouvement est l'ID d'une image durable liée au contexte, au
hash de plan et au rôle visual-review:* exacts. Le préfixe de rôle renvoyé par
la revue lie aussi la capture à la révision de page et au hash runtime courants.
Chaque capture porte aussi l'attestation version 1 renvoyée avec l'artefact et
son hash, le runtime, le scénario, la hauteur de document et les bandes de
sections contiguës exacts. Le serveur vérifie pour chacune ses octets statiques,
son kind image, son contexte, son hash de plan, son hash de fichier, ses
dimensions, son rôle et son attestation, puis lie ces champs au ledger du reçu.
Ces outils ne créent que des brouillons ; la publication retélécharge et
réinspecte ce ledger ainsi que toutes les planches/captures runtime prototype,
et refuse toute capture supprimée, modifiée, animée, tronquée, rejouée,
reclassifiée ou provenant d'un autre contexte comme revue périmée, puis exige
une demande explicite.
Les plans corrigés restent liés à la première page contextuelle, à son
chemin/slug et aux IDs/slugs de sections stables. Les révisions de page et de
sections, le ledger de session et les hashes de plan média sont persistés dans
une transaction sérialisable unique : tout échec annule l'ensemble. Les
registres portent un pointeur durable vers leur révision de tête, avancé par la
base à l'insertion d'une révision immuable plutôt que déduit du tri des dates.
Une révision manuelle ou externe bloque ce chemin. Une correction A → B → A
qui réutiliserait une ancienne révision immuable renvoie un conflit explicite au
lieu de présenter l'ancienne révision comme nouvelle tête.
Les créations et sauvegardes directes de page exigent control-plane:admin et
CMS_LOW_LEVEL_PAGE_ADMIN_BYPASS_ENABLED=true. L'ancien outil de
design est aussi réservé aux admins et désactivé sauf si
CMS_LEGACY_PAGE_DESIGN_ADMIN_BYPASS_ENABLED=true.
La boucle de blocage ne saute aucune révision : dry-run et persistance,
obtention de review_ready, puis Visual QA sur cette
révision/runtime exacts. Un diagnostic statique/runtime, un scénario ou champ de
fidélité en échec, un problème bloquant ou un écart non approuvé interdit
l'upload de preuves visual-review:*, la complétion de revue et la publication.
Le même plan contextuel est corrigé, une nouvelle révision immuable est
persistée et la boucle reprend au dry-run. Seul un PASS sans
problème bloquant autorise l'upload des preuves. Toute correction ou
modification de dépendance invalide le pass, les captures, le hash runtime et le
reçu.
Les valeurs littérales CMS utilisent du JSON borné récursivement. Tableaux,
objets, primitives et null peuvent être envoyés directement dans les props et
bindings ; les valueShape récursifs valident les valeurs imbriquées. Les
anciennes chaînes JSON restent lisibles.
yayaw_kibo_sections_generate crée ou met à jour des recettes CMS natives
kibo-ui-block pour le catalogue Kibo UI suivi. Il ne stocke jamais du TSX tiers
arbitraire comme contenu exécutable au runtime: chaque section générée est une
section recipe Yayaw rendue par des exemples Kibo présents dans le dépôt et
leurs dépendances Kibo/shadcn vendored. Les recettes générées exposent des props
éditables texte, lien, média et listes JSON afin que les pages lient du contenu
Yayaw localisé via PageSection.inputBindings pendant que les défauts Kibo
restent le repli de prévisualisation. Utilisez dryRun=true pour inspecter les
slugs cibles et publish=true seulement lorsque l'appelant veut explicitement
publier les révisions validées.
Les outils de publication/archive de page, publication d'entrée de données et
publication de section exigent expectedRevisionId pour préserver le
verrouillage optimiste. L'archive de section accepte expectedRevisionId lorsque
l'appelant connaît la dernière révision. Les outils d'archive destructeurs, y
compris page, section, composant et domaine public d'organisation, exigent
confirm: true.
Chaque appel d'outil MCP tente d'écrire control_plane_audit_events avec
acteur, clé API, opération, cible, environnement, transport, état dry-run,
raison, statut, résumé d'erreur et métadonnées de résultat. Une insertion
d'audit réussie renvoie auditId dans la réponse de l'outil; si le stockage
d'audit est indisponible, le serveur journalise l'échec et renvoie la réponse
sans auditId.
Contrat Des Futures Fonctionnalités
Les nouvelles fonctionnalités opérationnelles doivent être conçues comme opérables par le plan de contrôle sauf exclusion volontaire UI-only.
Lorsqu'une fonctionnalité nécessite des workflows de contenu, configuration, publication, statut ou audit, implémentez-la avec le pattern partagé du plan de contrôle:
- Placez la logique métier dans un point d'entrée service réutilisable qui accepte un argument acteur/contexte explicite.
- Gardez les lectures de cookie-session dans les adaptateurs UI, pas dans la couche service réutilisable.
- Faites appeler la même logique permissionnée par les server actions UI, outils MCP, scripts CLI et API HTTP optionnelles.
- Enregistrez des outils/ressources MCP typés dans
src/lib/server/services/control-planelorsque les clients MCP doivent opérer ou inspecter la fonctionnalité. - Exigez
reasonpour les écritures,dryRunlorsque c'est pratique,expectedRevisionIdpour les flux publish optimistes etconfirm: truepour les actions destructrices. - Gatez l'accès par clé API avec les permissions Better Auth
control-plane:*et exécutez quand même l'autorisation Yayaw aveccan(...)ou des checks de membership borné. - Tentez l'enregistrement d'événements d'audit pour toutes les opérations et
exposez
auditIdlorsque le stockage réussit. - Mettez à jour cette documentation et
content/llm/llm-source.md, puis régénérez les fichiers assistants avecbun run docs:llm:generate. - Ajoutez des tests ciblés pour schémas, mapping de permissions, gardes dry-run/write, insertion d'audit et couverture smoke discovery MCP ou appel d'outil.
Si une fonctionnalité est volontairement exclue de MCP/CLI, documentez la raison dans les docs de fonctionnalité ou le résumé de PR.
Validation
Checks utiles après des changements du plan de contrôle:
bun test src/lib/server/services/control-plane/control-plane.test.ts
bun test src/lib/server/services/control-plane/oauth-metadata.test.ts
bun run mcp:stdio