From 34c2cfc2daaa779a3c17e5c6a56fcbb0839d4a44 Mon Sep 17 00:00:00 2001 From: "Eduardo A." Date: Sat, 26 Sep 2026 17:03:34 -0300 Subject: [PATCH] docs(website): keep maintainer provenance off the developer site and pair every contributor doc JUM-895. The website sync now strips provenance-only parentheticals and Linear link definitions (stripMaintainerProvenance); the six Service Management E1-E8 maintainer design records leave the published tree and their old URLs resolve to the Service Manager guide; the remaining inline citations in the guide, designer-core and dead-letter-queue READMEs, Domain Designer features and runtime contracts moved into parentheses or were reworded. The designer-core README no longer claims npm publishing is manual, and the Portuguese runtime contracts page no longer lists four editable keys where nine exist. Six English-only evidence records under documentation/md gain Portuguese twins, and check-current-governance-docs now fails on a missing language twin. The 40 JUM-895 allow-list entries are gone. Co-Authored-By: Claude Opus 5.5 --- .../app/docs/[[...mdxPath]]/page.tsx | 23 + .../config/content-sources.json | 60 -- .../jumentix/concepts/architecture.mdx | 3 - .../jumentix/guides/service-management.mdx | 42 +- .../content/jumentix/guides/spa-pwa.mdx | 4 +- .../content/jumentix/packages/_meta.ts | 1 + .../jumentix/packages/dead-letter-queue.mdx | 413 ++++++++++++++ .../jumentix/packages/designer-core/index.mdx | 26 +- .../content/jumentix/reference/_meta.ts | 6 - .../reference/domain-designer-features.mdx | 31 +- .../reference/frontend-offline-data-layer.mdx | 14 +- .../content/jumentix/reference/index.mdx | 6 - .../jumentix/reference/package-scripts.mdx | 81 +-- .../service-management-cana-adoption.mdx | 453 --------------- ...ice-management-collaboration-packaging.mdx | 490 ----------------- .../service-management-contract-parity.mdx | 378 ------------- .../service-management-design-system-pwa.mdx | 372 ------------- ...service-management-module-architecture.mdx | 467 ---------------- .../service-management-operations-console.mdx | 415 -------------- ...service-management-runtime-environment.mdx | 23 +- .../pt-BR/jumentix/concepts/architecture.mdx | 3 - .../jumentix/guides/service-management.mdx | 30 +- .../content/pt-BR/jumentix/guides/spa-pwa.mdx | 4 +- .../content/pt-BR/jumentix/packages/_meta.ts | 1 + .../jumentix/packages/dead-letter-queue.mdx | 414 ++++++++++++++ .../jumentix/packages/designer-core/index.mdx | 27 +- .../content/pt-BR/jumentix/reference/_meta.ts | 6 - .../reference/domain-designer-features.mdx | 27 +- .../reference/frontend-offline-data-layer.mdx | 14 +- .../pt-BR/jumentix/reference/index.mdx | 6 - .../jumentix/reference/package-scripts.mdx | 77 ++- .../service-management-cana-adoption.mdx | 476 ---------------- ...ice-management-collaboration-packaging.mdx | 516 ------------------ .../service-management-contract-parity.mdx | 392 ------------- .../service-management-design-system-pwa.mdx | 392 ------------- ...service-management-module-architecture.mdx | 490 ----------------- .../service-management-operations-console.mdx | 415 -------------- ...service-management-runtime-environment.mdx | 31 +- .../documentation/DOCUMENTATION-EXPERIENCE.md | 17 + .../DOCUMENTATION-EXPERIENCE.pt-BR.md | 17 + apps/jumentix-website/public/docs-index.json | 413 +++----------- apps/jumentix-website/public/llms-full.txt | 211 ++----- apps/jumentix-website/public/llms.txt | 7 +- .../scripts/content-leaks.mjs | 36 ++ .../scripts/content-leaks.test.mjs | 50 ++ .../scripts/sync-markdown-content.mjs | 4 +- ci-cd/check-current-governance-docs.js | 23 +- ci-cd/documentation-audience-allowlist.json | 240 -------- .../check-current-governance-docs.test.ts | 37 ++ .../md/BUN-BRANCH-COVERAGE-SPIKE.pt-BR.md | 39 ++ .../BUN-INSTALL-COMPATIBILITY-AUDIT.pt-BR.md | 206 +++++++ .../BUN-TAXONOMY-BASELINE-VALIDATION.pt-BR.md | 29 + documentation/md/CONTRIBUTING-AND-TOOLING.md | 5 + .../md/CONTRIBUTING-AND-TOOLING.pt-BR.md | 5 + .../md/DOMAIN-DESIGNER-FEATURES-AND-USAGE.md | 6 +- ...OMAIN-DESIGNER-FEATURES-AND-USAGE.pt-BR.md | 2 +- .../md/EPIC-RELEASE-TAG-DRIVEN-CLOSURE.md | 16 +- .../EPIC-RELEASE-TAG-DRIVEN-CLOSURE.pt-BR.md | 41 ++ .../md/PLATFORM-DEPENDENT-SUITES.pt-BR.md | 32 ++ .../md/RUNTIME-ENVIRONMENT-CONTRACTS.md | 8 +- .../md/RUNTIME-ENVIRONMENT-CONTRACTS.pt-BR.md | 16 +- .../md/TEST-PYRAMID-ROLLOUT-REPORT.pt-BR.md | 43 ++ ...ING-SERVICE-MANAGER-AND-DOMAIN-DESIGNER.md | 6 +- ...RVICE-MANAGER-AND-DOMAIN-DESIGNER.pt-BR.md | 4 +- packages/dead-letter-queue/README.md | 6 +- packages/dead-letter-queue/README.pt-BR.md | 6 +- packages/designer-core/README.md | 23 +- packages/designer-core/README.pt-BR.md | 24 +- test-map.json | 26 +- 69 files changed, 1882 insertions(+), 6345 deletions(-) create mode 100644 apps/jumentix-website/content/jumentix/packages/dead-letter-queue.mdx delete mode 100644 apps/jumentix-website/content/jumentix/reference/service-management-cana-adoption.mdx delete mode 100644 apps/jumentix-website/content/jumentix/reference/service-management-collaboration-packaging.mdx delete mode 100644 apps/jumentix-website/content/jumentix/reference/service-management-contract-parity.mdx delete mode 100644 apps/jumentix-website/content/jumentix/reference/service-management-design-system-pwa.mdx delete mode 100644 apps/jumentix-website/content/jumentix/reference/service-management-module-architecture.mdx delete mode 100644 apps/jumentix-website/content/jumentix/reference/service-management-operations-console.mdx create mode 100644 apps/jumentix-website/content/pt-BR/jumentix/packages/dead-letter-queue.mdx delete mode 100644 apps/jumentix-website/content/pt-BR/jumentix/reference/service-management-cana-adoption.mdx delete mode 100644 apps/jumentix-website/content/pt-BR/jumentix/reference/service-management-collaboration-packaging.mdx delete mode 100644 apps/jumentix-website/content/pt-BR/jumentix/reference/service-management-contract-parity.mdx delete mode 100644 apps/jumentix-website/content/pt-BR/jumentix/reference/service-management-design-system-pwa.mdx delete mode 100644 apps/jumentix-website/content/pt-BR/jumentix/reference/service-management-module-architecture.mdx delete mode 100644 apps/jumentix-website/content/pt-BR/jumentix/reference/service-management-operations-console.mdx create mode 100644 apps/jumentix-website/scripts/content-leaks.test.mjs create mode 100644 ci-cd/test/check-current-governance-docs.test.ts create mode 100644 documentation/md/BUN-BRANCH-COVERAGE-SPIKE.pt-BR.md create mode 100644 documentation/md/BUN-INSTALL-COMPATIBILITY-AUDIT.pt-BR.md create mode 100644 documentation/md/BUN-TAXONOMY-BASELINE-VALIDATION.pt-BR.md create mode 100644 documentation/md/EPIC-RELEASE-TAG-DRIVEN-CLOSURE.pt-BR.md create mode 100644 documentation/md/PLATFORM-DEPENDENT-SUITES.pt-BR.md create mode 100644 documentation/md/TEST-PYRAMID-ROLLOUT-REPORT.pt-BR.md diff --git a/apps/jumentix-website/app/docs/[[...mdxPath]]/page.tsx b/apps/jumentix-website/app/docs/[[...mdxPath]]/page.tsx index b9685e5ab..cc68e8f6f 100644 --- a/apps/jumentix-website/app/docs/[[...mdxPath]]/page.tsx +++ b/apps/jumentix-website/app/docs/[[...mdxPath]]/page.tsx @@ -20,8 +20,31 @@ const legacyAliases: Record = { 'runtime-contracts': ['reference', 'runtime-contracts'], }; +/** + * Maintainer design records that were published under /docs/jumentix/reference + * until JUM-895. They stay in documentation/md for contributors; their old URLs + * resolve to the Service Manager guide, the developer-facing replacement + * (Requirement 093 rule 7). + */ +const retiredReferencePages = new Set([ + 'service-management-module-architecture', + 'service-management-contract-parity', + 'service-management-operations-console', + 'service-management-cana-adoption', + 'service-management-design-system-pwa', + 'service-management-collaboration-packaging' +]); + +function retiredReferenceTarget(segments: string[]): string[] | undefined { + const [reference, slug] = segments.slice(-2); + if (reference !== 'reference' || !retiredReferencePages.has(slug)) return undefined; + return [...segments.slice(0, -2), 'guides', 'service-management']; +} + function getCandidates(mdxPath: MdxPath): string[][] { const normalized = Array.isArray(mdxPath) ? mdxPath.filter(Boolean) : []; + const retiredTarget = retiredReferenceTarget(normalized); + if (retiredTarget) return getCandidates(retiredTarget); if (normalized.length === 0) { return [['jumentix']]; diff --git a/apps/jumentix-website/config/content-sources.json b/apps/jumentix-website/config/content-sources.json index 8d665e883..99b2d8185 100644 --- a/apps/jumentix-website/config/content-sources.json +++ b/apps/jumentix-website/config/content-sources.json @@ -530,66 +530,6 @@ "source": "../../documentation/md/DOMAIN-DESIGNER-FEATURES-AND-USAGE.md", "sourcePtBr": "../../documentation/md/DOMAIN-DESIGNER-FEATURES-AND-USAGE.pt-BR.md" }, - { - "section": "reference", - "slug": "service-management-module-architecture", - "title": "Service Management Module Architecture", - "titlePtBr": "Arquitetura de Módulos do Service Management", - "description": "Module boundaries and the IDesignerStore port contract behind the designer.", - "descriptionPtBr": "Fronteiras de módulo e o contrato da porta IDesignerStore por trás do designer.", - "source": "../../documentation/md/SERVICE-MANAGEMENT-MODULE-ARCHITECTURE.md", - "sourcePtBr": "../../documentation/md/SERVICE-MANAGEMENT-MODULE-ARCHITECTURE.pt-BR.md" - }, - { - "section": "reference", - "slug": "service-management-contract-parity", - "title": "Service Management Contract Parity", - "titlePtBr": "Paridade de Contrato do Service Management", - "description": "What each export guarantees: OAS 3.1, AsyncAPI, proto, codegen bundle and round-trip fidelity.", - "descriptionPtBr": "O que cada exportação garante: OAS 3.1, AsyncAPI, proto, bundle de codegen e fidelidade de ida e volta.", - "source": "../../documentation/md/SERVICE-MANAGEMENT-CONTRACT-PARITY.md", - "sourcePtBr": "../../documentation/md/SERVICE-MANAGEMENT-CONTRACT-PARITY.pt-BR.md" - }, - { - "section": "reference", - "slug": "service-management-operations-console", - "title": "Service Management Operations Console", - "titlePtBr": "Console de Operações do Service Management", - "description": "The capabilities matrix, PM2 ecosystem preview and the deploy-target metadata contract.", - "descriptionPtBr": "A matriz de capacidades, o preview do ecossistema PM2 e o contrato de metadados dos alvos de deploy.", - "source": "../../documentation/md/SERVICE-MANAGEMENT-OPERATIONS-CONSOLE.md", - "sourcePtBr": "../../documentation/md/SERVICE-MANAGEMENT-OPERATIONS-CONSOLE.pt-BR.md" - }, - { - "section": "reference", - "slug": "service-management-cana-adoption", - "title": "Service Management Cana Adoption", - "titlePtBr": "Adoção do Cana no Service Management", - "description": "The single-store decision, the one-way migration and the offline state matrix.", - "descriptionPtBr": "A decisão de store único, a migração unidirecional e a matriz de estados offline.", - "source": "../../documentation/md/SERVICE-MANAGEMENT-CANA-ADOPTION.md", - "sourcePtBr": "../../documentation/md/SERVICE-MANAGEMENT-CANA-ADOPTION.pt-BR.md" - }, - { - "section": "reference", - "slug": "service-management-design-system-pwa", - "title": "Service Management Design System and PWA", - "titlePtBr": "Design System e PWA do Service Management", - "description": "Design tokens, the accessibility model, and installing, updating and recovering the app shell.", - "descriptionPtBr": "Tokens de design, o modelo de acessibilidade e como instalar, atualizar e recuperar o app shell.", - "source": "../../documentation/md/SERVICE-MANAGEMENT-DESIGN-SYSTEM-PWA.md", - "sourcePtBr": "../../documentation/md/SERVICE-MANAGEMENT-DESIGN-SYSTEM-PWA.pt-BR.md" - }, - { - "section": "reference", - "slug": "service-management-collaboration-packaging", - "title": "Service Management Collaboration and Packaging", - "titlePtBr": "Colaboração e Empacotamento do Service Management", - "description": "The shared catalog, domain-package versioning and the designer-core packaging boundary.", - "descriptionPtBr": "O catálogo compartilhado, o versionamento de pacotes de domínio e a fronteira de empacotamento do designer-core.", - "source": "../../documentation/md/SERVICE-MANAGEMENT-COLLABORATION-PACKAGING.md", - "sourcePtBr": "../../documentation/md/SERVICE-MANAGEMENT-COLLABORATION-PACKAGING.pt-BR.md" - }, { "section": "reference", "slug": "frontend-offline-data-layer", diff --git a/apps/jumentix-website/content/jumentix/concepts/architecture.mdx b/apps/jumentix-website/content/jumentix/concepts/architecture.mdx index e78c8a415..c8e7170f8 100644 --- a/apps/jumentix-website/content/jumentix/concepts/architecture.mdx +++ b/apps/jumentix-website/content/jumentix/concepts/architecture.mdx @@ -23,9 +23,6 @@ Jumentix delivers a practical architecture baseline for production software: - HTTP/REST adapters for multiple Node.js frameworks - Realtime adapters for WebSocket and gRPC - Function-oriented runtime paths for cloud providers -- GUI inbound slot at `apps/backend-template/src/interface/GUI/` for future web (SPA/PWA/React/Vue/…) and desktop (Electron/GTK/…) clients — structure only today; GUIs drive the core and must not own domain rules - -See the interactive map on the commercial site: [/architecture](/architecture). ## Data and Integration Options diff --git a/apps/jumentix-website/content/jumentix/guides/service-management.mdx b/apps/jumentix-website/content/jumentix/guides/service-management.mdx index e5315bb52..b12062777 100644 --- a/apps/jumentix-website/content/jumentix/guides/service-management.mdx +++ b/apps/jumentix-website/content/jumentix/guides/service-management.mdx @@ -28,10 +28,12 @@ the environment files the backend boots from. flowchart TB subgraph browser["Browser · http://127.0.0.1:3200"] direction LR - DD["Tab 1 · Domain Designer
domains, entities, relationships,
RBAC, contracts, exports"] - CID["Tab 2 · Communication Interface Designer
inbound adapters"] - SC["Tab 3 · Service Configuration
runtime profile, PM2 preview, env keys"] - DM["Tab 4 · Deploy Management
deployment targets"] + ARCH["Tab · Architecture
Core service, domain services, links"] + DD["Tab · Domain Designer
domains, entities, relationships,
RBAC, contracts, exports"] + CID["Tab · Communication Interface Designer
inbound adapters"] + SC["Tab · Service Configuration
runtime profile, PM2 preview, env keys"] + DM["Tab · Deploy Management
deployment targets"] + OAS["Tab · OpenAPI
Swagger UI + service selector"] end CANA[("Cana · IndexedDB
the only designer store")] @@ -234,6 +236,10 @@ A complete first round trip: `domain-designer-oas-3.1.json`. 4. Delete the sample domain and model your own. +## 5.1 Architecture and OpenAPI tabs + +Architecture starts as one Core service (Users + every other domain). Split with Add Service, assign domains, draw links. OpenAPI renders the generated OAS in Swagger UI; pick a service to filter operations and Try it out against that address. + ## 6. Domain Designer ### 6.1 Canvas and navigation @@ -357,7 +363,7 @@ schema refs, add external `$ref` targets and set a discriminator property, then Where these land in the exported documents — and the exact fidelity guarantees of each crossing — is owned by -[Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity). +Contract Parity Guarantees. ### 6.6 Validation, the export gate and schema diff @@ -414,7 +420,7 @@ first. Import targets are `Import JSON`, `Import OAS 3.1` and `Import Package`; each failure reason maps to one explicit status message. Domain-package versioning, dependency graphs and conflict policy are owned by -[Collaboration and Packaging](/docs/jumentix/reference/service-management-collaboration-packaging). +Collaboration and Packaging. ## 7. Communication Interface Designer @@ -452,7 +458,7 @@ re-pick them each time. `Save Profile` validates before it writes: ports outside range, ports colliding across the protocols the selected service kind actually binds, and run-mode × -provider combinations with no deploy target in the Requirement `059` matrix are +provider combinations with no deploy target in the deploy matrix are refused with the reason on the status surface. ### 8.2 PM2 ecosystem preview @@ -467,7 +473,7 @@ or a silently empty list. ### 8.3 Runtime environment variables The editor exposes runtime keys in two tiers. The write allowlist is a security -decision, pinned by Requirement `126`: +decision: | Tier | Keys | | --- | --- | @@ -550,7 +556,7 @@ runtime version. PM2-managed targets also take a PM2 profile. ![Deploy Management with an EC2 target and a Cloudflare Workers target registered](/docs-assets/documentation/images/service-manager/08-deploy-management.png "Deploy Management") -The form validates against the Requirement `059` deploy matrix and explains +The form validates against the deploy matrix and explains every refusal: | Target type | Service types it can run | PM2 profile | Region means | @@ -563,7 +569,7 @@ a bare runtime name is refused. Targets can be edited in place and duplicated; `Cancel` leaves an edit without applying it. The matrix, the metadata contract and the lifecycle rules are owned by -[Operations Console](/docs/jumentix/reference/service-management-operations-console). +Operations Console. ## 10. Where your work is stored @@ -598,9 +604,9 @@ Export a JSON model before any risky change: that file is the only portable backup. Storage states, the one-way migration and the offline matrix are owned by -[Cana Adoption, Migration and Offline Behaviour](/docs/jumentix/reference/service-management-cana-adoption). +Cana Adoption, Migration and Offline Behaviour. The install, update and recovery flows are owned by -[Design System and PWA Shell](/docs/jumentix/reference/service-management-design-system-pwa). +Design System and PWA Shell. ## 11. From the model to a running service @@ -715,11 +721,11 @@ adapters, the deploy-target lifecycle and catalog sync. - [Runtime Environment Contracts](/docs/jumentix/reference/service-management-runtime-environment) - [Domain Designer Features and Usage](/docs/jumentix/reference/domain-designer-features) -- [Module Architecture and the IDesignerStore Port](/docs/jumentix/reference/service-management-module-architecture) -- [Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity) -- [Operations Console](/docs/jumentix/reference/service-management-operations-console) -- [Cana Adoption, Migration and Offline Behaviour](/docs/jumentix/reference/service-management-cana-adoption) -- [Design System and PWA Shell](/docs/jumentix/reference/service-management-design-system-pwa) -- [Collaboration and Packaging](/docs/jumentix/reference/service-management-collaboration-packaging) +- Module Architecture and the IDesignerStore Port +- Contract Parity Guarantees +- Operations Console +- Cana Adoption, Migration and Offline Behaviour +- Design System and PWA Shell +- Collaboration and Packaging - [Creating SPA/PWA with Jumentix](/docs/jumentix/guides/spa-pwa) - [Creating a REST API with Jumentix](/docs/jumentix/guides/rest-api) diff --git a/apps/jumentix-website/content/jumentix/guides/spa-pwa.mdx b/apps/jumentix-website/content/jumentix/guides/spa-pwa.mdx index 8a7e780c5..95ee76e80 100644 --- a/apps/jumentix-website/content/jumentix/guides/spa-pwa.mdx +++ b/apps/jumentix-website/content/jumentix/guides/spa-pwa.mdx @@ -76,7 +76,9 @@ npx @jumentix/cli-init init my-spa \ Use `--mode=frontend` when you only need the client. The generator writes `apps/frontend` with merged OAS, one module per domain, and optional Cana via -`--offline`. See [Getting started](/docs/jumentix/concepts/getting-started). +`--offline`. See [Getting started](/docs/jumentix/concepts/getting-started) and +repository docs `BOOTSTRAP-CLI-SCAFFOLDING.md` / +`JUMENTIX-SERVICE-FACTORY-CAPABILITIES-MATRIX.md`. **Success check:** `my-spa/.jumentix/project.json` exists and `apps/frontend` is present. diff --git a/apps/jumentix-website/content/jumentix/packages/_meta.ts b/apps/jumentix-website/content/jumentix/packages/_meta.ts index dc45e43f8..36980aa1c 100644 --- a/apps/jumentix-website/content/jumentix/packages/_meta.ts +++ b/apps/jumentix-website/content/jumentix/packages/_meta.ts @@ -13,6 +13,7 @@ export default { 'runtime-infra': '@jumentix/runtime-infra', 'cana-react': '@jumentix/cana-react', 'cana-vue': '@jumentix/cana-vue', + 'dead-letter-queue': '@jumentix/dead-letter-queue', cana: '@jumentix/cana', 'designer-core': '@jumentix/designer-core', 'key-value-storage': '@jumentix/key-value-storage', diff --git a/apps/jumentix-website/content/jumentix/packages/dead-letter-queue.mdx b/apps/jumentix-website/content/jumentix/packages/dead-letter-queue.mdx new file mode 100644 index 000000000..be7613253 --- /dev/null +++ b/apps/jumentix-website/content/jumentix/packages/dead-letter-queue.mdx @@ -0,0 +1,413 @@ +--- +title: "@jumentix/dead-letter-queue" +description: "Packages documentation." +--- + +# @jumentix/dead-letter-queue + +Private queue for transactions the mutex refused. + +> **A queued write has not happened.** Everything in this package follows from +> that one sentence. The service still throws `ResourceLockedError`; the queue +> only makes the attempt recoverable. + +--- + +## 1. The problem + +`UserService` guards every mutation with the mutex. When the resource is +already locked, it refuses: + +```ts +const { result: { previouslyLocked } } = await this.mutexService.lock(this.entityName, id); +if (previouslyLocked) await this.rejectLocked('update', id, data); +``` + +There are **twelve** such sites. Before this package, the caller got an error +and the intended transaction was discarded. Nothing recorded that it had been +attempted, so a write lost to contention was indistinguishable from a write +nobody ever made. + + + + + + + + + + + + + + + Client + UserService + MutexService + DeadLetterQueue + + + + + + + + + + + update(user-1, payload) + lock(User, user-1) + previouslyLocked = true + ResourceLockedError + enqueue(User, user-1, update, payload) + ResourceLockedError + + + + without the queue — the attempt disappeared + + with the queue — recorded, and still refused + + + +The client is refused in **both** paths. That is the point: the write has not +happened, and a client told otherwise would act on a lie. + +--- + +## 2. The pieces + + + + + + + + + + + + + apps/backend-template + @jumentix/dead-letter-queue + + + + + + + + + + + + UserService12 rejection sites + composeUserDeadLetterReplayhandlers + wiring + DeadLetterQueueenqueue · pending · replay + DeadLetterReplayWorkerinterval drain + InMemoryStore + KeyValueStore + Redis + + + + + + + + + + + rejectLocked() + handlers call back + get · set · del + + + +| Piece | Responsibility | +|---|---| +| `DeadLetterQueue` | Records, statuses, attempt bound, replay ordering | +| `DeadLetterReplayWorker` | Calls `replay` on an interval, without overlapping | +| `InMemoryDeadLetterStore` | Process-local; tests and single-process runtimes | +| `KeyValueDeadLetterStore` | Redis, through the client the mutex already uses | +| `composeUserDeadLetterReplay` | Maps operation names back to `UserService` methods | + +--- + +## 3. The record lifecycle + + + + + + + + + + + + + + + pending + succeeded + abandoned + + + + + + + + + enqueue() + handler returned + bound reached + handler threw, under the bound + + + Terminal records are kept, not deleted: "did that write ever happen" must stay answerable. + + + +| Status | Meaning | Picked by `replay`? | +|---|---|---| +| `pending` | Refused, replayable | yes | +| `succeeded` | A handler performed the write | no | +| `abandoned` | Attempt bound reached | no, ever | + +--- + +## 4. Replay goes through the service + +This is the decision most likely to be got wrong, so it is worth being explicit. + + + + + + + + + + + + + + + + ReplayWorker + handlerorThrow + UserServicere-acquires the mutex + lock clearedstatus = succeeded + still lockedattempts + 1, stays pending + + + + + + + + + Through the service, never the repository: a repository-level replay would write past the lock. + + + +Two things this diagram is defending: + +**Through the service, not the repository.** The service re-acquires the mutex, +so a record whose lock has not cleared is refused again and stays queued. A +repository-level replay would write *past* the lock, which is exactly the +corruption the mutex exists to prevent. + +**`orThrow` is not decoration.** `UserService` reports failure in +`response.error` and does **not** throw. A handler that ignored that would +return normally for a still-locked record, the queue would mark it `succeeded`, +and the write would be lost while the report said it landed. Every handler goes +through `orThrow`. + +--- + +## 5. API + +### `DeadLetterQueue` + +```ts +new DeadLetterQueue({ store?, maxAttempts?, now?, newId? }) +``` + +| Option | Default | Notes | +|---|---|---| +| `store` | `InMemoryDeadLetterStore` | Use `KeyValueDeadLetterStore` in a real runtime | +| `maxAttempts` | `5` | Must be at least 1; a queue that never attempts is refused at construction | +| `now` | `() => new Date()` | Injected for deterministic tests | +| `newId` | timestamp + counter | The counter is why two records in the same millisecond do not collide | + +| Method | Returns | +|---|---| +| `enqueue(input)` | The stored `pending` record | +| `pending()` | Replayable records, in rejection order | +| `find(id)` | One record, whatever its status | +| `replay(handlers)` | `{ replayed, retried, abandoned, skipped }` — ids, not counts | + +`enqueue` refuses input that could not be replayed: `entityName`, `resourceId` +and `operation` are all required. + +### `DeadLetterReplayWorker` + +```ts +new DeadLetterReplayWorker({ queue, handlers, intervalMs?, onReport?, onError? }) +``` + +| Member | Behaviour | +|---|---| +| `start()` | Begins the interval (default `30_000` ms). The timer is `unref`ed | +| `stop()` | Ends it; no tick runs afterwards | +| `tick()` | Forces one drain — on shutdown, or on the unlock of a resource | +| `running` | Whether the interval is active | + +Three properties, each a way a naive timer loop goes wrong: + +- **No overlap.** A drain slower than the interval is not started again while + the previous one runs, or the same record is replayed twice concurrently. +- **A failing drain does not stop the worker.** Redis down is a bad tick, not + the end. A worker that dies on the first error is indistinguishable from one + that was never started. +- **Stopping is complete**, including a tick already scheduled. + +### Stores + +| Store | Use | +|---|---| +| `InMemoryDeadLetterStore` | Tests, single-process runtimes | +| `KeyValueDeadLetterStore(client, { prefix })` | Redis, prefix defaults to `dlq` | + +The Redis client this repository uses exposes `get`, `set` and `del` — **no +`SCAN`, no `KEYS`**. The store therefore keeps an explicit index of record ids +under one key, which is also what preserves replay order. + + + + + + + + + + + + + + + dlq:index + id-1, id-2, id-3 + dlq:record:id-1 + dlq:record:id-2 + dlq:record:id-3 + + + + + + + + 1st2nd3rd + + + +--- + +## 6. Wiring + +Pass the queue as a service. Without it, behaviour is **exactly** as before. + +```ts +import { composeUserDeadLetterQueue, composeUserDeadLetterWorker } + from '@src/modules/Users/composition/composeUserDeadLetterReplay'; + +const deadLetterQueue = composeUserDeadLetterQueue(keyValueStorageClient); +const userService = UserService.compile({ + dataRepository, + services: { mutexService, passwordCryptoService, deadLetterQueue } +}); +const deadLetterWorker = composeUserDeadLetterWorker(deadLetterQueue, userService); +``` + +`composeUsersAuthServices` already does this and returns both. It builds the +worker but does **not** start it: a background timer is the runtime's to start +and, more importantly, to stop. + +```ts +deadLetterWorker?.start(); +process.on('SIGTERM', () => deadLetterWorker?.stop()); +``` + +Without a `keyValueStorageClient` both are `undefined`, on purpose: a +process-local queue would be lost on restart while looking like durability. + +--- + +## 7. Try it + +A runnable script, no Redis required: + +```bash +bun run --filter @jumentix/dead-letter-queue example +``` + +It is `examples/replay.ts` in this package — edit it and re-run. It walks the +whole lifecycle: a refused write, a replay that fails because the lock still +holds, a replay that succeeds, and a record that reaches the attempt bound. + +```ts +import { DeadLetterQueue } from '@jumentix/dead-letter-queue'; + +const queue = new DeadLetterQueue({ maxAttempts: 2 }); +await queue.enqueue({ + entityName: 'User', resourceId: 'user-1', operation: 'update', payload: { firstName: 'Ada' } +}); + +let locked = true; +const handlers = { + update: async () => { if (locked) throw new Error('User user-1 is locked'); } +}; + +await queue.replay(handlers); // retried: [ 'dlq-...' ] +locked = false; +await queue.replay(handlers); // replayed: [ 'dlq-...' ] +``` + +--- + +## 8. Operating it + +| Symptom | What it means | What to do | +|---|---|---| +| `pending()` grows | Locks are not clearing, or the worker is not started | Check `worker.running` and the mutex TTL | +| Records reach `abandoned` | A resource stayed locked for `maxAttempts` drains | Read `lastError`; the write is lost and the record is the evidence | +| `skipped` in a report | An operation has no registered handler | A wiring mistake — the record is kept, not discarded | +| `onError` fires | The drain itself failed, for example Redis down | The worker keeps ticking; fix the store | + +The report returns **ids**, not counts, so an operator can look a record up +rather than infer from a total. + +--- + +## 9. Validation + +```bash +bun run --filter @jumentix/dead-letter-queue build +bun run --filter @jumentix/dead-letter-queue typecheck +bun run --filter @jumentix/dead-letter-queue lint +bun run --filter @jumentix/dead-letter-queue test +bun run smoke:dead-letter:redis # against a real Redis container +``` + +The integration suite **skips itself** without `RUN_REDIS_INTEGRATION=1` rather +than passing. A suite that reports success without its dependency is the false +green this repository keeps finding. + +What only the real server can answer, and what that suite therefore asserts: +values come back as strings and parse, a second queue over the same server sees +what the first wrote, the index holds rejection order, the worker drains, and an +abandoned record is still abandoned after a reopen. diff --git a/apps/jumentix-website/content/jumentix/packages/designer-core/index.mdx b/apps/jumentix-website/content/jumentix/packages/designer-core/index.mdx index 1b2374d46..563eca3cb 100644 --- a/apps/jumentix-website/content/jumentix/packages/designer-core/index.mdx +++ b/apps/jumentix-website/content/jumentix/packages/designer-core/index.mdx @@ -7,7 +7,7 @@ description: "Browser-safe design document helpers for Jumentix frontends." The framework-free Service Management designer core, prepared as a versioned `@jumentix` package -([JUM-493](https://linear.app/jumentix/issue/JUM-493/feature-publish-designer-core-as-jumentix-package-xpertminds-org-dry)). +. Browser-safe ESM, zero runtime dependencies, no DOM, no storage implementation. @@ -23,8 +23,7 @@ A versão em português deste documento está em ## What it is -The designer core is the separable logic the JUM-468/JUM-469 modularization -carved out of the Service Management designer (`apps/service-management`): +The designer core is the separable logic carved out of the Service Management designer (`apps/service-management`): everything the designer knows about a domain model, with nothing about how that model is rendered or persisted. **This package's `src/` is the canonical home of that code** — the dependency direction runs package → consumer, @@ -57,7 +56,7 @@ canvas, the inspectors, the status surfaces, the sync clients (`state/designerSync.js`, `state/catalogSyncClient.js`) and the storage adapters. Neither `LocalStorageDesignerStore` nor `CanaDesignerStore` ships here, and the package has **no dependency on Cana** — Cana is published by -its own epic ([JUM-416](https://linear.app/jumentix/issue/JUM-416/release-publish-the-browser-consumable-cana-typescript-package)). +its own epic. ## Usage @@ -93,19 +92,18 @@ The package follows semver over its **public API surface** (the barrel The *data contracts* the core reads and writes (the full-suite export document, the domain-package document) are versioned independently inside the -payloads themselves — that policy is owned by -[JUM-492](https://linear.app/jumentix/issue/JUM-492/feature-domain-package-versioning-with-semantic-conflict-resolution) -and pinned in Requirement 126, Contract 3; this package's version does not -restate it. +payloads themselves — that policy is the domain-package versioning contract +; this package's version does not restate it. ## Publish policy -Per -Requirement 070, -**no automatic publish exists**. The repository-wide artifact gate -(`bun run npm:packages:check`) rebuilds the approved package cohort, inspects -each tarball, and imports it from an external consumer. The protected manual -workflow from `main` performs the eventual npm publication. `prepublishOnly` +Publication is automated: after each application release on `main`, the +release workflow publishes every public package whose version is not yet on +npm, so a new version ships by bumping `version` here and promoting to `main` +(NPM package publishing). +The repository-wide artifact gate (`bun run npm:packages:check`) rebuilds the +approved package cohort, inspects each tarball, and imports it from an external +consumer before anything is published. `prepublishOnly` performs a clean rebuild before assembling the tarball, and `test/packaging.test.ts` asserts the manifest and packed contents, so the gate verifies an artifact whose contents are diff --git a/apps/jumentix-website/content/jumentix/reference/_meta.ts b/apps/jumentix-website/content/jumentix/reference/_meta.ts index 8078b6813..9279209a1 100644 --- a/apps/jumentix-website/content/jumentix/reference/_meta.ts +++ b/apps/jumentix-website/content/jumentix/reference/_meta.ts @@ -6,11 +6,5 @@ export default { 'errors-responses': 'Errors and Responses Map', 'service-management-runtime-environment': 'Runtime Environment Contracts', 'domain-designer-features': 'Domain Designer Features and Usage', - 'service-management-module-architecture': 'Service Management Module Architecture', - 'service-management-contract-parity': 'Service Management Contract Parity', - 'service-management-operations-console': 'Service Management Operations Console', - 'service-management-cana-adoption': 'Service Management Cana Adoption', - 'service-management-design-system-pwa': 'Service Management Design System and PWA', - 'service-management-collaboration-packaging': 'Service Management Collaboration and Packaging', 'frontend-offline-data-layer': 'Frontend offline data layer' }; diff --git a/apps/jumentix-website/content/jumentix/reference/domain-designer-features.mdx b/apps/jumentix-website/content/jumentix/reference/domain-designer-features.mdx index 9719108e0..068d7a010 100644 --- a/apps/jumentix-website/content/jumentix/reference/domain-designer-features.mdx +++ b/apps/jumentix-website/content/jumentix/reference/domain-designer-features.mdx @@ -76,11 +76,11 @@ How to use: 1. Select a domain. 2. Fill values in `Bounded Context`. -3. Click `Save Context`. + 3. Click `Save Context`. These fields are persisted in the designer state and exported through JSON/package flows. -## 3.1) Architecture designer (JUM-815, JUM-816) +## 3.1) Architecture designer The **Architecture** tab sits beside Domain Designer. A new model starts as one **Core** service (monolith) that hosts every domain. Core must keep the **Users** domain (identity and authentication). Additional services are boxes on the canvas: @@ -97,9 +97,22 @@ How to use: 3. Drop or **Move here** a domain other than Users onto the new service. 4. **Add Link** between Core and the domain service. -## 3.2) Per-service OAS export and Swagger (JUM-817, JUM-818) +## 3.2) Per-service OAS export and Swagger -Export OAS 3.1 now writes `x-services`, `servers[]` with `x-service-id`, `x-service` on operations and schemas, and `x-architecture-links`. The merged document is the Core view. The **OpenAPI** tab embeds Swagger UI (vendored from `apps/backend-template/OASdoc`). The service selector filters operations; Try it out uses that service's `servers` URL. +Export OAS 3.1 now writes: + +- `x-services` and `servers[]` with `x-service-id` +- `x-service` on every operation and entity schema +- `x-architecture-links` for lossless architecture round-trip + +The merged document is the Core view (every operation, every service address). **OpenAPI** tab embeds Swagger UI (vendored `swagger-ui-dist` from `apps/backend-template/OASdoc`). The service selector filters to one service; Try it out uses that service's `servers` URL. + +How to use: + +1. Model architecture, then open **OpenAPI**. +2. Choose **Merged Core document** or a named service. +3. Press **Refresh** after model edits. +4. Export OAS from Domain Designer Share panel for files; the catalog record may store `design.oasDocuments.merged` and `design.oasDocuments.services`. ## 4) Entity Editing and Templates @@ -126,7 +139,7 @@ How to use: ## 5) RBAC Policy Mapping Per-entity action policy, aligned with the tenant RBAC authorization contract -(`TENANT-RBAC-AUTHORIZATION-CONTRACT.md`, JUM-477): +(`TENANT-RBAC-AUTHORIZATION-CONTRACT.md`): - Actions: - `list` @@ -267,10 +280,10 @@ How to use: 2. Use import buttons for JSON/OAS/package. 3. For package export, selected domain is used as source package. -### 10.1) OAS 3.1 export contract (Requirement 036, JUM-474) +### 10.1) OAS 3.1 export contract -The OpenAPI 3.1 export produces a document compliant with Requirement 036 and -the route-resolution check (`ci-cd/check-oas-route-resolution.js`): +The OpenAPI 3.1 export produces a document compliant with the port-object +contract rules and the route-resolution check (`ci-cd/check-oas-route-resolution.js`): - Every operation carries a unique `operationId` on the canonical `spec/1.0.0.yml` verb scheme (`getAll*`, `create*`, `get*ById`, `update*`, @@ -287,7 +300,7 @@ the route-resolution check (`ci-cd/check-oas-route-resolution.js`): (for example `Foo Bar` vs `Foo-Bar`) fail the export quality gate instead of silently overwriting each other in the document. -### 10.2) Lossless OAS round-trip (JUM-478) +### 10.2) Lossless OAS round-trip The OAS crossing is a contract between the exporter and the importer: what OAS cannot express natively crosses as agreed `x-` extensions and is diff --git a/apps/jumentix-website/content/jumentix/reference/frontend-offline-data-layer.mdx b/apps/jumentix-website/content/jumentix/reference/frontend-offline-data-layer.mdx index 57a46cf85..bee792ffe 100644 --- a/apps/jumentix-website/content/jumentix/reference/frontend-offline-data-layer.mdx +++ b/apps/jumentix-website/content/jumentix/reference/frontend-offline-data-layer.mdx @@ -9,29 +9,29 @@ The Portuguese version is at [FRONTEND-OFFLINE-DATA-LAYER.pt-BR.md](/docs/pt-BR/ `apps/frontend` is offline-first on `@jumentix/cana`. The OAS is parsed at boot, IndexedDB opens **before** the login form, the first signed-in session runs a full load behind a progress view, and X-CRUD reads and writes the local copy. The REST SDK is used for auth, sync, and outbox replay — not for grid reads after Cana is open. -## Boot (JUM-802) +## Boot 1. `src/data/canaSchema.ts` derives one store per OAS entity that has a list operation (`User` → `users`, `Organization` → `organizations`). `keyPath` is `x-primary-key`. Indexes are sortable and filterable fields plus `updatedAt`, `deletedAt`, and every `x-relation.field`, plus internal stores `meta` and `outbox`. 2. Schema `version` is a stable positive integer hash of that store set. When the fingerprint in `meta` disagrees, the database is **dropped and recreated** (full resync). Cana upgrades are additive; this seed does not migrate rows in place. 3. `src/data/db.ts` calls `createClient({ fallback: false }).open()` before `app.mount`. IndexedDB unavailable → boot error view (i18n). No localStorage fallback. -## Local repository (JUM-803) +## Local repository `src/data/localRepository.ts` lists through Cana (`query` uses an index when the primary sort field is indexed and there is no search/filter) and then `runListQuery` from `@jumentix/persistence-contracts`. `resolveRelations` loads belongsTo rows. Live listeners subscribe to Cana events on the entity store and its relation targets. -## X-CRUD local mode (JUM-804) +## X-CRUD local mode When Cana is open, `entityStore` lists locally and mutations go through the outbox. Component tests that never call `openCana()` keep the previous server path (`x-list-capabilities`). After sync, listings do not need `GET /api/.../users`. Dashboard totals use local counts. List rows omit nested arrays, so sync follows each list page with GET-by-id (`getOneById` / `getOrganizationById`) when an array field is missing. The profile store then reads the signed-in user from Cana; scalar saves go through the User outbox. Email/document/phone sub-resources still call REST and write the GET copy back into Cana. -## Sync (JUM-805) +## Sync Login routes to `/sync`. `fullLoad` pages each list operation at `maxSize` (100), sorted `updatedAt:asc,id:asc`. `deltaSync` uses `updatedAt gt lastSync` and `includeDeleted=true`. A different username wipes the database and full-loads again. The shell is routed only after `meta.session.lastSyncAt` is written. -## Outbox (JUM-806 / JUM-807) +## Outbox UI writes Cana first with `_sync: 'pending'`. Intents live in `outbox` in the same transaction. Replay order follows `createdAt`; 2xx replaces the local row with the server copy; 5xx/network stay pending; definitive 4xx compensates (create → delete, update/delete → `beforeImage`) and pushes a notification with “reopen with my data”. 401 uses the existing session-expiry path and keeps the outbox for the same user. -## PWA (JUM-808) +## PWA Hand-written `public/sw.js` (no `vite-plugin-pwa` — pinning and third-party review cost more than a small worker). Registration runs in production builds, or when `VITE_PWA=1`. The non-production `vite` process does not register the worker, so Cypress e2e against `vite` proves local reads by failing `/api` after sync, not by stopping the origin server. `public/manifest.json` uses the OAS `info.title` (`Jumentix API`), plus 192/512 icons (`purpose` `any` / `any maskable`). @@ -45,6 +45,6 @@ npx lighthouse@11.7.1 http://127.0.0.1:4173/ --only-categories=pwa --chrome-flag Result: **PWA category score 1** (`lighthouse` 11.7.1). `installable-manifest` 1, `splash-screen` 1, `themed-omnibox` 1, `maskable-icon` 1, `content-width` 1, `viewport` 1. `lighthouse` 12.8.2 returns `categories.pwa` empty — that release dropped the PWA category, so 11.7.1 is the audit that still grades installability. -## Tests (JUM-809) +## Tests `fake-indexeddb` `6.2.5` is preloaded from `test/setup/indexeddb.ts`. Cypress helpers: `cy.goOffline()`, `cy.goOnline()`, `cy.serverCreate()`. Specs: `offline-boot.cy.ts`, `offline-sync.cy.ts`, `offline-writes.cy.ts`, `offline-pwa.cy.ts`. `rtk proxy bun run --filter @jumentix/frontend test:e2e` → **20 passing / 0 failing** across 9 specs. diff --git a/apps/jumentix-website/content/jumentix/reference/index.mdx b/apps/jumentix-website/content/jumentix/reference/index.mdx index 22b6c7f5f..5ccaaee03 100644 --- a/apps/jumentix-website/content/jumentix/reference/index.mdx +++ b/apps/jumentix-website/content/jumentix/reference/index.mdx @@ -23,10 +23,4 @@ Maps and contracts for day-to-day work — examples first. - [Errors and Responses Map](/docs/jumentix/reference/errors-responses) - [Runtime Environment Contracts](/docs/jumentix/reference/service-management-runtime-environment) - [Domain Designer Features and Usage](/docs/jumentix/reference/domain-designer-features) -- [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture) -- [Service Management Contract Parity](/docs/jumentix/reference/service-management-contract-parity) -- [Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console) -- [Service Management Cana Adoption](/docs/jumentix/reference/service-management-cana-adoption) -- [Service Management Design System and PWA](/docs/jumentix/reference/service-management-design-system-pwa) -- [Service Management Collaboration and Packaging](/docs/jumentix/reference/service-management-collaboration-packaging) - [Frontend offline data layer](/docs/jumentix/reference/frontend-offline-data-layer) diff --git a/apps/jumentix-website/content/jumentix/reference/package-scripts.mdx b/apps/jumentix-website/content/jumentix/reference/package-scripts.mdx index 10ef803cf..bf0387403 100644 --- a/apps/jumentix-website/content/jumentix/reference/package-scripts.mdx +++ b/apps/jumentix-website/content/jumentix/reference/package-scripts.mdx @@ -19,27 +19,32 @@ bun run | `compat:check-node-version` | Use when you need this specific workspace operation. | `bun run compat:check-node-version` | `node ci-cd/check-node-version.js` | | `preinstall` | Use when you need this specific workspace operation. | `bun run preinstall` | `bun ci-cd/check-bun-version.js` | | `deps:check-overrides` | Use when you need this specific workspace operation. | `bun run deps:check-overrides` | `bun ci-cd/check-dependency-override-integrity.js` | -| `mono:build` | Run workspace-wide recursive operations. | `bun run mono:build` | `bun run --filter '*' build` | +| `mono:build` | Run workspace-wide recursive operations. | `bun run mono:build` | `bun run mono:build:deps && bun run --filter '*' build` | +| `mono:build:deps` | Run workspace-wide recursive operations. | `bun run mono:build:deps` | `bun run --filter @jumentix/cana build` | | `mono:test` | Run workspace-wide recursive operations. | `bun run mono:test` | `bun run workspace:test` | | `mono:lint` | Run workspace-wide recursive operations. | `bun run mono:lint` | `bun run --filter '*' lint` | | `mono:typecheck` | Run workspace-wide recursive operations. | `bun run mono:typecheck` | `bun run --filter '*' typecheck` | | `docs:translate:ptbr` | Generate or synchronize documentation artifacts. | `bun run docs:translate:ptbr` | `bun tooling/scripts/generate-ptbr-docs.mjs` | | `docs:translate:ptbr:links` | Generate or synchronize documentation artifacts. | `bun run docs:translate:ptbr:links` | `bun tooling/scripts/patch-ptbr-links.mjs` | | `docs:consumers:package-scripts` | Generate or synchronize documentation artifacts. | `bun run docs:consumers:package-scripts` | `bun tooling/scripts/generate-consumer-package-scripts-docs.mjs` | -| `prepare` | Install git hooks (husky). Usually runs automatically. | `bun run prepare` | `husky install` | +| `docs:check-current-governance` | Generate or synchronize documentation artifacts. | `bun run docs:check-current-governance` | `bun ci-cd/check-current-governance-docs.js` | +| `prepare` | Install git hooks (husky). Usually runs automatically. | `bun run prepare` | `husky` | | `changelog:update` | Generate or verify changelog from git history. | `bun run changelog:update` | `bun ci-cd/update-changelog.js` | | `changelog:check` | Generate or verify changelog from git history. | `bun run changelog:check` | `bun ci-cd/update-changelog.js --check` | | `oas:check-routes` | Validate OpenAPI contracts and route resolution. | `bun run oas:check-routes` | `bun ci-cd/check-oas-route-resolution.js` | +| `oas:check-relations` | Validate OpenAPI contracts and route resolution. | `bun run oas:check-relations` | `bun ci-cd/check-oas-relations.js` | +| `entities:purge-tombstones` | Use when you need this specific workspace operation. | `bun run entities:purge-tombstones` | `bun apps/backend-template/scripts/purge-tombstones.js` | | `deps:check-cycles` | Use when you need this specific workspace operation. | `bun run deps:check-cycles` | `bun ci-cd/check-core-import-cycles.js` | | `arch:check-boundaries` | Validate architecture boundaries and constraints. | `bun run arch:check-boundaries` | `bun ci-cd/check-hexagonal-boundaries.js` | | `arch:check-users-legacy-imports` | Validate architecture boundaries and constraints. | `bun run arch:check-users-legacy-imports` | `bun ci-cd/check-users-legacy-imports.js` | | `arch:check-workspace-boundaries` | Validate architecture boundaries and constraints. | `bun run arch:check-workspace-boundaries` | `bun ci-cd/check-workspace-boundaries.js` | +| `arch:check-ownership-placement` | Validate architecture boundaries and constraints. | `bun run arch:check-ownership-placement` | `bun ci-cd/check-workspace-ownership-placement.js` | | `workspace:check-coverage-policy` | Validate workspace-level policies. | `bun run workspace:check-coverage-policy` | `bun ci-cd/check-workspace-coverage-policy.js` | | `workspace:check-quality` | Validate workspace-level policies. | `bun run workspace:check-quality` | `bun ci-cd/check-workspace-quality.js` | | `ci:affected` | Use in CI validation and delivery gates. | `bun run ci:affected` | `bun ci-cd/check-affected-workspaces.js` | | `ci:monorepo` | Use in CI validation and delivery gates. | `bun run ci:monorepo` | `bun ci-cd/run-monorepo-ci.js` | | `release:dry-run` | Run release governance and dry-run routines. | `bun run release:dry-run` | `bun ci-cd/release-dry-run.js all` | -| `release:dry-run:packages` | Run release governance and dry-run routines. | `bun run release:dry-run:packages` | `bun ci-cd/release-dry-run.js packages` | +| `release:dry-run:packages` | Run release governance and dry-run routines. | `bun run release:dry-run:packages` | `bun run npm:packages:check && bun ci-cd/release-dry-run.js packages` | | `release:dry-run:apps` | Run release governance and dry-run routines. | `bun run release:dry-run:apps` | `bun ci-cd/release-dry-run.js apps` | | `agent-registry:register` | Use when you need this specific workspace operation. | `bun run agent-registry:register` | `bun packages/agent-registry/bin/agent-registry-cli.js register` | | `agent-registry:heartbeat` | Use when you need this specific workspace operation. | `bun run agent-registry:heartbeat` | `bun packages/agent-registry/bin/agent-registry-cli.js heartbeat` | @@ -48,8 +53,12 @@ bun run | `agent-registry:repair` | Use when you need this specific workspace operation. | `bun run agent-registry:repair` | `bun packages/agent-registry/bin/agent-registry-cli.js repair` | | `agent-registry:sync` | Use when you need this specific workspace operation. | `bun run agent-registry:sync` | `bun packages/agent-registry/bin/agent-registry-cli.js sync` | | `agent-registry:check` | Use when you need this specific workspace operation. | `bun run agent-registry:check` | `bun packages/agent-registry/bin/agent-registry-cli.js check` | +| `agent-bus:publish` | Use when you need this specific workspace operation. | `bun run agent-bus:publish` | `bun packages/agent-registry/bin/agent-registry-cli.js publish` | +| `agent-bus:watch` | Use when you need this specific workspace operation. | `bun run agent-bus:watch` | `bun packages/agent-registry/bin/agent-registry-cli.js watch` | +| `agent-bus:status` | Use when you need this specific workspace operation. | `bun run agent-bus:status` | `bun packages/agent-registry/bin/agent-registry-cli.js status` | | `arch:check-http-adapters` | Validate architecture boundaries and constraints. | `bun run arch:check-http-adapters` | `bun ci-cd/check-http-adapter-authenticity.js` | | `ci:check-provider` | Use in CI validation and delivery gates. | `bun run ci:check-provider` | `bun ci-cd/check-ci-provider.js` | +| `sonar:check-reliability` | Use when you need this specific workspace operation. | `bun run sonar:check-reliability` | `bun ci-cd/check-sonar-reliability.js` | | `ci:check-third-party-review` | Use in CI validation and delivery gates. | `bun run ci:check-third-party-review` | `bun ci-cd/check-third-party-review.js` | | `requirements:check` | Use when you need this specific workspace operation. | `bun run requirements:check` | `bun ci-cd/check-requirements-registry.js` | | `test-map:generate` | Use when you need this specific workspace operation. | `bun run test-map:generate` | `bun ci-cd/generate-test-map.js` | @@ -64,7 +73,7 @@ bun run | `ci:smoke` | Use in CI validation and delivery gates. | `bun run ci:smoke` | `JUMENTIX_JWT_TOKEN_SECRET_KEY=${JUMENTIX_JWT_TOKEN_SECRET_KEY:-ci_jwt_secret_key} NODE_ENV=ci bun apps/backend-template/scripts/run-api-smoke.js` | | `ci:integration` | Use in CI validation and delivery gates. | `bun run ci:integration` | `JUMENTIX_JWT_TOKEN_SECRET_KEY=${JUMENTIX_JWT_TOKEN_SECRET_KEY:-ci_jwt_secret_key} bun ci-cd/run-integration-tests.js` | | `ci:security-smoke` | Use in CI validation and delivery gates. | `bun run ci:security-smoke` | `NODE_ENV=ci bun apps/backend-template/scripts/run-security-smoke.js` | -| `ci:gate` | Use in CI validation and delivery gates. | `bun run ci:gate` | `bun run check-bun-version && bun run deps:check-overrides && bun run deps:audit && bun run lint && bun run deps:check-cycles && bun run arch:check-boundaries && bun run arch:check-users-legacy-imports && bun run arch:check-workspace-boundaries && bun run arch:check-http-adapters && bun run workspace:check-quality && bun run workspace:check-coverage-policy && bun run release:governance:check && bun run governance:check-authorship && bun run requirements:check && bun run packages:check-suites && bun run test-map:check && bun run ci:check-provider && bun run ci:check-third-party-review && bun run integrations:check && bun run integration-migration:check && bun run agent-registry:check && bun run test:unit && bun run ci:security-smoke && bun run oas:check-routes && bun run serverless:check-handlers && bun run build:dev && bun run ci:smoke` | +| `ci:gate` | Use in CI validation and delivery gates. | `bun run ci:gate` | `bun run check-bun-version && bun run deps:check-overrides && bun run deps:audit && bun run lint && bun run deps:check-cycles && bun run arch:check-boundaries && bun run arch:check-users-legacy-imports && bun run arch:check-workspace-boundaries && bun run arch:check-ownership-placement && bun run arch:check-http-adapters && bun run workspace:check-quality && bun run workspace:check-coverage-policy && bun run release:governance:check && bun run governance:check-authorship && bun run requirements:check && bun run docs:check-current-governance && bun run packages:check-suites && bun run packages:check-build-freshness && bun run website:check-content-routes && bun run rtdb:check-indexes && bun run test:integrity && bun run test-map:check && bun run ci:check-provider && bun run ci:check-third-party-review && bun run integrations:check && bun run integration-migration:check && bun run agent-registry:check && bun run test:unit && bun run frontend:test:coverage && bun run frontend:coverage:check && bun run ci:security-smoke && bun run oas:check-routes && bun run oas:check-relations && bun run serverless:check-handlers && bun run build:dev && bun run ci:smoke` | | `ci:gate:branch` | Use in CI validation and delivery gates. | `bun run ci:gate:branch` | `bun ci-cd/run-branch-quality-gate.js` | | `ci:gate:task` | Use in CI validation and delivery gates. | `bun run ci:gate:task` | `bun ci-cd/run-task-change-tests.js` | | `ci:gate:strict` | Use in CI validation and delivery gates. | `bun run ci:gate:strict` | `bun ci-cd/run-full-test-matrix.js` | @@ -73,10 +82,13 @@ bun run | `website:build` | Operate the commercial website lifecycle. | `bun run website:build` | `bun run --filter @jumentix/website build` | | `website:start` | Operate the commercial website lifecycle. | `bun run website:start` | `bun run --filter @jumentix/website start` | | `website:storybook` | Operate the commercial website lifecycle. | `bun run website:storybook` | `bun run --filter @jumentix/website storybook` | -| `website:storybook:build` | Operate the commercial website lifecycle. | `bun run website:storybook:build` | `bun run --filter @jumentix/website storybook:build` | +| `website:deps:build` | Operate the commercial website lifecycle. | `bun run website:deps:build` | `bun run --filter @jumentix/cana build && bun run --filter @jumentix/cana-react build && bun run --filter @jumentix/cana-vue build && bun run --filter @jumentix/designer-core build && bun run --filter @jumentix/key-value-storage build && bun run --filter @jumentix/message-mediator build && bun run --filter @jumentix/mutex-service build && bun run --filter @jumentix/sdk-grpc-client build && bun run --filter @jumentix/sdk-rest-client build && bun run --filter @jumentix/sdk-websocket-client build` | +| `website:storybook:build` | Operate the commercial website lifecycle. | `bun run website:storybook:build` | `bun run website:deps:build && bun run --filter @jumentix/website storybook:build` | | `website:storybook:smoke` | Operate the commercial website lifecycle. | `bun run website:storybook:smoke` | `bun run --filter @jumentix/website storybook:smoke` | | `website:test:prepublish` | Operate the commercial website lifecycle. | `bun run website:test:prepublish` | `bun run --filter @jumentix/website test:prepublish` | +| `website:test:unit` | Operate the commercial website lifecycle. | `bun run website:test:unit` | `bun run --filter @jumentix/website test:unit` | | `website:test:cypress` | Operate the commercial website lifecycle. | `bun run website:test:cypress` | `bun run --filter @jumentix/website test:cypress` | +| `website:test:routes` | Operate the commercial website lifecycle. | `bun run website:test:routes` | `bun run --filter @jumentix/website test:routes` | | `website:test:quality` | Operate the commercial website lifecycle. | `bun run website:test:quality` | `bun run --filter @jumentix/website test:quality` | | `website:vercel:link` | Operate the commercial website lifecycle. | `bun run website:vercel:link` | `bun x vercel link --cwd apps/jumentix-website --project jumentix-website --yes` | | `website:vercel:pull:preview` | Operate the commercial website lifecycle. | `bun run website:vercel:pull:preview` | `bun x vercel pull --cwd apps/jumentix-website --environment=preview --yes` | @@ -85,15 +97,16 @@ bun run | `website:publish` | Operate the commercial website lifecycle. | `bun run website:publish` | `bun run website:test:prepublish && bun run website:deploy:vercel` | | `website:deploy:vercel:preview` | Operate the commercial website lifecycle. | `bun run website:deploy:vercel:preview` | `bun x vercel --cwd apps/jumentix-website` | | `npm:whoami` | Execute npm organization and publish helper commands. | `bun run npm:whoami` | `bun pm whoami` | -| `npm:org:check:xpertminds` | Execute npm organization and publish helper commands. | `bun run npm:org:check:xpertminds` | `bun ci-cd/check-npm-org-integration.js` | -| `npm:publish:dry-run:packages` | Execute npm organization and publish helper commands. | `bun run npm:publish:dry-run:packages` | `bun ci-cd/npm-publish-dry-run.js` | +| `npm:org:check:jumentix` | Execute npm organization and publish helper commands. | `bun run npm:org:check:jumentix` | `bun ci-cd/check-npm-org-integration.js` | +| `npm:packages:check` | Execute npm organization and publish helper commands. | `bun run npm:packages:check` | `bun ci-cd/check-npm-package-release.js` | +| `npm:publish:dry-run:packages` | Execute npm organization and publish helper commands. | `bun run npm:publish:dry-run:packages` | `bun run npm:packages:check` | | `pm2:list` | Manage PM2 runtime processes. | `bun run pm2:list` | `pm2 ls` | | `pm2:logs` | Manage PM2 runtime processes. | `bun run pm2:logs` | `pm2 logs` | | `pm2:stop:all` | Manage PM2 runtime processes. | `bun run pm2:stop:all` | `pm2 stop all` | | `pm2:delete:all` | Manage PM2 runtime processes. | `bun run pm2:delete:all` | `pm2 delete all` | -| `pm2:start:dev:restapi` | Manage PM2 runtime processes. | `bun run pm2:start:dev:restapi` | `pm2 start ./pm2/ecosystem.dev.config.cjs --only jumentix-dev-service-management,jumentix-dev-restapi --update-env` | -| `pm2:start:dev:websocket-rest` | Manage PM2 runtime processes. | `bun run pm2:start:dev:websocket-rest` | `pm2 start ./pm2/ecosystem.dev.config.cjs --only jumentix-dev-service-management,jumentix-dev-restapi,jumentix-dev-websocketapi --update-env` | -| `pm2:start:dev:grpc-rest` | Manage PM2 runtime processes. | `bun run pm2:start:dev:grpc-rest` | `pm2 start ./pm2/ecosystem.dev.config.cjs --only jumentix-dev-service-management,jumentix-dev-restapi,jumentix-dev-grpcapi --update-env` | +| `pm2:start:dev:restapi` | Manage PM2 runtime processes. | `bun run pm2:start:dev:restapi` | `bun run service-management:vendor && pm2 start ./pm2/ecosystem.dev.config.cjs --only jumentix-dev-service-management,jumentix-dev-restapi,jumentix-dev-purge-tombstones --update-env` | +| `pm2:start:dev:websocket-rest` | Manage PM2 runtime processes. | `bun run pm2:start:dev:websocket-rest` | `bun run service-management:vendor && pm2 start ./pm2/ecosystem.dev.config.cjs --only jumentix-dev-service-management,jumentix-dev-restapi,jumentix-dev-websocketapi,jumentix-dev-purge-tombstones --update-env` | +| `pm2:start:dev:grpc-rest` | Manage PM2 runtime processes. | `bun run pm2:start:dev:grpc-rest` | `bun run service-management:vendor && pm2 start ./pm2/ecosystem.dev.config.cjs --only jumentix-dev-service-management,jumentix-dev-restapi,jumentix-dev-grpcapi,jumentix-dev-purge-tombstones --update-env` | | `pm2:start:staging:restapi` | Manage PM2 runtime processes. | `bun run pm2:start:staging:restapi` | `pm2 start ./pm2/ecosystem.staging.config.cjs --only jumentix-staging-service-management,jumentix-staging-restapi --update-env` | | `pm2:start:staging:websocket-rest` | Manage PM2 runtime processes. | `bun run pm2:start:staging:websocket-rest` | `pm2 start ./pm2/ecosystem.staging.config.cjs --only jumentix-staging-service-management,jumentix-staging-restapi,jumentix-staging-websocketapi --update-env` | | `pm2:start:staging:grpc-rest` | Manage PM2 runtime processes. | `bun run pm2:start:staging:grpc-rest` | `pm2 start ./pm2/ecosystem.staging.config.cjs --only jumentix-staging-service-management,jumentix-staging-restapi,jumentix-staging-grpcapi --update-env` | @@ -118,9 +131,9 @@ bun run | `test:integration:lambda` | Run tests for specific scope or profile. | `bun run test:integration:lambda` | `JUMENTIX_MESSAGE_MEDIATOR_ADAPTER=inmemory NODE_ENV=dev bun ci-cd/run-suite.js --script-label lambda apps/backend-template/test/integration/Lambda` | | `test:integration:cloudflare-workers` | Run tests for specific scope or profile. | `bun run test:integration:cloudflare-workers` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label cloudflare-workers apps/backend-template/test/integration/Cloudflare-Workers` | | `test:integration:vercel-functions` | Run tests for specific scope or profile. | `bun run test:integration:vercel-functions` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label vercel-functions apps/backend-template/test/integration/Vercel-Functions` | +| `test:integration:feathers` | Run tests for specific scope or profile. | `bun run test:integration:feathers` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label feathers apps/backend-template/test/integration/Feathers` | | `test:integration:loopback` | Run tests for specific scope or profile. | `bun run test:integration:loopback` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label loopback apps/backend-template/test/integration/LoopBack` | | `test:integration:sails-js` | Run tests for specific scope or profile. | `bun run test:integration:sails-js` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label sails-js apps/backend-template/test/integration/Sails-JS` | -| `test:integration:feathers` | Run tests for specific scope or profile. | `bun run test:integration:feathers` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label feathers apps/backend-template/test/integration/Feathers` | | `test:integration:derby-js` | Run tests for specific scope or profile. | `bun run test:integration:derby-js` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label derby-js apps/backend-template/test/integration/Derby-JS` | | `test:integration:adonis-js` | Run tests for specific scope or profile. | `bun run test:integration:adonis-js` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label adonis-js apps/backend-template/test/integration/Adonis-JS` | | `test:integration:total-js` | Run tests for specific scope or profile. | `bun run test:integration:total-js` | `NODE_ENV=dev bun ci-cd/run-suite.js --script-label total-js apps/backend-template/test/integration/Total-JS` | @@ -191,7 +204,8 @@ bun run | `cli` | Use when you need this specific workspace operation. | `bun run cli` | `bun run dev:cli` | | `cli:bootstrap` | Use when you need this specific workspace operation. | `bun run cli:bootstrap` | `node ./bin/jumentix-bootstrap.js` | | `dev:cli` | Start development runtime mode. | `bun run dev:cli` | `bun -r tsconfig-paths/register ./apps/backend-template/src/interface/CLI/index.ts` | -| `dev:service-management` | Start development runtime mode. | `bun run dev:service-management` | `pm2 start ./apps/service-management/server.js --name jumentix-dev-service-management --interpreter bun --update-env` | +| `service-management:vendor` | Use when you need this specific workspace operation. | `bun run service-management:vendor` | `bun apps/service-management/scripts/sync-service-management-cana-bundle.js && bun apps/service-management/scripts/sync-service-management-designer-core.js && bun apps/service-management/scripts/sync-service-management-d3.js && bun apps/service-management/scripts/sync-service-management-swagger-ui.js` | +| `dev:service-management` | Start development runtime mode. | `bun run dev:service-management` | `bun run service-management:vendor && pm2 start ./apps/service-management/server.js --name jumentix-dev-service-management --interpreter bun --update-env` | | `start:cli` | Use when you need this specific workspace operation. | `bun run start:cli` | `bun run dev:cli` | | `prod:http` | Start production runtime profile. | `bun run prod:http` | `pm2 start ./.build/apps/backend-template/src/interface/HTTP/adapters/start-rest-api.js --name jumentix-prod-http --interpreter bun --interpreter-args='--env-file=./.build/apps/backend-template/src/config/.env.prod' --update-env` | | `prod:express` | Start production runtime profile. | `bun run prod:express` | `JUMENTIX_HTTP_FRAMEWORK=express pm2 start ./.build/apps/backend-template/src/interface/HTTP/adapters/start-rest-api.js --name jumentix-prod-express --interpreter bun --interpreter-args='--env-file=./.build/apps/backend-template/src/config/.env.prod' --update-env` | @@ -211,19 +225,19 @@ bun run | `staging:websocket` | Start staging runtime profile. | `bun run staging:websocket` | `bun run pm2:start:staging:websocket-rest` | | `staging:grpc` | Start staging runtime profile. | `bun run staging:grpc` | `bun run pm2:start:staging:grpc-rest` | | `prod:serverless` | Start production runtime profile. | `bun run prod:serverless` | `bun run lint && NODE_ENV=prod serverless dev` | -| `docker:composeredis` | Start Redis dependencies with bounded retries for transient container-registry failures. | `bun run docker:composeredis` | `ci-cd/start-redis-compose.sh` | +| `docker:composeredis` | Start/stop containerized dependencies and services. | `bun run docker:composeredis` | `ci-cd/cleanup-local-ci-services.sh && docker compose -f "apps/backend-template/docker-compose-redis.yml" down --remove-orphans \|\| true && docker compose -f "apps/backend-template/docker-compose-redis.yml" up -d --build --wait --force-recreate --remove-orphans` | | `docker:composemessaging` | Start/stop containerized dependencies and services. | `bun run docker:composemessaging` | `docker compose -f "apps/backend-template/docker-compose-messaging.yml" up -d --build --wait` | | `docker:compose:platform-services` | Start/stop containerized dependencies and services. | `bun run docker:compose:platform-services` | `docker compose -f "apps/backend-template/docker-compose-platform-services.yml" up -d --build --wait` | -| `docker:up:postgresql` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:postgresql` | `ci-cd/start-compose-service.sh jumentix-postgresql apps/backend-template/docker-compose-postgresql.yml` | -| `docker:up:mysql` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:mysql` | `ci-cd/start-compose-service.sh jumentix-mysql apps/backend-template/docker-compose-mysql.yml` | -| `docker:up:mssql` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:mssql` | `ci-cd/start-compose-service.sh jumentix-mssql apps/backend-template/docker-compose-mssql.yml` | -| `docker:up:oracle` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:oracle` | `ci-cd/start-compose-service.sh jumentix-oracle apps/backend-template/docker-compose-oracle.yml` | -| `docker:up:mongodb` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:mongodb` | `ci-cd/start-compose-service.sh jumentix-mongodb apps/backend-template/docker-compose-mongodb.yml` | -| `docker:up:cassandra` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:cassandra` | `ci-cd/start-compose-service.sh jumentix-cassandra apps/backend-template/docker-compose-cassandra.yml` | -| `docker:up:dynamodb` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:dynamodb` | `ci-cd/start-compose-service.sh jumentix-dynamodb apps/backend-template/docker-compose-dynamodb.yml` | -| `docker:up:firebase` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:firebase` | `ci-cd/start-compose-service.sh jumentix-firebase apps/backend-template/docker-compose-firebase.yml` | -| `docker:up:aurora` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:aurora` | `ci-cd/start-compose-service.sh jumentix-aurora apps/backend-template/docker-compose-aurora.yml` | -| `docker:up:rds` | Start containerized dependencies with bounded retry for transient registry failures. | `bun run docker:up:rds` | `ci-cd/start-compose-service.sh jumentix-rds apps/backend-template/docker-compose-rds.yml` | +| `docker:up:postgresql` | Start/stop containerized dependencies and services. | `bun run docker:up:postgresql` | `docker compose -p jumentix-postgresql -f "apps/backend-template/docker-compose-postgresql.yml" down --remove-orphans \|\| true && docker compose -p jumentix-postgresql -f "apps/backend-template/docker-compose-postgresql.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:mysql` | Start/stop containerized dependencies and services. | `bun run docker:up:mysql` | `docker compose -p jumentix-mysql -f "apps/backend-template/docker-compose-mysql.yml" down --remove-orphans \|\| true && docker compose -p jumentix-mysql -f "apps/backend-template/docker-compose-mysql.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:mssql` | Start/stop containerized dependencies and services. | `bun run docker:up:mssql` | `docker compose -p jumentix-mssql -f "apps/backend-template/docker-compose-mssql.yml" down --remove-orphans \|\| true && docker compose -p jumentix-mssql -f "apps/backend-template/docker-compose-mssql.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:oracle` | Start/stop containerized dependencies and services. | `bun run docker:up:oracle` | `docker compose -p jumentix-oracle -f "apps/backend-template/docker-compose-oracle.yml" down --remove-orphans \|\| true && docker compose -p jumentix-oracle -f "apps/backend-template/docker-compose-oracle.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:mongodb` | Start/stop containerized dependencies and services. | `bun run docker:up:mongodb` | `docker compose -p jumentix-mongodb -f "apps/backend-template/docker-compose-mongodb.yml" down --remove-orphans \|\| true && docker compose -p jumentix-mongodb -f "apps/backend-template/docker-compose-mongodb.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:cassandra` | Start/stop containerized dependencies and services. | `bun run docker:up:cassandra` | `docker compose -p jumentix-cassandra -f "apps/backend-template/docker-compose-cassandra.yml" down --remove-orphans \|\| true && docker compose -p jumentix-cassandra -f "apps/backend-template/docker-compose-cassandra.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:dynamodb` | Start/stop containerized dependencies and services. | `bun run docker:up:dynamodb` | `docker compose -p jumentix-dynamodb -f "apps/backend-template/docker-compose-dynamodb.yml" down --remove-orphans \|\| true && docker compose -p jumentix-dynamodb -f "apps/backend-template/docker-compose-dynamodb.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:firebase` | Start/stop containerized dependencies and services. | `bun run docker:up:firebase` | `docker compose -p jumentix-firebase -f "apps/backend-template/docker-compose-firebase.yml" down --remove-orphans \|\| true && docker compose -p jumentix-firebase -f "apps/backend-template/docker-compose-firebase.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:aurora` | Start/stop containerized dependencies and services. | `bun run docker:up:aurora` | `docker compose -p jumentix-aurora -f "apps/backend-template/docker-compose-aurora.yml" down --remove-orphans \|\| true && docker compose -p jumentix-aurora -f "apps/backend-template/docker-compose-aurora.yml" up -d --build --wait --force-recreate --remove-orphans` | +| `docker:up:rds` | Start/stop containerized dependencies and services. | `bun run docker:up:rds` | `docker compose -p jumentix-rds -f "apps/backend-template/docker-compose-rds.yml" down --remove-orphans \|\| true && docker compose -p jumentix-rds -f "apps/backend-template/docker-compose-rds.yml" up -d --build --wait --force-recreate --remove-orphans` | | `docker:down:postgresql` | Start/stop containerized dependencies and services. | `bun run docker:down:postgresql` | `docker compose -p jumentix-postgresql -f "apps/backend-template/docker-compose-postgresql.yml" down --remove-orphans` | | `docker:down:mysql` | Start/stop containerized dependencies and services. | `bun run docker:down:mysql` | `docker compose -p jumentix-mysql -f "apps/backend-template/docker-compose-mysql.yml" down --remove-orphans` | | `docker:down:mssql` | Start/stop containerized dependencies and services. | `bun run docker:down:mssql` | `docker compose -p jumentix-mssql -f "apps/backend-template/docker-compose-mssql.yml" down --remove-orphans` | @@ -252,10 +266,6 @@ bun run | `test:integration:lambda:ci` | Run tests for specific scope or profile. | `bun run test:integration:lambda:ci` | `JUMENTIX_TEST_RUNTIME=node JUMENTIX_MESSAGE_MEDIATOR_ADAPTER=inmemory NODE_ENV=dev bun ci-cd/run-suite.js --script-label lambda apps/backend-template/test/integration/Lambda` | | `test:integration:cloudflare-workers:ci` | Run tests for specific scope or profile. | `bun run test:integration:cloudflare-workers:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label cloudflare-workers apps/backend-template/test/integration/Cloudflare-Workers` | | `test:integration:vercel-functions:ci` | Run tests for specific scope or profile. | `bun run test:integration:vercel-functions:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label vercel-functions apps/backend-template/test/integration/Vercel-Functions` | -| `test:integration:loopback:ci` | Run tests for specific scope or profile. | `bun run test:integration:loopback:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label loopback apps/backend-template/test/integration/LoopBack` | -| `test:integration:sails-js:ci` | Run tests for specific scope or profile. | `bun run test:integration:sails-js:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label sails-js apps/backend-template/test/integration/Sails-JS` | -| `test:integration:feathers:ci` | Run tests for specific scope or profile. | `bun run test:integration:feathers:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label feathers apps/backend-template/test/integration/Feathers` | -| `test:integration:derby-js:ci` | Run tests for specific scope or profile. | `bun run test:integration:derby-js:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label derby-js apps/backend-template/test/integration/Derby-JS` | | `test:integration:adonis-js:ci` | Run tests for specific scope or profile. | `bun run test:integration:adonis-js:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label adonis-js apps/backend-template/test/integration/Adonis-JS` | | `test:integration:total-js:ci` | Run tests for specific scope or profile. | `bun run test:integration:total-js:ci` | `JUMENTIX_TEST_RUNTIME=node NODE_ENV=dev bun ci-cd/run-suite.js --script-label total-js apps/backend-template/test/integration/Total-JS` | | `test:integration:mutex` | Run tests for specific scope or profile. | `bun run test:integration:mutex` | `NODE_ENV=dev RUN_REDIS_INTEGRATION=1 bun ci-cd/run-suite.js --script-label mutex apps/backend-template/test/integration/mutex` | @@ -277,16 +287,23 @@ bun run | `coverage:merge` | Use when you need this specific workspace operation. | `bun run coverage:merge` | `bun ci-cd/merge-coverage-reports.js` | | `workspace:test` | Validate workspace-level policies. | `bun run workspace:test` | `bun ci-cd/run-workspace-tests.js` | | `quarantine:flake` | Use when you need this specific workspace operation. | `bun run quarantine:flake` | `bun ci-cd/quarantine-flake.js` | -| `test:coverage` | Run tests for specific scope or profile. | `bun run test:coverage` | `NODE_ENV=dev bun x jest apps/backend-template/test/unit 'packages/[^/]+/test' --coverage --coverageThreshold='{}' --forceExit` | +| `test:coverage` | Run tests for specific scope or profile. | `bun run test:coverage` | `NODE_ENV=dev bun x jest apps/backend-template/test/unit apps/service-management/test/unit apps/service-management-api/test/unit ci-cd/test 'packages/[^/]+/test' --coverage --coverageThreshold='{}' --forceExit` | | `coverage:check` | Use when you need this specific workspace operation. | `bun run coverage:check` | `bun ci-cd/check-coverage-thresholds.js` | | `test:browser` | Run tests for specific scope or profile. | `bun run test:browser` | `bun packages/cana/scripts/run-browser-tests.js` | | `test:integration:key-value` | Run tests for specific scope or profile. | `bun run test:integration:key-value` | `bun apps/backend-template/scripts/run-redis-key-value-integration.js` | +| `test:integration:dead-letter` | Run tests for specific scope or profile. | `bun run test:integration:dead-letter` | `bun apps/backend-template/scripts/run-redis-dead-letter-integration.js` | | `smoke:key-value:redis` | Run smoke checks for fast environment validation. | `bun run smoke:key-value:redis` | `bun run docker:composeredis && bun run test:integration:key-value && docker compose -f "apps/backend-template/docker-compose-redis.yml" down --remove-orphans` | +| `smoke:dead-letter:redis` | Run smoke checks for fast environment validation. | `bun run smoke:dead-letter:redis` | `bun run docker:composeredis && bun run test:integration:dead-letter && docker compose -f "apps/backend-template/docker-compose-redis.yml" down --remove-orphans` | | `test:integration:message-mediator` | Run tests for specific scope or profile. | `bun run test:integration:message-mediator` | `NODE_ENV=dev RUN_BROKER_INTEGRATION=1 bun ci-cd/run-suite.js --script-label message-mediator --timeout 60000 packages/message-mediator/test/integration` | | `test:integration:db-repositories` | Run tests for specific scope or profile. | `bun run test:integration:db-repositories` | `JUMENTIX_TEST_RUNTIME=bun NODE_ENV=dev RUN_DB_REPOSITORIES_INTEGRATION=1 bun ci-cd/run-suite.js --script-label db-repositories --timeout 120000 packages/external-db-repositories/test/integration` | | `smoke:message-mediator` | Run smoke checks for fast environment validation. | `bun run smoke:message-mediator` | `bun run docker:composemessaging && bun run test:integration:message-mediator && docker compose -f "apps/backend-template/docker-compose-messaging.yml" down` | | `smoke:db-repositories` | Run smoke checks for fast environment validation. | `bun run smoke:db-repositories` | `bun run docker:up:cassandra && bun run docker:up:mongodb && bun run test:integration:db-repositories; status=$?; bun run docker:down:cassandra; bun run docker:down:mongodb; exit $status` | - -## Next step - -Return to [Getting started](/docs/jumentix/concepts/getting-started) or the [packages hub](/docs/jumentix/packages). +| `packages:check-build-freshness` | Use when you need this specific workspace operation. | `bun run packages:check-build-freshness` | `bun ci-cd/check-package-build-freshness.js` | +| `website:check-content-routes` | Operate the commercial website lifecycle. | `bun run website:check-content-routes` | `bun apps/jumentix-website/scripts/check-content-routes.js` | +| `rtdb:check-indexes` | Use when you need this specific workspace operation. | `bun run rtdb:check-indexes` | `bun packages/agent-registry/bin/check-rtdb-indexes.js` | +| `test:integrity` | Run tests for specific scope or profile. | `bun run test:integrity` | `bun ci-cd/check-test-integrity.js` | +| `rtdb:export-rules` | Use when you need this specific workspace operation. | `bun run rtdb:export-rules` | `bun ci-cd/export-database-rules.js` | +| `frontend:test:unit` | Use when you need this specific workspace operation. | `bun run frontend:test:unit` | `bun run --filter @jumentix/frontend test` | +| `frontend:test:coverage` | Use when you need this specific workspace operation. | `bun run frontend:test:coverage` | `bun run --filter @jumentix/frontend test:coverage` | +| `frontend:coverage:check` | Use when you need this specific workspace operation. | `bun run frontend:coverage:check` | `bun apps/frontend/scripts/check-coverage.js` | +| `frontend:test:e2e` | Use when you need this specific workspace operation. | `bun run frontend:test:e2e` | `bun run --filter @jumentix/frontend test:e2e` | diff --git a/apps/jumentix-website/content/jumentix/reference/service-management-cana-adoption.mdx b/apps/jumentix-website/content/jumentix/reference/service-management-cana-adoption.mdx deleted file mode 100644 index 1b1bf0078..000000000 --- a/apps/jumentix-website/content/jumentix/reference/service-management-cana-adoption.mdx +++ /dev/null @@ -1,453 +0,0 @@ ---- -title: "Service Management Cana Adoption" -description: "The single-store decision, the one-way migration and the offline state matrix." ---- - -# Service Management Cana Adoption, Migration and Offline Behaviour - -This is the E6 document of the Service Management E1–E8 documentation chain -([JUM-487](https://linear.app/jumentix/issue/JUM-487/docs-e6-documentation-cana-adoption-migration-and-offline-behavior)). -It documents Cana adoption from the **consumer's** side — what the designer -promises its users about their data — exactly as the code behaves today, after -the Cana-adoption lane -([JUM-483](https://linear.app/jumentix/issue/JUM-483/feature-canadesignerstore-idesignerstore-adapter-over-the-cana-client), -[JUM-484](https://linear.app/jumentix/issue/JUM-484/feature-one-way-migration-of-service-managementv1-from-localstorage-to), -[JUM-485](https://linear.app/jumentix/issue/JUM-485/feature-write-event-integration-multi-tab-sync-via-cana-message), -[JUM-486](https://linear.app/jumentix/issue/JUM-486/test-offlineonline-matrix-for-designer-persistence-on-cana)) -landed. - -Cana's own documentation -(CANA-INDEXEDDB-ADAPTER and -[CANA-USAGE-GUIDE](/docs/jumentix/packages/cana/usage), Cana -[JUM-418](https://linear.app/jumentix/issue/JUM-418/docs-document-cana-architecture-api-migration-and-offline-examples)) -documents what the database does. This document records what a user of the -designer can rely on, what can destroy their work, and what to do about it — -with every state naming both what is shown and what the user can do. - -Two deliberate boundaries: - -- **The storage schema is linked, not duplicated.** Keys and payload shape are - pinned by - Requirement 126, Contract 2; - this document references that contract and does not restate it. -- **The internals live in the sibling documents.** The port contract and the - module architecture belong to the E3 document, - [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture); - the parity guarantees (including the export-scope boundary) to the E4 - document, - [Service Management Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity); - the status-surface contract every message below is rendered through to the - E5 document, - [Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console). - -## The one fact everything else follows from: no fallback - -**Cana has no fallback to localStorage. No fallback at all** (decision -2026-07-29). Since -[JUM-484](https://linear.app/jumentix/issue/JUM-484/feature-one-way-migration-of-service-managementv1-from-localstorage-to)'s -one-way migration, the designer's data lives in Cana and nowhere else: the -transitional `LocalStorageDesignerStore` is retired and deleted, and no driver -argument, environment global or URL parameter can route the designer away from -Cana -(`designerStoreFactory.js`). -Every failure state below is therefore genuine — there is nothing behind the -store to catch the designer. - -Three things follow that a user should know without having to infer them: - -1. **Where your work lives.** In this browser, on this machine, in this - browser profile — an IndexedDB database named `service-management`, one - object store (`designerDocuments`), two documents under the pinned keys - `service-management.v1` and `service-management.schema-baseline.v1`. It is - **not synced** to any server (the application server serves the shell; it - never sees your design data), **not backed up by us**, and **not - recoverable by support**. Another browser, another profile, another machine - is another, empty, designer. -2. **How it can be lost.** Browser storage eviction (the browser reclaiming - origin storage under disk pressure), clearing site data, a - private/incognito session or blocked storage, an unsupported browser - without usable IndexedDB, quota exhaustion, and corruption. Each is named - plainly in the matrix below — none of them is a "degraded mode", because - there is no degraded mode: a store that cannot keep your data is a store - that has lost it. -3. **What to do about it.** **Export.** Under the no-fallback requirement, - the designer's export (and the one automatic backup the migration - produces) is the only recovery mechanism that exists — see - [Export and backup](#export-and-backup). - -Documentation that described Cana adoption as an improvement without stating -these loss modes would be accurate about the technology and misleading about -the product. The matrix below is the product truth, and it is the same matrix -[JUM-486](https://linear.app/jumentix/issue/JUM-486/test-offlineonline-matrix-for-designer-persistence-on-cana)'s -browser suite tests; the two must not drift. - -## Migration: one-way, verified, terminal (JUM-484) - -The migration of `service-management.v1` from localStorage to Cana runs at -boot, before any state load, on the first launch after the upgrade — and -exactly once -(`canaMigration.js`). -Because safety cannot come from retreat, it comes from construction: - -- **Backup before the first write.** Before anything is written to Cana, the - verbatim localStorage payload is downloaded as - `service-management-v1-backup-.json`. This file is the recourse - that replaces the retired fallback — keep it. -- **Verify before cutover.** The state payload (and the schema-diff baseline, - when one exists) is written through the storage port, read back, and - content-compared against the source. Only a verified migration records its - marker (`service-management.v1.cana-migration`, status `verified`) and a - provenance record in Cana (`service-management.migration.v1`, schema - version 1). -- **Failure is declared, never silent, and never destructive.** Any failure — - an unwritable store, a read-back mismatch — leaves the localStorage source - untouched and the migration re-runnable on the next launch. A source - payload that is not readable JSON cannot be migrated and is never deleted: - it stays in place for manual recovery, and the boot says so. -- **Idempotent.** The writes are `put`s of the same payload under the same - pinned keys; an interrupted migration re-runs to the identical result, and - a verified marker short-circuits re-entry — including across an offline - period (proven by the JUM-486 matrix). -- **30-day delayed source retention.** After a verified migration the - localStorage source stays in place, UNUSED, for 30 days — a manual recovery - path only, never a fallback: no code path reads it as a store. After the - retention period the boot removes it. The migration is terminal. -- **One-way means one-way.** There is no rollback. Once the migration has - verified, the designer reads and writes Cana exclusively, and no setting - can take it back. - -What the user sees: - -- On success, the status region announces: *"Your saved design was moved to - the new persistent store and verified. A backup was downloaded as - `service-management-v1-backup-.json`; the previous copy stays, - unused, for 30 days as a manual recovery path."* -- On failure, an error names the reason - (*"Your previously saved design could not be migrated: …"*) and the - migration retries on the next launch — the source is still there. -- The wire format did not change (Requirement 126 Contract 2): same keys, - same JSON documents — only where they live. A baseline that existed crosses - with the state; an absent baseline stays absent, never fabricated. - -**Proven by:** -`canaMigration.test.ts` -(unit) and -`canaMigration.browser.integration.test.ts` -(real browser, real IndexedDB), plus the migration-idempotence-offline cell -of the JUM-486 matrix. - -## Offline behaviour: everything works, and "offline" is not "safe" - -**Everything works with no network — the designer was always local.** The -model, the console tabs, undo/redo, import/export and every save run against -Cana in the browser; the server only serves the application shell and the -runtime-env/PM2 APIs. With the installable PWA shell -([JUM-489](https://linear.app/jumentix/issue/JUM-489)) the app even loads -with no network at all: the shell comes from the service-worker cache, the -data from Cana — two different stores that never impersonate each other (the -shell never masks an evicted database as a first run, and never presents -cached data of its own; see the -component README). - -What "offline-first" does **not** guarantee: - -- **No sync.** Offline means *no server is needed*, not *your data exists - anywhere else*. There is no account, no cloud copy, no cross-device or - cross-browser replication. -- **No backup.** Durability is the browser's storage policy, not ours. - "Clear site data" removes BOTH the shell cache and the Cana database — - the shell's presence never implies designer data is safe. -- **No immunity.** Eviction, quota exhaustion and corruption all happen - offline. Offline work is exactly as exposed as online work, and the answer - is the same: export. - -**Proven by:** the JUM-486 offline cells — the server is genuinely killed -(never an emulated offline flag), edits continue against the real IndexedDB, -a reload comes back from the shell cache with both the online and the -offline edits intact, and coming back online loses nothing and duplicates -nothing. - -## The state matrix: what you see, what you can do - -One matrix, three columns: the storage condition (from Cana's quota, -persistence, eviction and crash-recovery policies — Cana -[JUM-560](https://linear.app/jumentix/issue/JUM-560/feature-storage-quota-persistence-and-eviction-policy) -and -[JUM-411](https://linear.app/jumentix/issue/JUM-411/fix-implement-worker-crash-recovery-and-state-resynchronization)), -what the designer shows you, and what you can do. Every message is rendered -through the JUM-543 non-blocking status region — never a blocking alert. The -boot declares the environment states at startup, BEFORE you invest work; the -write-path states surface at the moment they happen. - -### Private/incognito or blocked storage — non-persisting session - -- **What you see** (at startup, severity error): *"Persistent storage is - unavailable in this browsing context (private/incognito mode, blocked - storage, or storage not yet wired into this host). The designer cannot save - your work: anything you build in this session will be lost when it ends."* - The designer still opens and is fully explorable — in memory only. If you - try to save, the failure is surfaced (*"A save could not be confirmed…"*), - never silently accepted; a reload loses the edit and the declaration - recurs. -- **What you can do:** export the in-memory model (Export JSON works without - storage) before closing, and redo the work in a normal window/profile where - storage is allowed. Nothing you build in this session can be recovered - afterwards. - -### A browser without usable IndexedDB — unsupported environment - -- **What you see** (at startup, severity error): *"This browser provides no - usable IndexedDB storage. The Service Management designer depends on it for - persistence, so this environment is unsupported: you can explore the - designer, but nothing you build here can be saved."* This is a distinct - state from the private-mode one above — not a blank screen, and not the - same message. -- **What you can do:** explore and export; do real work in a browser with - IndexedDB. - -### Persistence not granted — degraded durability - -- **What you see** (severity info): *"Storage is working but durability is - degraded: durability: storage is not persistent; the browser may reclaim it - under pressure."* Reads and writes work; the browser simply has not promised to - keep the data when disk runs tight, which makes eviction (below) more - likely. -- **What you can do:** keep working, but export regularly; where the browser - offers a persistence grant, allow it. - -### Quota near exhaustion — warning before failure - -- **What you see** (severity info): *"Storage is working but durability is - degraded: quota: storage usage is near the origin quota (usage/quota - bytes); writes may start failing."* The warning arrives BEFORE the hard - failure — reads still work, and the export path is reachable from the - warned session (proven by the JUM-486 quota cell, which exports - `domain-designer.json` from exactly this state). -- **What you can do:** export now, then free origin storage (browser - settings) before continuing. - -### Quota exhausted — the write did not happen - -- **What you see:** a write rejected for quota is reported as unconfirmed - (*"A save could not be confirmed (quota: …); reconciling with the stored - document."*) — never as success. Cana's taxonomy is explicit: a - quota-rejected write DID NOT happen; the durable record does not carry the - doomed edit, and a reload tells the same truth. -- **What you can do:** your edit is still on screen in this session — export - it before reloading, free storage, then redo the save. - -### Evicted database — data loss, declared - -- **What you see** (at startup, severity error): *"Previously saved designer - data is no longer readable (storage eviction or corruption) and there is no - fallback store. A fresh template was loaded instead; your only recourse is - a backup/export made earlier."* An evicted database and a first run are - indistinguishable by inspection; only Cana's tombstone verdict tells them - apart, so this state is NEVER presented as a first run — and a genuinely - fresh profile is never reported as data loss (both directions proven). -- **What you can do:** restore from an earlier export or migration backup - (Import JSON). There is no other recourse — no fallback store, no server - copy, no support recovery. - -### Corrupted record — lost, not empty, announced, and the designer recovers - -- **What you see** (at startup, severity error): *"Your previously saved - design could not be loaded: the stored data is corrupted and there is no - fallback store, so a fresh template was loaded instead and the saved model - was lost. Your recourse is a backup/export made earlier — restore it with - Import JSON."* A stored payload that no longer parses reports `'lost'` - through the port — never `'empty'` — and the designer recovers instead of - crashing: the seed template loads and the recovered save makes the record - readable again. The probe-time environment states cannot see an unreadable - record (only eviction), so the loss is declared at load time through the - same `data-lost` state and the same status region - ([JUM-626](https://linear.app/jumentix/issue/JUM-626/fix-announce-load-time-storage-corruption-recovery-in-the-boot-ui)). - When the verified pre-migration copy is still retained in localStorage - (its 30-day window, JUM-484), the message names it — the pinned key and the - retention date — as the first recourse. -- **What you can do:** restore from an earlier export or migration backup - (Import JSON); when the message names the retained pre-migration copy, copy - it out before its retention date and import that. - -### Unknown write outcome — worker crash after dispatch - -- **What you see:** *"A save could not be confirmed (unknown-outcome: …); - reconciling with the stored document."* When Cana reports a write's outcome - as unknown (a storage worker that died after the write was dispatched, Cana - JUM-411), the designer never assumes success: it reads the stored document - back. A read-back matching the attempted payload confirms the save - (*"The save was confirmed after reconciliation."*); anything else reloads - the last confirmed state into the designer, so the screen never diverges - from what is durable. If the read-back itself fails, the message says to - export immediately — and means it. -- **What you can do:** nothing, in the common case — the reconciliation is - automatic. On the failure message: export now. - -**Proven by:** the eight cells of -`offlinePersistenceMatrix.browser.integration.test.ts` -(the JUM-486 matrix — offline persistence, offline migration idempotence, -crash classification, private/blocked storage, missing IndexedDB, eviction, -corruption, quota warning-then-failure), run in a real WebKit browser against -the real server and the real vendored Cana bundle, plus -`canaDesignerStore.test.ts` -(the error-taxonomy → port mapping) at unit level. The matrix and this -section are the same promise; change one, change both. - -## Multi-tab behaviour (JUM-485) - -Open the designer in two tabs of the same browser profile and both tabs stay -consistent: each tab holds its own Cana client over the same database, and -committed writes cross between tabs over a `BroadcastChannel` bridge — -Cana's ordered write events (`CanaClient.subscribe`, Cana JUM-413) reach only -the subscribing client instance, so the channel is the cross-tab boundary -(`designerSync.js`). -The recorded semantics: - -- **Undo is local-only; remote changes are not undoable.** A change from - another tab never enters your undo stack, and it truncates your redo - branch. Undoing one of YOUR actions after a remote change restores your - snapshot and persists it as a new, deliberate local write — it is never an - undo OF the remote change. -- **A pending local edit survives a remote change.** When another tab's - change touches what you are editing, the committed document wins (Cana - holds the truth), but your mid-form input, focus, caret and canvas - scroll/zoom are preserved, the status region announces the remote change, - and your next explicit save asserts your version. Neither your pending edit - nor the remote change is silently dropped. Conflict resolution is - whole-document last-writer-wins — there is no field-level merge. -- **The selection is per-tab and reconciled, never imported.** If another tab - deletes the relationship or entity you have selected, your selection - clears; if it deletes your selected domain, the selection moves to the - first remaining domain. Every reconciliation is announced — a dangling - selection is impossible and never silent. -- **Backgrounded and closed tabs catch up without loss or duplication.** A - frozen tab that missed channel messages resynchronises by document - read-back when it becomes visible; a tab reopened later already loads the - current document at boot. The persisted event cursor is best-effort - bookkeeping only — losing it just means the next start resyncs by document, - which is always correct (see the follow-ups section). -- **An unavailable channel is declared, not hidden.** Without a usable - `BroadcastChannel` the designer still saves to Cana, but the status region - says plainly that this tab will not see other tabs' changes until reload — - the designer never quietly reverts to a single-tab local session. - -**Proven by:** -`designerSync.test.ts` -(unit) and -`multiTabSync.browser.integration.test.ts` -(real two-tab browser contexts). - -## Export and backup - -Under no-fallback, export is the only recovery mechanism that exists — so it -must be obvious, not merely available. - -- **How to export:** the Domain Designer toolbar's **Export JSON** button - downloads `domain-designer.json` — the full-suite document (JUM-547: - `{ kind: "service-management-suite", version: "2.0.0", domains, relationships, interfaces, serviceConfiguration, runtimeEnvironment, codeWorkspace, deployments, view }`). **Import JSON** on the same toolbar restores it. The - other export buttons (Markdown, JSON Schema, OAS 3.1, AsyncAPI, gRPC proto, - boilerplate bundle, domain package) are design artifacts for downstream - tooling, not backups. -- **The export's scope, honestly:** the JSON export carries all five tabs of - the suite state — the domain model, the interface adapters, the service - configuration, the generated-code workspace and the deploy targets — with one recorded boundary: the - runtime environment crosses as the environment *selection* only - (`environment`, `fileName`), never its values, so no machine configuration - (and no secret) leaves in a bundle; import restores the selection and keeps - the local machine's values. Bundles exported before JUM-547 (the domain-only - shape) still import cleanly, with the missing sections defaulted; a bundle - with an unknown section or a newer major version is refused clearly rather - than half-imported. The details and their proof live in the E4 document, - [Service Management Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity). -- **The migration backup is the one full-fidelity copy.** The - `service-management-v1-backup-.json` the migration downloads is - the verbatim `service-management.v1` payload — every section. Keep it: it - is the only automatic backup the designer ever makes. (Since JUM-547 the UI - import reads every section back — including the runtime environment values - the backup carries, which a bundle never does — so it doubles as a - full-fidelity restore path.) -- **When to export:** before clearing site data or switching - browser/profile/machine; the moment a quota or durability warning appears; - before and after a large redesign session; and periodically on any project - you could not afford to rebuild. After an eviction or corruption message, - an earlier export is your only way back. -- **Why it matters more here:** in a system with a fallback, export is a - convenience. Here it is the difference between an inconvenience and a - total, unrecoverable loss — the browser owes your data nothing. - -## Troubleshooting - -- **"My design disappeared and I got a data-lost message."** That is an - eviction (or corruption) declaration, not a first run: the designer had - data, the browser's storage no longer does, and there is no fallback. - Restore your latest export with Import JSON. To make recurrence less - likely, keep the origin's storage pressure low and grant persistence when - the browser offers it. -- **"My design disappeared with NO message."** If this is a new browser, a - new profile, a new machine, or after clearing site data, this is a first - run — your data was never here, because it never leaves the browser profile - it was created in. The two situations are deliberately distinguishable: a - genuinely fresh start shows the seed template silently; a real loss is - announced. -- **"Everything works in a normal window but vanishes in private mode."** - Expected: the private window is a declared non-persisting session. Work - done there does not carry over — export it before closing if you need it. -- **"I got an unsupported-environment message."** This browser provides no - usable IndexedDB. The designer is explorable, but nothing can be saved; use - a browser with IndexedDB. -- **"A save could not be confirmed."** Read the reason in the message: - `quota:` means the origin is full (export, free space, retry); - `unknown-outcome:` means the outcome was indeterminate and the designer has - already reconciled by read-back; `unavailable:` means the storage - environment itself is gone (see the first three rows of the matrix). - -## Known follow-ups - -Recorded honestly, with their owning issues: - -- **The Cana bundle bundling defect — belongs to the Cana lane.** The - designer's vendored Cana bundle is built from `packages/cana`'s - `adapter.ts` entry rather than the package index, because bun's full-graph - bundling of the index emits dangling export bindings WebKit refuses to - link. The artifact check in - `apps/service-management/scripts/sync-service-management-cana-bundle.js` - fails closed against exactly that regression until the bundler defect is - fixed upstream. -- **Multi-tab cursor persistence is best-effort.** The sync cursor survives - an in-session gap but not a page reload (a fresh client restarts its - retained event window), and losing it is safe by construction — catch-up is - always by document read-back. A more durable cursor is a possible - refinement, not a correctness gap. - -## What this document deliberately does not cover - -- **The storage schema itself** — keys, sections and payload shapes are - pinned by - Requirement 126, Contract 2; - this document links it rather than duplicating it. -- **The port contract and module internals** — the `IDesignerStore` state - sets, the adapter's error-taxonomy mapping and the migration module's - construction belong to the E3 document, - [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture). -- **Parity guarantees** — including the full-suite export scope and the - JUM-547 `runtimeEnvironment` decision — belong to the E4 document, - [Service Management Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity). -- **The status-surface contract** — how messages are rendered (aria-live - region, severities, no `alert()`) belongs to the E5 document, - [Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console). -- **What the Cana engine itself guarantees** — its durability policy, error - taxonomy, transaction outcomes and subscription API are documented by Cana: - CANA-INDEXEDDB-ADAPTER and - [CANA-USAGE-GUIDE](/docs/jumentix/packages/cana/usage) - ([JUM-418](https://linear.app/jumentix/issue/JUM-418/docs-document-cana-architecture-api-migration-and-offline-examples)). - -## References - -- Migration + environment states: `canaMigration.js`; Cana adapter: `apps/service-management/src/store/CanaDesignerStore.js`; factory: `designerStoreFactory.js` -- Multi-tab sync engine: `designerSync.js`; state core: `packages/designer-core/src/state/designerState.js`; boot wiring and export/import glue: `apps/service-management/script.js` -- Vendored Cana bundle sync: `apps/service-management/scripts/sync-service-management-cana-bundle.js` -- Suites: `canaMigration.test.ts`, `canaDesignerStore.test.ts`, `designerSync.test.ts` (unit); `canaMigration.browser.integration.test.ts`, `multiTabSync.browser.integration.test.ts` (browser); the JUM-486 offline/online matrix `offlinePersistenceMatrix.browser.integration.test.ts` (browser, [JUM-486](https://linear.app/jumentix/issue/JUM-486/test-offlineonline-matrix-for-designer-persistence-on-cana)) -- Storage schema: Requirement 126, Contract 2; bilingual parity: Requirement 076 -- Cana engine documentation: CANA-INDEXEDDB-ADAPTER, [CANA-USAGE-GUIDE](/docs/jumentix/packages/cana/usage) -- Sibling E-chain documents: [Runtime Environment Contracts](/docs/jumentix/reference/service-management-runtime-environment) (E1), [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture) (E3), [Service Management Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity) (E4), [Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console) (E5), Service Management Application, [Domain Designer Features and Usage](/docs/jumentix/reference/domain-designer-features) -- Linear: [JUM-483](https://linear.app/jumentix/issue/JUM-483/feature-canadesignerstore-idesignerstore-adapter-over-the-cana-client), [JUM-484](https://linear.app/jumentix/issue/JUM-484/feature-one-way-migration-of-service-managementv1-from-localstorage-to), [JUM-485](https://linear.app/jumentix/issue/JUM-485/feature-write-event-integration-multi-tab-sync-via-cana-message), [JUM-486](https://linear.app/jumentix/issue/JUM-486/test-offlineonline-matrix-for-designer-persistence-on-cana), [JUM-547](https://linear.app/jumentix/issue/JUM-547/feature-full-suite-exportimport-carry-interfaces-service-configuration), [JUM-626](https://linear.app/jumentix/issue/JUM-626/fix-announce-load-time-storage-corruption-recovery-in-the-boot-ui), Cana [JUM-418](https://linear.app/jumentix/issue/JUM-418/docs-document-cana-architecture-api-migration-and-offline-examples), [JUM-560](https://linear.app/jumentix/issue/JUM-560/feature-storage-quota-persistence-and-eviction-policy), [JUM-411](https://linear.app/jumentix/issue/JUM-411/fix-implement-worker-crash-recovery-and-state-resynchronization), [JUM-415](https://linear.app/jumentix/issue/JUM-415/feature-define-offline-conflicts-migrations-and-data-durability-policy), [JUM-413](https://linear.app/jumentix/issue/JUM-413) diff --git a/apps/jumentix-website/content/jumentix/reference/service-management-collaboration-packaging.mdx b/apps/jumentix-website/content/jumentix/reference/service-management-collaboration-packaging.mdx deleted file mode 100644 index 1b8970c19..000000000 --- a/apps/jumentix-website/content/jumentix/reference/service-management-collaboration-packaging.mdx +++ /dev/null @@ -1,490 +0,0 @@ ---- -title: "Service Management Collaboration and Packaging" -description: "The shared catalog, domain-package versioning and the designer-core packaging boundary." ---- - -# Service Management Collaboration and Packaging - -This is the E8 document of the Service Management E1–E8 documentation chain -([JUM-494](https://linear.app/jumentix/issue/JUM-494/docs-e8-documentation-collaboration-and-packaging)) -— the **last link of the chain and the epic's terminal documentation gate -under -Requirement 094**: -the Linear Project cannot be set to `Completed` while this Issue is in any -state other than completed. - -It documents the collaboration and packaging lane exactly as it shipped: - -- **Collaboration** — the multi-user shared catalog - ([JUM-491](https://linear.app/jumentix/issue/JUM-491/feature-multi-user-shared-catalog-backend-sync-over-cana-resync-events)): - a contract-first `Catalogs` backend module, optimistic concurrency per - catalog record, tombstone deletion with restore, and a designer-side sync - client that converges by document read-back. -- **Domain-package versioning** - ([JUM-492](https://linear.app/jumentix/issue/JUM-492/feature-domain-package-versioning-with-semantic-conflict-resolution)): - exported domain packages are versioned data with a dependency graph and - deterministic, explainable conflict resolution on import. -- **Packaging** - ([JUM-493](https://linear.app/jumentix/issue/JUM-493/feature-publish-designer-core-as-jumentix-package-xpertminds-org-dry)): - the framework-free designer core is the versioned - `@jumentix/designer-core` package under - `packages/designer-core/` — manifest, - deterministic build, type declarations, DOM-free proof and a consumer - smoke test against the built artifact. Publishing is **dry-run only** - (Requirement 070): the publish path is verified, never fired. - -Because this is the terminal gate, its content reflects **what was -delivered**, including everything carried over — each descope below is named, -not silently dropped. The chain-closure section at the end is the Req 094 -evidence: it names the E1–E8 chain completely and records its gate state. - -This document is the English reference. A versão em português está em -[SERVICE-MANAGEMENT-COLLABORATION-PACKAGING.pt-BR.md](/docs/pt-BR/jumentix/reference/service-management-collaboration-packaging). - -## Collaboration: the shared catalog (JUM-491) - -Everything before JUM-491 treats the designer as a **single-browser tool**: -Cana persists the designer document in the browser's IndexedDB, and JUM-485 -synchronizes it across that browser's tabs. JUM-491 turns the designer into -a **multi-user system**: a team shares one catalog of domain designs through -the backend — also the only continuous second copy of a user's work on -different hardware (Cana has no fallback; export is manual). - -The full mechanism is owned by the dedicated -Shared Catalog Sync document (architecture, OAS -contract, authorization model, convergence proof, verification commands). -This document states what the delivery means for the epic — the concurrency -unit, the rejection path, the deletion semantics and the convergence rule — -once, and defers to it rather than maintaining a second, drifting -explanation. - -### What shipped - -- **A contract-first `Catalogs` backend module** - (`apps/service-management-api/src/modules/Catalogs/domain/Model/Catalog.ts`), - hexagonal like the Users reference module: domain aggregate (version bump, - tombstone, restore), pure - `CatalogAuthorizationPolicy`, - use cases - (`CatalogUseCases.ts`), - the optimistic-concurrency enforcement point - (`CatalogDataRepository.ts`), - the OAS-validated - `CatalogController` - and composition - (`composeCatalogsServices.ts`). - Six operations on `/catalogs` in the canonical - `spec/1.0.0.yml` — list, create, get, update, - delete, restore — enforced by `bun run oas:check-routes`. -- **Optimistic concurrency per catalog record.** The record (one shared - domain design) is the declared concurrency unit: coarse enough that a - relationship spanning two entities always has a consistent version to - check, fine enough that teammates never block each other across domains. - The server-managed `version` token starts at 1 on create and bumps on - every write; a stale write is rejected with a **reviewable 409** whose - metadata carries `catalogId`, `expectedVersion`, `currentVersion` **and - the current record** — the loser's edit is never discarded. -- **Deletion is a tombstone, recoverable.** Delete sets `deletedAt` and - bumps the version so the deletion propagates on read-back; a locally-dirty - copy raises a `deleted-remotely` conflict instead of vanishing; - `POST /catalogs/{id}/restore` recovers the record. -- **Server-side authorization, TENANT-RBAC aligned.** The role matrix gains - catalog scopes (`admin`: read/create/update/delete; `user`: - read/create/update — team members edit, only admins delete), and the - tenant policy binds the catalog to exactly one organization. A client - cannot grant itself access — proven by positive/negative integration - tests. See - Tenant and RBAC Authorization Contract. -- **Mediator events.** Every successful write publishes - `catalogs.catalog.created | updated | deleted | restored` with - `{ id, organization, version, actor }` — the same version token the API - enforces — on the message mediator - (`CatalogService.ts`); - publication never breaks the primary write. -- **A DOM-free designer sync client** - (`apps/service-management/src/state/catalogSyncClient.js`): - a sibling consumer of the same Cana committed-event stream that JUM-485's - tab sync subscribes to. Local commits schedule a debounced push of dirty - shared domains (dirtiness decided by the durable marker - `domain.context.catalog = { id, version, contentHash }`, carried - additively per JUM-492's pattern); convergence is a polled **document - read-back** diffed by `(id, version)` — Cana's resync rule applied across - the network: a gap is a reload signal, never an event replay, because Cana - cursors are per client instance and mean nothing across machines. Remote - changes cross the same `applyRemoteDocument` one-path as tab sync, so undo - isolation, selection reconciliation and redo truncation are identical. -- **A real convergence proof.** - `catalogSync.integration.test.ts` - boots the real Express backend (real JWT auth, real mediator) and runs two - real designer clients over real `fetch`; one is partitioned behind a real - `ECONNREFUSED`, both keep editing, and on heal the read-back converges - them — the partitioned edit survives as a reviewable conflict, resolved - explicitly (`take-server` / `take-local`, where `take-local` is a - deliberate new write against the server's current version, never a blind - overwrite). - -### What JUM-491 deliberately descoped (12-01 carry-over candidates) - -Named in -Shared Catalog Sync -and restated here because the gate must see them: - -- **WebSocket fan-out to browsers** (push instead of poll): the broker - adapters exist in `packages/message-mediator`, but no fan-out to designer - clients is wired; poll-based read-back is correct under any broker choice. -- **Designer share/conflict UI chrome**: the client module is DOM-free and - composable; the share/unshare/conflict surfaces (and the token-provider - UX) are a follow-up. -- **Driver-level native conditional writes**: the repository's - read-check-write is the reference behavior; production drivers should later - map the same check into `IStoreMutationOptions.expectedVersion` native - conditionals. -- **Offline deletion-intent queue**: an offline local deletion of a shared - domain cannot be pushed; the surviving server record is re-admitted on - read-back — the "committed document wins" boundary, session-scoped by - design in this slice. - -### What this changes for the user — and what it does not - -Before JUM-491, the designer was per-browser and **export was the only way -work left the machine** — the E6 document, -[Service Management Cana Adoption, Migration and Offline Behaviour](/docs/jumentix/reference/service-management-cana-adoption), -owns that data story and this document does not restate it. The shared -catalog adds the missing lane: work a user *shares* now lives on the server -and converges across machines. Two honest boundaries remain, stated up -front: - -- **A sync target is not a backup.** It propagates deletions; it does not - replace the storage-quota/eviction policy or the offline durability - contract. -- **Cana still has no fallback.** When the catalog is unreachable the client - declares it (`degraded` on the status surface) and keeps saving locally to - Cana — it never silently degrades into a hidden single-user mode. - -## Domain-package versioning (JUM-492) - -A domain package (the `-package.json` export) is **versioned data, -not code**, and import is a deterministic policy instead of an unconditional -append. The public contract is pinned in -Requirement 126, Contract 3; -the implementation lives in -`src/packages/packageVersioning.js` -(DOM-free), wired through the exporter -(`buildDomainPackageDocument`) -and the importer -(`designerImporters.js`). - -### The versioned package document - -Export emits the v2 shape `{ kind: "domain-package", version: "2.0.0", exportedAt, package: { name, version, dependencies: [{ name, range }] }, domain }`. The `package` block declares identity from the domain's context: -`packageName` (falling back to the domain name), `packageVersion` (falling -back to `1.0.0`) and `packageDependencies` entries (`name@range`; a bare -name is a presence-only dependency). Compatibility is explicit in both -directions: legacy v1 documents keep importing with a synthesized `1.0.0` -identity; a document whose major is newer than the importer's, or a `kind` -other than `domain-package`, is refused clearly. - -### Version semantics, redefined for a data model - -The usual semver meanings do not map onto a data model, so Requirement 126 -redefines them: - -- **patch** — documentation/metadata only (field descriptions, formats, - constraints, domain context text, OAS composition hints); -- **minor** — additive structure (a new entity, field or message contract; a - required flag loosened); -- **major** — removal or narrowing (a removed entity/field/contract, a field - type or PK/FK/unique change, a required flag tightened, an RBAC or - invariant change, an aggregate declaration change). - -### Provenance and the installed-package registry - -Imported content is stamped: the domain carries `context.provenance = { package, version }` plus `context.packageName`/`context.packageVersion`, -and every imported entity carries `meta.provenance`. The normalizers carry -these fields **additively** — only when the source declares them — so -pre-JUM-492 payloads are unchanged and provenance crosses storage, loads and -the full-suite export intact. The installed-package registry derives from -provenance **only**: a hand-built domain is never an installation, so -importing a package named like a locally-built domain appends (with the -JUM-617 id recomputation) instead of merging into unrelated content. - -### The dependency graph - -Dependencies resolve transitively over the registry with the incoming -package overlaid. Ranges follow the npm convention: `*`/empty (any), exact -`1.2.3`, caret `^1.2.3` (same major; for `0.x`, same minor), tilde `~1.2.3` -(same major.minor); anything else is invalid and satisfies nothing, so it is -reported rather than silently accepted. A missing or range-incompatible -dependency is **reported** through the status region and the import proceeds -— the designer reports, it is not the resolver. A cycle the incoming package -would close is reported by name chain and the import is **refused** — cycles -are detected, never entered. - -### Semantic conflict resolution - -Re-importing an installed package resolves deterministically and -explainably: - -- **same version + equal content → no-op** — idempotent re-import, proven by - test; -- **same version + different content → refused** (`same-version-conflict`, - divergences listed — version immutability); -- **older version → refused** (`downgrade-rejected`); -- **newer version → merge.** Additive and metadata changes (patch/minor - semantics — `AUTO_MERGE_CLASSES`) apply automatically. Removals, - narrowings and **always RBAC and invariants** - (`REQUIRES_DECISION_CLASSES`) keep the existing designer content and are - listed in the **merge preview**, rendered on the schema-diff surface - before anything changes; the merge applies only after the user explicitly - accepts (a gated `window.confirm` — one of the destructive-action gates - JUM-543 deliberately keeps). RBAC and invariants are always in the - decision class: automatically resolving a security policy or a domain - invariant is a decision a merge algorithm must not make. All outcomes - surface through `showStatus`, never `alert()`. - -Entity matching inside a merge is by name, never by id: colliding ids are -recomputed at import on the -[JUM-617](https://linear.app/jumentix/issue/JUM-617/fix-importdomainpackage-recompute-domainentity-ids-on-re-import) -rule, so ids can never be the match key; new incoming entities receive -collision-free ids through the importer's `uniqueId` callback. - -**Proven by:** -`designerPackageVersioning.test.ts` -(version parsing/ordering, range satisfaction, dependency parsing and -transitive resolution, cycle detection, every conflict class), -`designerRoundTrip.test.ts` -(versioned export→import deep-equal with provenance, re-export fixed point, -idempotent re-import, conflicting re-import refusal, deterministic merge -with RBAC kept, compatible/incompatible dependency pairs, JUM-617 preserved -on the append path) and -`designerExporters.test.ts` -(the v2 package document shape, pinned). - -## Packaging: the `@jumentix` designer core (JUM-493) - -**This lane shipped as a package with a verified dry run — never an automatic -publish.** This section previously recorded JUM-493 as descoped (the 12-01 -decision); the package has since landed, and this document records the landed -state. The framework-free designer core is now the versioned -`packages/designer-core/` package under the -the `@jumentix` npm scope — browser-safe ESM, zero runtime dependencies, MIT -licensed, with provenance metadata pointing at its monorepo location. - -- **The package is the canonical home, not a copy.** The core modules moved - from `apps/service-management/src/` into - `packages/designer-core/src/` — the - workspace boundary gate forbids a package importing from an app, so the - dependency direction was inverted: the zero-build SPA now consumes the - package through `@jumentix/designer-core/…` bare specifiers, resolved by - the import map to a vendored tree - (`apps/service-management/vendor/designer-core/`, containment-safe, synced - by - `apps/service-management/scripts/sync-service-management-designer-core.js` - — the same vendoring model as the Cana bundle) in the browser, and by the - repo's path mappings (tsconfig `paths`, Jest `moduleNameMapper`) straight - to the canonical sources in tests. The shipped surface — the domain model - and its normalizers, the validation/model-check engine, the exporters - (JSON, Markdown, JSON Schema, AsyncAPI, boilerplate bundle, package, OAS), - the importers, the schema-diff/merge-preview engine, the hexagonal codegen, - and `IDesignerStore` as a type/contract only — is exactly the package's - `src/` tree; the build - (`scripts/build.js`) - copies it verbatim into `dist/` and generates type declarations from the - JSDoc-annotated sources with the repo-pinned TypeScript compiler. **Out**, - enforced by test: every DOM module (`script.js`, `ui/`, `pwa/`), the sync - clients (`state/designerSync.js`, `state/catalogSyncClient.js`) and the - storage adapters — neither `LocalStorageDesignerStore` nor - `CanaDesignerStore` ships, and the package has no dependency on Cana. -- **The acceptance bar is met by proof, not by construction.** Three suites - under `packages/designer-core/test/` - pin the package: `packaging.test.ts` asserts the manifest (entry points at - built output, types-first exports map, `files`, licence, `sideEffects`, - dry-run-only scripts, in the style of the cana packaging suite), asserts - the built file set is *exactly* the declared closure, and asserts the - packed tarball contents via `npm pack --dry-run --json`; - `dom-free.test.ts` scans the built artifact's AST for any `window`, - `document`, `localStorage`, `indexedDB`, `alert()` or FileReader/DOMParser - reference and for any import crossing the package boundary; - `consumer-smoke.test.ts` executes the issue's acceptance test — it imports - the built artifact in a **separate non-DOM process** (no `document`, no - `window`, no `localStorage`) and runs a validate → export → re-import - round trip on the sample model, deep-equal with a re-export fixed point. -- **The publish policy is manually approved.** Per - Requirement 070 - no automatic publish exists. `bun run npm:packages:check` rebuilds the - approved public cohort, inspects each tarball, and imports it in an external - consumer. Publication is then a manual `main` workflow protected by the - `npm-publish` environment; `prepublishOnly` forces a clean rebuild before - npm assembles the release tarball. -- **Versioning policy.** The package follows semver over its public barrel: - patch for internal fixes, minor for additive exports, major for removed or - narrowed surface. The *data* contracts it reads and writes (full-suite - export, domain-package document) stay versioned in-payload under JUM-492's - policy (Requirement 126, Contract 3) — the package version does not restate - them. JUM-492's domain-package versioning builds on exactly this split. - -The practical consequence for the user is unchanged from the E6 document: -**export is how work leaves the machine** — as the full-suite document or as -a versioned domain package — and the shared catalog (above) is the only -continuous second copy. The package changes who can *depend on* the core, not -how a designer user's work is stored. - -### The full-suite export (JUM-547), the portable bundle - -The JSON export is the versioned full-suite document -(`{ kind: "service-management-suite", version: "2.0.0", domains, relationships, interfaces, serviceConfiguration, runtimeEnvironment, codeWorkspace, deployments, view }`) carrying **all five tabs** in a re-importable shape -([JUM-547](https://linear.app/jumentix/issue/JUM-547/feature-full-suite-exportimport-carry-interfaces-service-configuration)). -One recorded security decision matters for packaging: the bundle carries the -runtime environment **selection only** (`{ environment, fileName }`) — -**never values**, because the values mirror real `.env` contents of the -machine the designer runs on. No secret can leave in a bundle; on import the -selection is restored and the local machine's values are preserved. The -contract is pinned in Requirement 126, Contract 3; the E5 document, -[Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console), -owns the operations-console side of the runtime-environment story. - -## Chain closure: the E1–E8 gate evidence (Req 094) - -This section is the epic's documentation-gate evidence: the chain named -completely, each link's state, and the ownership boundaries that keep two -documents from drifting over the same behaviour. - -**The published chain.** The chain is named E1–E8; the project published -**seven** dedicated documentation issues — E1 and E3 through E8. **No E2 -documentation issue exists in the project** (an audit of the project's issue -list confirms none was ever created), so the gate closes on the seven -published documents: - -| Link | Issue | Document | State | -|---|---|---|---| -| E1 | [JUM-464](https://linear.app/jumentix/issue/JUM-464/docs-e1-documentation-enpt-runtime-env-contract-and-fixed-paths) | [Runtime Environment Contracts](/docs/jumentix/reference/service-management-runtime-environment) | Done | -| E3 | [JUM-473](https://linear.app/jumentix/issue/JUM-473/docs-e3-documentation-module-architecture-and-storage-port-contract) | [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture) | Done | -| E4 | [JUM-479](https://linear.app/jumentix/issue/JUM-479/docs-e4-documentation-contract-parity-guarantees) | [Service Management Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity) | Done | -| E5 | [JUM-482](https://linear.app/jumentix/issue/JUM-482/docs-e5-documentation-operations-console) | [Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console) | Done | -| E6 | [JUM-487](https://linear.app/jumentix/issue/JUM-487/docs-e6-documentation-cana-adoption-migration-and-offline-behavior) | [Service Management Cana Adoption, Migration and Offline Behaviour](/docs/jumentix/reference/service-management-cana-adoption) | Done | -| E7 | [JUM-490](https://linear.app/jumentix/issue/JUM-490/docs-e7-documentation-design-system-and-pwa-shell) | [Service Management Design System and PWA Shell](/docs/jumentix/reference/service-management-design-system-pwa) | Done | -| E8 | [JUM-494](https://linear.app/jumentix/issue/JUM-494/docs-e8-documentation-collaboration-and-packaging) | this document | this PR | - -Every link is published in EN and PT-BR, synchronized per -Requirement 076 -— neither artifact is a stub. - -**Consistency: one owner per behaviour, the others link.** The chain was -audited for duplicated, drifting description; where two documents touch the -same behaviour, the ownership is: - -- **The data story** (where work lives, the one-way migration, offline data - behaviour, export as the only recovery path) — owned by **E6**; E7 states - the shell↔data boundary once and links, and this document links for the - per-browser baseline the shared catalog extends. -- **The storage port contract and the DOM-free module boundary** — owned by - **E3**; this document links for the packaging boundary instead of - re-deriving it. -- **The export contract and the domain-package contract** — pinned by - **Requirement 126** (Contract 3), with the parity guarantees owned by - **E4**; this document teaches the JUM-492 behaviour and cites the contract - rather than restating it. -- **The env-file/enum semantics and the runtime-env API** — owned by **E1**; - **E5** owns the operations-console surfaces that consume them, including - the status-surface contract every document above references. -- **The shared-catalog mechanism** — owned by - Shared Catalog Sync (JUM-491's dedicated - document); this document states the delivery's meaning for the epic and - links. - -**Gate state, exactly.** Under Req 094 the Project cannot be set to -`Completed` until this Issue is completed. At this PR: E1, E3–E7 are Done; -E8 is delivered by this PR and transitions only after it merges. No pending -check is described as passing here: the JUM-491 artifacts this document -links (`SHARED-CATALOG-SYNC.md`, `catalogSyncClient.js`, the `Catalogs` -module, the convergence test) land with their own PR, and the JUM-493 -packaging lane has since shipped as `@jumentix/designer-core` — dry-run -only, per the section above — both stated, not smoothed -over. Project-completion evidence per Req 094 (linking this Issue, its PR -and commit evidence, and the documentation-integrity validation results) is -recorded in the epic's Project Updates feed per -Requirement 102 -when the gate closes. - -## What this document deliberately does not cover - -- **The shared-catalog mechanism in full** — owned by - Shared Catalog Sync: the OAS contract table, - the authorization decision matrix, the sync client API and the - verification commands. -- **The per-browser data story** — owned by the E6 document, - [Service Management Cana Adoption, Migration and Offline Behaviour](/docs/jumentix/reference/service-management-cana-adoption); - this document references it for the baseline the shared catalog extends. -- **The storage port and module boundary** — owned by the E3 document, - [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture). -- **The export parity guarantees and the full contract text** — owned by the - E4 document, - [Service Management Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity), - and pinned by - Requirement 126, Contract 3. -- **The operations console** (Service Configuration, the runtime-environment - editor, PM2 preview, Deploy Management) — owned by the E5 document, - [Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console). - -## References - -- Collaboration (JUM-491): - `apps/service-management-api/src/modules/Catalogs/domain/Model/Catalog.ts`, - `CatalogAuthorizationPolicy`, - `CatalogUseCases.ts`, - `CatalogService.ts`, - `CatalogDataRepository.ts`, - `CatalogController`, - `composeCatalogsServices.ts`, - `spec/1.0.0.yml`, - `apps/service-management/src/state/catalogSyncClient.js`, - Shared Catalog Sync -- Domain-package versioning (JUM-492): - `src/packages/packageVersioning.js`, - `buildDomainPackageDocument`, - `designerImporters.js` -- Packaging (JUM-493): - `packages/designer-core/` - (manifest, - barrel, - `scripts/build.js`, - [README](/docs/jumentix/packages/designer-core)), - with suites - `packaging.test.ts`, - `dom-free.test.ts` and - `consumer-smoke.test.ts`; - the source of truth remains the DOM-free boundary under - `apps/service-management/src/` -- Suites: - `catalogSyncClient.test.ts`, - `catalogSync.integration.test.ts`, - `designerPackageVersioning.test.ts`, - `designerRoundTrip.test.ts`, - `designerExporters.test.ts` -- Requirements: - Requirement 094 - (epic documentation completion gate), - Requirement 076 - (EN/PT parity), - Requirement 070 - (dry-run-only publish), - Requirement 102 - (project updates), - Requirement 126, Contract 3 - (ownership and public contracts, Contract 3) -- Sibling E-chain documents: - [Runtime Environment Contracts](/docs/jumentix/reference/service-management-runtime-environment) (E1), - [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture) (E3), - [Service Management Contract Parity Guarantees](/docs/jumentix/reference/service-management-contract-parity) (E4), - [Service Management Operations Console](/docs/jumentix/reference/service-management-operations-console) (E5), - [Service Management Cana Adoption, Migration and Offline Behaviour](/docs/jumentix/reference/service-management-cana-adoption) (E6), - [Service Management Design System and PWA Shell](/docs/jumentix/reference/service-management-design-system-pwa) (E7), - Service Management Application, - Tenant and RBAC Authorization Contract -- Linear: - [JUM-491](https://linear.app/jumentix/issue/JUM-491/feature-multi-user-shared-catalog-backend-sync-over-cana-resync-events), - [JUM-492](https://linear.app/jumentix/issue/JUM-492/feature-domain-package-versioning-with-semantic-conflict-resolution), - [JUM-493](https://linear.app/jumentix/issue/JUM-493/feature-publish-designer-core-as-jumentix-package-xpertminds-org-dry), - [JUM-494](https://linear.app/jumentix/issue/JUM-494/docs-e8-documentation-collaboration-and-packaging), - [JUM-547](https://linear.app/jumentix/issue/JUM-547/feature-full-suite-exportimport-carry-interfaces-service-configuration), - [JUM-617](https://linear.app/jumentix/issue/JUM-617/fix-importdomainpackage-recompute-domainentity-ids-on-re-import) diff --git a/apps/jumentix-website/content/jumentix/reference/service-management-contract-parity.mdx b/apps/jumentix-website/content/jumentix/reference/service-management-contract-parity.mdx deleted file mode 100644 index 406052f09..000000000 --- a/apps/jumentix-website/content/jumentix/reference/service-management-contract-parity.mdx +++ /dev/null @@ -1,378 +0,0 @@ ---- -title: "Service Management Contract Parity" -description: "What each export guarantees: OAS 3.1, AsyncAPI, proto, codegen bundle and round-trip fidelity." ---- - -# Service Management Contract Parity Guarantees - -This is the E4 document of the Service Management E1–E8 documentation chain -([JUM-479](https://linear.app/jumentix/issue/JUM-479/docs-e4-documentation-contract-parity-guarantees)). -It documents what the designer's contract exports **guarantee** — as the code -behaves today, after the contract-parity lane -([JUM-474](https://linear.app/jumentix/issue/JUM-474/feature-oas-31-export-compliant-with-req-036-and-route-resolution), -[JUM-475](https://linear.app/jumentix/issue/JUM-475/feature-asyncapi-and-proto-exports-targeting-canonical-specasyncapi), -[JUM-476](https://linear.app/jumentix/issue/JUM-476/feature-codegen-preview-and-boilerplate-bundle-emit-hexagonal-layout), -[JUM-477](https://linear.app/jumentix/issue/JUM-477/feature-rbac-editor-aligned-to-tenant-rbac-authorization-contract), -[JUM-478](https://linear.app/jumentix/issue/JUM-478/feature-lossless-round-trip-import-of-spec100yml-with-full-meta)) -and its verification machinery -([JUM-470](https://linear.app/jumentix/issue/JUM-470), -[JUM-471](https://linear.app/jumentix/issue/JUM-471/test-bun-unit-suite-exportersimporters-round-trip)) -landed. - -The guarantee is a promise to the boilerplate, not a feature list: **an -artifact exported from the designer can be consumed by the boilerplate -without hand-editing.** Each guarantee below names the check that proves it, -so a reader can verify rather than trust, and a maintainer who breaks one -fails a suite — not a review opinion. - -The export surface itself (eight exporters, their formats and the export -quality gate) is pinned by -Requirement 126, Contract 3 -— this document links to it rather than duplicating it. Usage-level walkthroughs -live in -[Domain Designer Features and Usage](/docs/jumentix/reference/domain-designer-features) -(sections 10.1–10.2). - -## Guarantee 1 — the OAS 3.1 export is boilerplate-consumable (JUM-474) - -Builder: `buildOasDocument` in -`packages/designer-core/src/exporters/designerExporters.js`. - -**The document declares `openapi: '3.1.0'` — 3.1, not 3.0 — because that is -the version the boilerplate consumes.** The canonical -`spec/1.0.0.yml` declares `3.1.0`, and the entity -model is legislated OpenAPI 3.1 end to end by -Requirement 026 -(3.1 is the JSON Schema 2020-12-aligned line; the designer's JSON Schema -export targets the same draft). An export that matched the canonical -version's syntax but not its version would be a contract the consumer has to -translate — so the export emits exactly the dialect the boilerplate's gates -already validate. - -The exported document is guaranteed to satisfy -Requirement 036, -the port-object discipline enforced on the canonical spec by -`ci-cd/check-oas-route-resolution.js`: - -- **Canonical verb `operationId`s.** Every operation carries an `operationId` - on the canonical verb scheme (`getAll*` / `create*` / `get*ById` / - `update*` / `delete*`), qualified by the schema name so ids stay unique - across domains (`getAllBilling_Invoice`). -- **The `$ref` discipline.** Request bodies reference - `RequestCreate` / `RequestUpdate` port input objects via - `$ref`; every 2xx response references the entity schema, its - `ArrayOf` wrapper, or the shared `ResourceDeleteResponse` — never an - inline schema. Every referenced schema carries a non-empty `description`. -- **Canonical error codes.** Error responses use the - [ERROR-CONTRACTS-AND-RESPONSES](/docs/jumentix/reference/errors-responses) status - set (400/401/403/404/409) with its canonical descriptions. -- **Port wrappers are marked, not hidden.** Derived port input/output - wrappers carry `'x-port-object': true`, which is what lets the OAS importer - skip them (see Guarantee 4). - -**Proven by:** -`designerOasCompliance.test.ts`, -which imports `validatePortObjectContracts` and `resolveSchemaByRef` **from -the real checker** (not a copy) and applies them to a document exported from -a UI-style model — the export cannot drift from the gate without failing the -suite. The name-collision half of the guarantee (two names that tokenize to -the same schema/route, e.g. `Foo Bar` vs `Foo-Bar`, are export-gate-blocking -errors rather than silent overwrites) lives in -`modelValidation.js` -and is pinned by -`modelValidation.test.ts`. - -## Guarantee 2 — AsyncAPI 3.0 per transport and a canonical proto (JUM-475) - -Builders: -`packages/designer-core/src/exporters/asyncApiExporters.js`. - -- **One file per transport, canonical naming.** The export emits - `.websocket.yml` and `.grpc.yml`, matching the - `spec/asyncapi/` directory - one-for-one — never a single combined document, never the 2.x - publish/subscribe shape. Under 3.0, channels hold `messages` and the - top-level `operations` map carries `action: send|receive` (`response` - contracts are received; every other type is sent) plus channel/message - `$ref`s. -- **The same `$ref` payload discipline as OAS.** Message payloads live once - under `components.schemas` and messages reference them — identical payloads - share one schema entry instead of being inlined per message. -- **Every exported document validates against** - `validateAsyncApi30Document` - (`asyncApi30Validation.js`), - the in-repo structural validator for the 3.0 shape (the repository does not - depend on `@asyncapi/parser`). The canonical `spec/asyncapi/` files pass - the same rules — drop-in shape parity between what the designer emits and - what the boilerplate ships. -- **The gRPC proto export reproduces the canonical envelope.** proto3, - package `realtime`, service `AsyncApiGateway`, envelope messages - `AsyncApiRequest`/`AsyncApiResponse`, one rpc/message pair per designer - message contract. For a contract-less model the emitted proto is - **byte-identical** to the checked-in - `spec/asyncapi/async-api.proto`. - -**Proven by:** -`designerAsyncApiExport.test.ts` -— including the byte-identity assertion and the test that runs the validator -over the canonical files themselves, so the canonical documents and the -export drift together or fail together. - -## Guarantee 3 — the codegen bundle is deliverable code (JUM-476) - -Builder: -`packages/designer-core/src/codegen/hexagonalCodegen.js`, -consumed by both the boilerplate-bundle exporter -(`buildBoilerplateBundleDocument`, artifact `kind: 'boilerplate-bundle'`, -`version: '2.0.0'`) and the designer's Code Preview pane — same builder, so -preview and bundle cannot drift apart. - -- **The layout is hexagonal and mirrors the migrated Users module** - (`src/modules//` with `domain/{Entity,Model,security}`, - `application/{ports,use-cases}`, `adapters/in/http/controllers`, - `adapters/out/persistence`, `composition/`, and `events/contracts/` only - when message contracts exist — see - HEXAGONAL-FEATURE-DRIVEN-MIGRATION). -- **Contract shapes are consumed, never re-derived.** Field types come from - the Guarantee-1 OAS component schemas, HTTP routes from its paths and - `operationId`s, event channels from the Guarantee-2 AsyncAPI channels. A - contract change regenerates the code; the code never forks the contract. -- **The output passes the repository's own architecture checks.** Every - generated controller passes `validateControllerFile` from - `ci-cd/check-hexagonal-boundaries.js`, - and the generated import graph only points inward (domain ← application ← - adapters ← composition). -- **The output compiles.** The emitted file set compiles under - `tsc --strict` — the suite writes the bundle to a temp directory and runs - the real compiler over it. - -**What the developer still writes.** The bundle is a runnable skeleton, not a -service: the persistence adapter is an in-memory `Map` mirror of -`UserDataRepository.ts` that you swap for the real store client, the -composition root must be wired into the service bootstrap, and any business -rule beyond the canonical CRUD verbs is yours. The guarantee covers the -boundary — layout, contracts, compilation, architecture checks — not the -application logic. - -**Proven by:** -`hexagonalCodegen.test.ts`. - -## Guarantee 4 — round-trip fidelity, with the honest boundaries (JUM-471, JUM-478) - -Suite: -`designerRoundTrip.test.ts`. -The property under test is the crossing itself — export → import → compare — -so an exporter-drops-field + importer-ignores-field cancellation cannot hide -behind fixed expected outputs. - -### Symmetric crossings (lossless, deep-equal asserted) - -- **JSON** (`buildJsonExportDocument` → `buildStateFromSuiteExport` over - `normalizeStatePayload`): the versioned full-suite document (JUM-547) — - `domains`, `relationships`, `view`, `interfaces`, `serviceConfiguration` - and `deployments` round-trip deep-equal, and the export is idempotent. The - boundary is documented and asserted: selections and `idCounter` are not - part of the document and are recomputed on import, and `runtimeEnvironment` - crosses as the environment *selection* only (see the JUM-547 section - below). Pre-JUM-547 domain-only documents (`{ domains, relationships, view }`, no `kind`/`version`) import cleanly with the missing sections - defaulted; a document with an unknown top-level section, a newer major - `version`, or a `kind` other than `service-management-suite` fails clearly - instead of half-importing. -- **Domain package** (`buildDomainPackageDocument` → `buildDomainFromPackage`): - a package round-trips deep-equal into an empty model, stamped with - provenance (JUM-492): the v2 document carries a `package` block - (`{ name, version, dependencies }`), and imported content records - `context.provenance`/`meta.provenance` (`{ package, version }`). Re-imports - are version-aware: the same version with equal content is a no-op, the same - version with different content and downgrades are refused, and a newer - version merges deterministically — additive/metadata changes apply, and - removals, narrowings, RBAC and invariants keep the existing content and are - listed in the merge preview for a user decision (Requirement 126 Contract - 3). Dependency ranges are resolved against the installed-package registry; - missing or incompatible dependencies are reported, and a cycle the incoming - package would close is refused. The JUM-617 id recomputation still guards - the append path (a different package with colliding ids). - -### The OAS crossing: fixed point, empty loss list - -OAS is narrower than the internal model, so the OAS crossing was the lossy -one. JUM-478 drove the model-level loss list **from 21 diff paths to zero**, -and the suite asserts the exact empty list — a field silently joining (or -rejoining) it fails the suite: - -- **Export → import → export reaches a fixed point**; the first-crossing - document diff is exactly `[]` (asserted with `toStrictEqual`, not - eyeballed). -- What OAS cannot express natively crosses as agreed `x-` extensions the - importer normalizes back into `entity.meta`: `x-aggregate-root`, - `x-invariants`, `x-rbac` (emitted only when the policy diverges from the - designer default — an absent `x-rbac` normalizes back to exactly that - default), `x-fieldless: true` (an empty field set survives instead of - gaining the importer's default `id`/`createdAt`/`updatedAt`), and per-field - `x-field-flags: { pk, fk, unique }` (emitted only when the flags diverge - from the importer's name heuristic: `id` → PK/unique, `*Id` → FK). - Message contracts and composition (`oneOf`/`allOf`/`anyOf`, `discriminator`, - `x-external-refs`) cross the same way. -- **Relationships cross as top-level `x-relations` rows keyed by schema - name** (`{ name, fromSchema, toSchema, fromCardinality, toCardinality }`) — - never by model id, because the importer recomputes ids and ids in the - document would break the fixed point. Rows whose endpoints did not import - are dropped, the same rule `normalizeStatePayload` applies to dangling - model ids. -- **The port-object wrappers are skipped by design — and that is not a - loss.** `RequestCreate*`/`RequestUpdate*`/`*ArrayOf`/`ResourceDeleteResponse` - are derived artifacts, not model state; re-importing them would fabricate - phantom entities. Designer-exported documents mark them with - `'x-port-object': true`. - -### The boundaries that remain (named, asserted, not swept away) - -For **designer-exported documents** the model-level crossing still does not -carry, by design: - -- **Domain bounded-context blocks** (`domain.context`) and **canvas layout** - (positions, colors) — they have no extension carriage; import recomputes - them. Domain and entity **names** survive. -- **The typed-format normalization**: typed fields (`uuid`/`date`/`datetime`) - come back carrying the canonical format the exporter derived from their - type. The suite asserts this as the transformation - `applyDocumentedOasFormatNormalization`, so it is part of the contract, not - an accident. -- **View state** (zoom, filters, the export-gate toggle) is JSON-export - content, not OAS content — the OAS importer never restored it. - -For **foreign documents** — OAS files the designer did not emit, such as the -canonical `spec/1.0.0.yml` — the importer recognizes -port objects by convention (the same naming/description rules: `Request*`, -`*ArrayOf`, `ResourceDeleteResponse`, "Port input/output object" descriptions -that are not ` resource` entity contracts, and non-object schemas), and -the named remaining losses are: - -- the **`example` / `default` / `minItems` / `maxItems` facets** — the - designer field model has no slot for them; -- **array item `$ref` linkages** — value-object references flatten to the - `itemsType` vocabulary; -- **legacy (non-canonical) `operationId`s** — import keeps no operationIds, - so re-export regenerates them on the canonical verb scheme. - -The canonical import itself is pinned: `spec/1.0.0.yml` (OpenAPI 3.1.0, 33 -operationIds) imports as one `Imported` domain with exactly the six contract -schemas (`Document`, `Email`, `Address`, `Phone`, `User`, `Organization`) — -no phantom port-object entities, no relationships — with full meta -normalization, and re-exports to a fixed point whose entity schemas keep the -source's properties and required sets and whose paths carry the five -canonical CRUD operations per schema. - -### One-way exporters - -Markdown, JSON Schema, AsyncAPI and the boilerplate bundle have no importer. -Their suites assert structural invariants instead of a crossing: every -domain/entity/field/relationship renders; one JSON Schema definition per -entity with `required ⊆ properties` and `additionalProperties: false`; one -AsyncAPI 3.0 operation per contract per transport with shared payload refs; -one bundle module per domain with the hexagonal file set. - -## Where the gates enforce all of this - -- **The export quality gate** (Requirement 126, Contract 3): with - `view.exportBlockCritical` true (the default), every exporter refuses to - run while - `modelValidation.js` - reports any `error`-severity issue — which includes the OAS name-collision - rule and unenforceable RBAC roles (see below). The gate's DOM half is - `canExportModel` in - `apps/service-management/script.js`. -- **The unit suites are the enforcement.** All suites named above run under - Bun through the mapped runner - (`ci-cd/run-unit-tests.js`, which refuses a - run that discovers zero tests) inside `bun run test:unit` — a cell of every - branch quality gate (`ci-cd/run-branch-quality-gate.js` selects it for - direct pushes to `dev`, and the full matrix for pull requests into `dev` - and `main`). -- **The canonical-spec gates stay green on the boilerplate side**: - `bun run oas:check-routes` (Req 036 on `spec/`) and - `bun run arch:check-boundaries` are cells of `ci:gate`, so a canonical - contract the designer targets cannot silently stop being what the export - was shaped against. - -## RBAC alignment and the divergence it reconciled (JUM-477) - -The per-entity RBAC editor is aligned to the -Tenant and RBAC Authorization Contract -through -`src/model/rbacContract.js`, -a designer-side mirror of the Users domain implementation (`Rbac.ts`, -`TenantAuthorizationPolicy.ts`). The reconciliation found a real divergence, -recorded here rather than quietly fixed: - -- **The designer persisted `tenantScoped` as a free per-rule flag; the - runtime has no such knob.** Tenant scoping in the runtime is *derived* from - the role set (`shouldRequireOrganization`: normalized `admin`/`user` roles - constrain the principal to its organization; `superadmin` and legacy direct - scopes keep a global boundary). A stored `tenantScoped` value that - contradicted the roles was a configuration the boilerplate would silently - not honour. The editor now derives `tenantScoped` from the selected roles - (the checkbox is read-only and previews the derived value), and stored - policies are repaired to the derived value on load — a compatible extension - under Requirement 126 Contract 2, with the stored shape unchanged. -- **The principal vocabulary is closed.** Only the normalized tenant roles - (`superadmin`, `admin`, `user`) and the legacy direct scopes are - enforceable; anything else is rejected at edit time and reported as an - `error` by model validation, so the export gate blocks it instead of - exporting a policy the runtime would drop. - -**Proven by:** -`rbacContract.test.ts`, -which pins the mirror against `Rbac.ts` itself — if the domain vocabulary -drifts, the suite fails. This is also why `x-rbac` round-trips losslessly -(Guarantee 4): the exported policy is the normalized, enforceable one, and -the importer rebuilds it against the same contract. - -## Full-suite export and the `runtimeEnvironment` decision (JUM-547, landed) - -Export and import now carry **all five tabs**, not just the domain model. The -JSON export (`domain-designer.json`) is the versioned full-suite document: -`{ kind: "service-management-suite", version: "2.0.0", domains, relationships, interfaces, serviceConfiguration, runtimeEnvironment, codeWorkspace, deployments, view }` — -the same sections the pinned `service-management.v1` document persists in -Cana (Requirement 126, Contract 2), minus the session selections and -`idCounter`. A model designed across all five tabs exports and re-imports -with every tab intact; a bundle exported before this change (the domain-only -shape, no `kind`/`version`) imports cleanly with the missing sections -defaulted, and a bundle with an unknown section or a newer major version -fails clearly rather than half-succeeding. - -The recorded decision is `runtimeEnvironment`'s treatment -([JUM-547](https://linear.app/jumentix/issue/JUM-547/feature-full-suite-exportimport-carry-interfaces-service-configuration), -Requirement 126 Contract 3): it mirrors real `.env` contents, so an export -bundle containing the values **would be a file that can carry configuration -off the machine**. Of the three candidate positions — export the selection -only; export values restricted to the editable tier; omit the section -entirely — the landed stance is the first: **the bundle carries the -environment selection (`environment`, `fileName`) but never `values`**, and -import restores the selection while preserving the local machine's values. -The runtime environment is a property of where the designer is running; the -selection is design metadata worth sharing. Since no values cross, **no -secret can leave in a bundle** — the guarantee the third position was -preferred for, kept without losing the selection. The suite proves it by -asserting the wire document contains no value string. - -**Proven by:** -`designerRoundTrip.test.ts` -(full-suite deep-equal, values-never-cross, backward/forward compatibility) -and -`designerExporters.test.ts` -(document shape). - -## References - -- OAS exporter/importer: `packages/designer-core/src/exporters/designerExporters.js`, `designerImporters.js` -- AsyncAPI/proto exporters: `packages/designer-core/src/exporters/asyncApiExporters.js`; validator: `asyncApi30Validation.js` -- Codegen: `packages/designer-core/src/codegen/hexagonalCodegen.js` -- Model validation / export gate: `modelValidation.js`, `apps/service-management/script.js` -- RBAC mirror: `src/model/rbacContract.js`; contract: Tenant and RBAC Authorization Contract -- Suites: `designerRoundTrip.test.ts`, `designerPackageVersioning.test.ts`, `designerOasCompliance.test.ts`, `designerAsyncApiExport.test.ts`, `hexagonalCodegen.test.ts`, `rbacContract.test.ts`, `modelValidation.test.ts` -- Gates: `ci-cd/check-oas-route-resolution.js`, `ci-cd/check-hexagonal-boundaries.js`, `ci-cd/run-unit-tests.js` -- Canonical targets: `spec/1.0.0.yml`, `spec/asyncapi/`, `spec/asyncapi/1.0.0.grpc.yml`, `spec/asyncapi/async-api.proto` -- Requirements: Requirement 036 (port objects), Requirement 026 (OAS 3.1 entity compliance), Requirement 126, Contract 3 (ownership and public contracts, Contracts 2–3) -- Sibling E-chain documents: Service Management Application, [Service Management Module Architecture and IDesignerStore Port Contract](/docs/jumentix/reference/service-management-module-architecture), [Domain Designer Features and Usage](/docs/jumentix/reference/domain-designer-features) -- Linear: [JUM-474](https://linear.app/jumentix/issue/JUM-474/feature-oas-31-export-compliant-with-req-036-and-route-resolution), [JUM-475](https://linear.app/jumentix/issue/JUM-475/feature-asyncapi-and-proto-exports-targeting-canonical-specasyncapi), [JUM-476](https://linear.app/jumentix/issue/JUM-476/feature-codegen-preview-and-boilerplate-bundle-emit-hexagonal-layout), [JUM-477](https://linear.app/jumentix/issue/JUM-477/feature-rbac-editor-aligned-to-tenant-rbac-authorization-contract), [JUM-478](https://linear.app/jumentix/issue/JUM-478/feature-lossless-round-trip-import-of-spec100yml-with-full-meta), [JUM-470](https://linear.app/jumentix/issue/JUM-470), [JUM-471](https://linear.app/jumentix/issue/JUM-471/test-bun-unit-suite-exportersimporters-round-trip), [JUM-547](https://linear.app/jumentix/issue/JUM-547/feature-full-suite-exportimport-carry-interfaces-service-configuration), [JUM-492](https://linear.app/jumentix/issue/JUM-492/feature-domain-package-versioning-with-semantic-conflict-resolution) diff --git a/apps/jumentix-website/content/jumentix/reference/service-management-design-system-pwa.mdx b/apps/jumentix-website/content/jumentix/reference/service-management-design-system-pwa.mdx deleted file mode 100644 index db4f449ec..000000000 --- a/apps/jumentix-website/content/jumentix/reference/service-management-design-system-pwa.mdx +++ /dev/null @@ -1,372 +0,0 @@ ---- -title: "Service Management Design System and PWA" -description: "Design tokens, the accessibility model, and installing, updating and recovering the app shell." ---- - -# Service Management Design System and PWA Shell - -This is the E7 document of the Service Management E1–E8 documentation chain -([JUM-490](https://linear.app/jumentix/issue/JUM-490/docs-e7-documentation-design-system-and-pwa-shell)). -It documents the two surfaces that define what the designer *looks like* and -*how it reaches the user's machine*: the **design-system adoption** -([JUM-488](https://linear.app/jumentix/issue/JUM-488/feature-adopt-jumentix-design-system-and-storybook-coverage)) -and the **installable PWA shell** -([JUM-489](https://linear.app/jumentix/issue/JUM-489/feature-installable-pwa-shell-service-worker-manifest)) -— exactly as the code behaves today, with the multi-tab sync gap closure -([JUM-485](https://linear.app/jumentix/issue/JUM-485/feature-write-event-integration-multi-tab-sync-via-cana-message)) -already landed inside the shell. - -Part of this document is contributor-facing (how the token layer stays in -sync, how to add a component without reintroducing ad-hoc styles) and part is -deliberately **user-facing**: the keyboard and screen-reader model, how to -install the designer as an app, what the update prompt means, and how to -recover from a stuck cache without knowing what a service worker is. A -keyboard path that exists but is undocumented is a path nobody finds. - -Two deliberate boundaries: - -- **The data story is linked, not duplicated.** Where the designer's work - lives (Cana), how the localStorage migration behaved, and what offline - means for *data* belong to the E6 document - ([JUM-487](https://linear.app/jumentix/issue/JUM-487/docs-e6-documentation-cana-adoption-migration-and-offline-behavior)), - with the storage port contract in the E3 document, - [Service Management Module Architecture](/docs/jumentix/reference/service-management-module-architecture). - This document states the shell↔data boundary once — because the update and - recovery story is incomprehensible without it — and does not restate the - persistence story. -- **The visual source of truth lives on the website.** The component - inventory, the design constraints and the Storybook workflow are owned by - the website's - Design System and Storybook - document. This document covers the designer's *adoption* of that system; - it does not redefine it. - -## Design system adoption at the token layer (JUM-488) - -The designer is a zero-build vanilla SPA served from its own static root. It -cannot import the website's React components, so adoption happens at the -**token layer**: shared custom properties and shared idioms, not shared -components. - -### Which tokens the designer uses - -`apps/service-management/tokens.css` -vendors the website's -`components/design-system/tokens.css`: -the color ramps (`--jtx-blue-*`, `--jtx-green-*`, `--jtx-coral-*`, -`--jtx-yellow-*`), ink and muted text, surfaces and lines, code-surface -background, radii, shadows, the 4–48 px spacing scale, `--jtx-motion-fast`, -`--jtx-content-width`, the dark-theme overrides under `:root.dark`, the -`prefers-reduced-motion` guard, and the `--jtx-font-sans` / `--jtx-font-mono` -typography stacks (Inter and IBM Plex Mono — the same type voice as the -website's Mantine theme). - -`styles.css` resolves **every -cosmetic value** — color, typography, radius, shadow, spacing, motion — to -those tokens, loads after `tokens.css` in -`index.html`, and scopes its -element-level rules under `.service-management-shell` (with `:where()`) so -the exact app cascade applies when the website Storybook embeds the -stylesheet. What deliberately stays literal: - -- **Structural geometry the canvas math depends on** — the 3200×2200 canvas, - the 24 px grid, 520 px domains, 190 px entities — pinned by - `src/model/modelQueries.js` - and its unit suite. These numbers are behaviour, not cosmetics. -- **The compact inspector density** (5–7 px gaps in the dense panels), which - has no token equivalent. -- **The attention-tone literals** — error styling follows the design system's - "attention" idiom: `--jtx-coral-50` surface with coral-ramp text `#a32e1a` - and border `#ffc0ad`, the same literals the website's `StatusBadge` - component uses (no coral-ramp text token exists to reference). - -Beyond colors, the designer follows the system's interaction idioms: the -focus ring is the design-system `:focus-visible` ring (3 px `--jtx-blue-200`, -2 px offset) on every input, select and button; hover follows the -secondary-action idiom (blue-50 surface, blue-200 border); motion respects -`prefers-reduced-motion` through the vendored guard. - -### How tokens stay in sync — the vendored-copy rule - -`apps/service-management/tokens.css` is a **copy**, and copies drift unless a -rule forbids it. The rule is written in the file's own header: - -> SOURCE OF TRUTH: `apps/jumentix-website/components/design-system/tokens.css`. -> Keep the token values byte-identical with the website file; token changes -> land in the website file first and are mirrored here. - -Two corollaries a contributor must know: - -- **The website file is extended additively, never designer-first.** When the - designer needed the typography stacks as tokens (JUM-488), they were added - to the *website* file — because the vendored copy exists, non-Mantine - consumers inherit the same type voice — and then mirrored down. A token - added only to the designer's copy would be invisible to the website and - silently deleted on the next mirror. -- **The `.jtx-story-canvas` Storybook wrapper is website-only** and is - intentionally omitted from the vendored copy — it is the one sanctioned - difference between the two files. - -### How to add a component without reintroducing ad-hoc styles - -1. **Reuse existing tokens before introducing a new value.** A cosmetic - literal in `styles.css` is a regression to the pre-JUM-488 state; if no - token fits, add a *semantic* token to the website's `tokens.css` first and - mirror it into the vendored copy in the same change. -2. **Keep structural geometry literal only when the canvas math owns it** — - and when it does, the number belongs in `modelQueries.js`'s pinned set, - not invented per-component. -3. **Scope element-level rules under `.service-management-shell`.** An - unscoped rule leaks into the Storybook chrome the moment the story mounts - it. -4. **Add or extend a story** in the designer's Storybook coverage (below) so - the new surface is exercised by the same static-build, smoke and - accessibility gates as the rest. - -### Where the Storybook inventory lives - -The designer has no Storybook of its own; its coverage lives in the website -workspace as -`components/service-management-designer/ServiceManagementDesigner.stories.tsx`, -which imports the designer's real `tokens.css` and `styles.css` and mounts -its real markup. Eight stories cover the key UI states: `TabShell`, -`WorkspaceControls`, `DomainCanvas` (entities, edges, mini-map), -`StatusSurfaces` (the JUM-543 non-blocking surfaces), `EntityInspector`, -`PanelsAndLists`, `CodePreviews` and `PwaUpdateBanner` (the JUM-489 update -prompt). Because the stylesheets are token-driven, the addon-themes -light/dark switch applies to the designer exactly as it does to the website -components. - -The inventory is enforced, not aspirational: -`scripts/storybook-smoke.mjs` -requires all eight designer story IDs and a minimum catalog of 54 entries, -and the Storybook accessibility addon is configured to report violations as -errors (`a11y: { test: 'error' }` in -`.storybook/preview.tsx`). -Storybook is exclusively a website-workflow gate — run by the path-scoped -`.github/workflows/website.yml`, never by the monorepo test matrix — with -commands `bun run website:storybook`, `website:storybook:build` and -`website:storybook:smoke` from the root. The full workflow, ownership and -design constraints are documented in -Design System and Storybook. - -## Accessibility: the keyboard and screen-reader model - -This section is user-facing. Everything in it is operable today and matches -the behaviour JUM-488 shipped; the semantic layer is exercised by the -Storybook accessibility gate (violations fail as errors), and tab switching -itself is exercised end-to-end by the browser suites below. - -### The tab bar is a real tablist - -The six visible tabs are a WAI-ARIA tablist (`role="tablist"`, `role="tab"`, -`aria-selected`, `aria-controls` in -`index.html`; behaviour in -`src/ui/tabs.js`): - -- **One tab stop for the whole bar.** Roving `tabindex` puts only the active - tab in the tab order; the others are reachable by arrow keys, not by - repeated Tab presses. -- **Arrow keys move and activate.** `ArrowLeft`/`ArrowRight` cycle through - the tabs (wrapping at the ends), `Home`/`End` jump to the first/last tab, - and the focused tab activates automatically — there is no separate - "confirm" step. -- **Space and Enter keep their native behaviour** on every button, including - the tabs. - -### Reaching the canvas: the skip link - -The first tab stop on the page is the **skip link** ("Skip to canvas -workspace"), which jumps past the header and tab bar to the workspace -(`#workspace-main`). It is invisible until focused; the design-system focus -ring doubles as its reveal affordance. - -### The canvas's non-visual equivalent - -The domain canvas is a visual surface, but every structural operation has a -keyboard path — selection is never pointer-only: - -- **Select without the pointer.** Every entry in the sidebar lists (domains, - relationships) is a real `