diff --git a/docs/design/dictionary-marketplace.md b/docs/design/dictionary-marketplace.md index 4edabae..d7c0fa7 100644 --- a/docs/design/dictionary-marketplace.md +++ b/docs/design/dictionary-marketplace.md @@ -1,304 +1,247 @@ -# Design: Dictionary Bundle Marketplace +# Design: Bundle Hygiene (formerly "Dictionary Marketplace") **Author:** zimin.an **Date:** 2026-05-12 -**Status:** PROPOSED v1 — needs `/office-hours` для premise validation + `/plan-eng-review` -**Scope:** ДЗЗ domain, multi-tenant ready (corp private bundles + curated public ДЗЗ catalog) -**Sprint estimate:** **~1.5-2 спринта** (10-14 days CC), v1 internal-only (no public catalog UI) -**Blockers:** none, но рекомендация defer'a до v2.14.0 prod stable + 2 weeks dogfood +**Status:** PROPOSED v2 (post `/office-hours` + cross-model challenge) +**Supersedes:** v1 «Dictionary Marketplace» (Approach B: full registry + browse UI + dry-run preview + install dialog, 10-14d) +**Scope:** ДЗЗ domain, single-tenant (Approach C-revised — bundle format spec + publisher CLI, NO consumer UI) +**Sprint estimate:** **2-3 days CC**, hard cap. Anything beyond requires new design doc. +**Blockers:** none --- ## TL;DR -Сейчас каждый customer install Ordinis строит свой `ordinis-cuod-bundle` Maven module from scratch. ЦУОД bundle = 40 dictionaries (КА, типы, ground stations, частотные диапазоны, операторы и т.д.) — это ~2-3 недели работы дублируется при onboarding нового customer'а. **Нет shared catalog**, нет «бери готовое». +v1 предлагал full marketplace (Nexus registry + browse UI + install dialog + dry-run preview + multi-tenancy + dep resolution) за 10-14 days. После `/office-hours` premise challenge + cross-model challenge — **v1 был over-scoped для N=1 customer** (ЦУОД anchor only сегодня). -**Предложение:** Bundle marketplace. Авторы публикуют bundle versions в реестр (corp Nexus → потенциально public ДЗЗ catalog). Admin в running instance видит каталог, install'нет нужный bundle (с optional sample data seed), получает 40 готовых справочников за минуту. +**Revised:** Formalize bundle format spec + publisher CLI. **No consumer-side UI.** This is **bundle hygiene**, не marketplace. -**Win:** Customer onboarding 2-3 недели → 1 день. Sales: «у нас готовая ДЗЗ-библиотека из 40+ справочников». Compounding value: каждый install обогащает catalog. +**Why bundle hygiene matters at N=1:** +1. **Java team handoff** требует documented manifest spec в любом случае — без этого new engineers не знают как добавить новый dict без полного code review pipeline +2. **Sales artifact** — demo prospects «ЦУОД bundle is independently signed/versioned, here's how you'd ship your own» без shipping консьюмерского UI +3. **Optionality** — когда customer #2 signs, bundle format уже defined, можно ship UI quickly (consumer UI = 5-7d incremental) -**Risk:** schema conflicts при install, malicious bundles, version drift breaking consumer integrations. Mitigations через signed manifests + dry-run preview + namespace prefix. +**What this design explicitly is NOT:** +- ❌ Browse catalog UI +- ❌ Install dialog в admin-ui +- ❌ Dry-run preview UI +- ❌ Public marketplace +- ❌ Bundle dep resolution / topological install +- ❌ Multi-tenant Nexus namespacing + +**Anti-feature:** если scope expands beyond 2-3 days hard cap → STOP, re-design doc. Don't let founder weekend-prototyping pull this into Approach B by accident. --- -## Current state +## Premises (validated через `/office-hours` 2026-05-12, including cross-model challenge) -Сейчас в Ordinis: -- `ordinis-cuod-bundle` Maven module → ЦУОД-specific dictionaries + sample data в `src/main/resources/bundles/cuod/` -- Build-time bundle: компилируется в jar, deploy'ится с app -- Single-bundle per installation (multi-bundle architectural future, не shipped) -- No runtime install / browse / discover +**P1.** Marketplace UI/registry **value scales с N customers.** At N=1 → near-zero. Build **only когда customer #2 LOI/contract стадия с extensibility language**. -``` -[ Customer A install ] [ Customer B install ] - ↓ docker pull ordinis:vX ↓ docker pull ordinis:vX - + ordinis-cuod-bundle + ordinis-customer-b-bundle - (build-time, requires Maven build) (нет — customer пишет свой Maven module) +**P2.** Bundle **format + signing + manifest** valuable AT N=1 — separable concern от marketplace UI. Doubles as Java handoff spec + sales demo. + +**P3.** ЦУОД go-live (anchor v1) **уже live на v2.14.0 prod.** Дальнейший development must support handoff readiness, не speculative future features. + +**P4.** **No customer #2 named with deadline as of 2026-05-12.** Sales conversations ongoing (Альтум integration в process, гидрометео preliminary discussions). Marketplace UI build now = infrastructure ahead of validated need. + +**P5.** **«Defer entirely» (Approach A) was directionally right но oversold.** Cross-model challenge revealed: pure defer assumes founder won't drift to greenfield work. Pre-committed scoped C contains damage better than aspirational pure-defer. + +**P6.** **Bundle hygiene work ROI положителен независимо** от marketplace decision: даже если marketplace UI shipped'нется только в Q4 2026 или никогда — formal bundle spec нужен для Java handoff (target 2-3 months). + +**P7.** **Hard cap 2-3 days CC.** No scope creep. If пилот results in «we should add UI now», that's NEW design doc, new /office-hours, fresh decision. + +--- + +## What ships в v2 (bundle hygiene, 2-3 days) + +### 1. Bundle manifest format spec + +`ordinis-cuod-bundle/manifest.yaml` (NEW file, replaces ad-hoc Maven module structure): + +```yaml +apiVersion: ordinis.io/v1 +kind: Bundle +metadata: + id: ru.cuod.dzz-ground-segment + name: ЦУОД ДЗЗ — наземный сегмент + version: 1.0.0 # semver, follows Maven artifact version + description: | + 40 dictionaries для управления наземным сегментом ДЗЗ: + КА, типы КА, наземные станции, антенны, частотные диапазоны. + domain: dzz + scope: PUBLIC + author: ЦУОД team + license: proprietary + signature: ed25519:base64... # added by publisher CLI + +dictionaries: + # Existing list from ordinis-cuod-bundle/src/main/resources/bundles/cuod/ + - name: spacecraft + schemaVersion: 1.0.0 + scope: PUBLIC + schemaRef: dictionaries/spacecraft/schema.json # relative path + sampleDataRef: dictionaries/spacecraft/sample.json # optional + - name: satellite_type + schemaVersion: 1.0.0 + scope: PUBLIC + schemaRef: dictionaries/satellite_type/schema.json + # ... 38 more ``` -Каждый новый customer = **new Maven module от руки**. +**Migration:** Existing ad-hoc `ordinis-cuod-bundle/src/main/resources/bundles/cuod/` structure refactored to match manifest. **No behavior change** — bundle still loaded via existing Maven module pattern. Just **adds spec on top**. -## Что хочется +### 2. Publisher CLI (`ordinis-bundle-publisher` Maven plugin) +```bash +mvn ordinis:bundle-publish \ + -Dbundle.path=ordinis-cuod-bundle \ + -Dnexus.url=https://nexus.corp/ordinis-bundles/private/cuod \ + -Dsigning.key=$BUNDLE_SIGNING_KEY ``` -[ Customer admin opens /catalog ] - ↓ - Browse: ЦУОД ДЗЗ Bundle v1.4.0 [40 dicts] - Гидрометео ДЗЗ Bundle v0.8.0 [12 dicts] - Базовый справочник стран v2.0.0 [1 dict] - ↓ - [ Install ] → dry-run preview → confirm - ↓ - POST /api/v1/bundles/install { bundleId, version, options } - ↓ - 40 dictionaries created in DB через standard `DictionaryDefinitionService` - + optional sample data seeded - + bundle metadata recorded в `installed_bundles` table - ↓ - Done in ~30 sec + +Steps плагина: +1. Read `manifest.yaml` +2. Validate каждый `schemaRef` is valid JSON Schema 7 via existing `SchemaValidator` +3. Compute bundle archive (tar.gz of manifest + schemas + sample data) +4. Sign archive с ed25519 key +5. Upload to corp Nexus с canonical path `//bundle.tar.gz` +6. Generate `bundle.sig` adjacent + +**Reuses:** existing Maven publish patterns в repo, corp Nexus credentials через GitLab CI variables. + +### 3. Signature verification helper + +`BundleSignatureVerifier.java` в new module `ordinis-bundle-spec` (lightweight): + +```java +public class BundleSignatureVerifier { + public boolean verify(byte[] archive, byte[] signature, PublicKey publicKey); +} ``` +**Не consumed в v2** (no consumer side). Built **as documentation artifact** для future marketplace work — defines verification contract. + +### 4. Documentation + +`docs/user-guide/bundle-authoring.md`: +- Bundle structure spec (manifest.yaml format) +- How to publish (mvn command + Nexus setup) +- Versioning semantics (semver, breaking change rules) +- Schema authoring conventions reused from existing JavaDoc patterns + +`docs/integration/bundle-format-spec.md`: +- Formal spec для Java team handoff +- Manifest schema definition +- Signing contract +- Future consumer protocol (placeholder) + +--- + +## What does NOT ship в v2 (explicit non-goals) + +| Feature | Why not | +|---|---| +| ❌ Admin UI «browse bundles» | No customer #2 with browse use case | +| ❌ Install dialog в admin-ui | Customer-specific deployment = helm + values, не runtime install | +| ❌ Dry-run preview UI | No install operation to preview | +| ❌ Multi-tenant Nexus namespacing | N=1 customer, single namespace OK | +| ❌ Dep resolution / topological install | Single bundle, no inter-bundle deps yet | +| ❌ Public catalog | Customer #2+ + community curation = v3 | +| ❌ Bundle uninstall flow | No install in admin UI → no uninstall | +| ❌ Bundle update / migration scripts | Manual helm upgrade pattern remains | + +**Anti-feature commitment:** If during 2-3d implementation эти items появляются «just a bit more», STOP. Write new design doc, fresh /office-hours, fresh approval. + --- ## Architecture ``` - Bundle Registry - (Nexus / GitLab Package Registry) - - immutable versioned artifacts - - signed bundles (corp PKI) - ▲ - │ npm/maven-like resolve +ordinis-cuod-bundle/ ordinis-bundle-spec/ (NEW module) +├── manifest.yaml ← NEW ├── BundleManifest.java +├── pom.xml ├── BundleSignatureVerifier.java +└── src/main/resources/ └── pom.xml + └── bundles/cuod/ + ├── dictionaries/ ordinis-bundle-publisher/ (NEW Maven plugin) + │ ├── spacecraft/ ├── BundlePublishMojo.java + │ │ ├── schema.json ├── ManifestValidator.java + │ │ └── sample.json └── pom.xml + │ └── ... (39 more) + └── (existing Maven structure) + │ - ┌──────────────────────────────────────────┐ - │ ordinis-rest-api (NEW endpoints) │ - │ BundleCatalogController │ - │ GET /api/v1/bundles/available │ - │ GET /api/v1/bundles/{id}/versions │ - │ BundleInstallController │ - │ POST /api/v1/bundles/install (dry-run +│ - │ apply) │ - │ GET /api/v1/bundles/installed │ - │ POST /api/v1/bundles/{id}/uninstall │ - └────────────────┬──────────────────────────┘ - │ - ┌────────────────▼──────────────────────────┐ - │ BundleResolver (NEW component) │ - │ - fetch manifest.yaml from registry │ - │ - verify signature (corp PKI ed25519) │ - │ - parse dictionary defs + sample data │ - │ - check conflicts with existing dicts │ - └────────────────┬──────────────────────────┘ - │ - ┌────────────────▼──────────────────────────┐ - │ Standard DictionaryDefinitionService │ - │ - create dict (per-dict CREATE event) │ - │ - seed sample records (if opted in) │ - │ - outbox → Kafka → downstream consumers │ - └───────────────────────────────────────────┘ - ▲ - ┌────────────────┴──────────────────────────┐ - │ ordinis-admin-ui │ - │ BundleCatalogPage.tsx (NEW route) │ - │ BundleInstallModal.tsx (dry-run preview) │ - │ InstalledBundlesTab (manage existing) │ - └───────────────────────────────────────────┘ + │ mvn ordinis:bundle-publish + ▼ + Corp Nexus + ordinis-bundles/private/cuod/ + └── ru.cuod.dzz-ground-segment/ + └── 1.0.0/ + ├── bundle.tar.gz + └── bundle.sig ``` -### Bundle manifest format - -```yaml -# bundle.yaml — published artifact metadata -apiVersion: ordinis.io/v1 -kind: Bundle -metadata: - id: ru.cuod.dzz-ground-segment - name: ЦУОД ДЗЗ — наземный сегмент - version: 1.4.0 - description: | - 40 справочников для управления наземным сегментом ДЗЗ: - КА, типы КА, наземные станции, антенны, частотные диапазоны, - операторы, форматы данных, уровни обработки. - domain: dzz - scope: PUBLIC # or INTERNAL / RESTRICTED - author: ЦУОД team - license: proprietary - signature: ed25519:base64... - -dependencies: - # Reference other bundles (e.g. shared "countries" dict) - - id: ru.shared.countries - version: ^2.0.0 - - id: ru.shared.iso-units - version: ^1.0.0 - -dictionaries: - - name: spacecraft - schemaVersion: 1.0.0 - scope: PUBLIC - schemaJson: - $schema: http://json-schema.org/draft-07/schema# - type: object - properties: - code: {type: string, x-unique: true} - type: {type: string, x-references: "satellite_type.code"} - # ... - sampleData: - - businessKey: ISS - data: {code: ISS, type: OPERATIONAL, ...} - # ... 39 more dictionaries - -# Migration hints for upgrades -migrations: - - from: 1.3.x - to: 1.4.0 - summary: добавлено поле x-frequencies в antenna - breaking: false - sql: null # null = no DB migration, schema add only -``` - -### Install flow (dry-run + apply) - -``` -1. Admin clicks Install в catalog -2. Frontend: POST /api/v1/bundles/install - { bundleId, version, dryRun: true, options: { seedSampleData: true } } -3. Backend BundleResolver: - - Download manifest.yaml from Nexus - - Verify ed25519 signature against trusted authors registry - - Parse 40 dictionary defs - - For each: check conflicts (dict name уже exists? schema compatible?) - - Compute diff: новые dicts, modifications, conflicts - - Return: { willCreate: [...], willUpdate: [...], conflicts: [...], warnings: [...] } -4. Frontend shows preview modal: - - 40 dicts to create - - 0 conflicts - - 312 sample records to seed - - [Cancel] [Install for real] -5. POST /api/v1/bundles/install { ..., dryRun: false } -6. Backend opens transaction: - - For each dict: DictionaryDefinitionService.create() (existing logic) - - For sample data: bulk insert via existing DictionaryRecordService - - INSERT INTO installed_bundles (id, version, installed_at, installed_by, manifest_sha256) - - Commit -7. Standard outbox → Kafka → downstream consumers получают NewDictionaryCreated events - (40 events за raz, batched в outbox) -8. Frontend: success toast + redirect к catalog (теперь installed_bundles tab показывает entry) -``` - -### Conflict resolution strategies - -При install bundle X v1.4.0, если dict `spacecraft` уже существует: - -| Strategy | Behavior | -|---|---| -| **`abort`** (default) | Return 409, admin вручную resolve | -| **`namespace`** | Create as `dzz_ground_segment__spacecraft` (prefix bundle id) | -| **`merge`** | Skip dict if schema совместима (existing has all bundle fields); fail if incompatible | -| **`override`** | Replace existing schema (DANGEROUS — only с explicit confirmation + admin role RESTRICTED) | - -v1: только `abort` + `namespace`. `merge` / `override` — v2 после dogfooding. +No new pods. No runtime changes к ordinis-app / ordinis-admin-ui. Only build-time tooling + Nexus artifacts. --- -## Components (new) +## Approaches Considered -| Component | Module | LOC est | +### Approach A — Defer entirely (0 days now) + +- **Pros:** Zero sunk cost. Capacity goes to AI metric work / prod hardening / customer #2 acquisition. +- **Cons:** Java handoff still needs bundle format spec eventually — built later under deadline pressure. Pure defer ~20% adherence (cross-model challenge): founder likely drifts to greenfield marketplace prototyping anyway. + +### Approach B — Full marketplace v1 (10-14 days, v1 doc original) + +- **Pros:** Real differentiator if customer #2 ships. Demo wow factor. +- **Cons:** Infrastructure ahead of validated need. Spec drift между hypothetical и real customer #2 requirements. 10-14d sunk cost if N never grows. Triggers Brooks's «is this solving real problem или one we created?» + +### Approach C-revised — **Bundle hygiene** (2-3 days, CHOSEN) + +- **Pros:** + - Java handoff readiness (must-have anyway, due in 2-3 months) + - Sales demo asset («signed versioned bundle artifact, here's our hygiene story») + - Contains founder's marketplace energy в legitimate scope-bounded work + - 5× cheaper than B, не «do nothing» of A + - Future-leverage: when customer #2 needs UI, format already defined → UI 5-7d вместо 10-14d +- **Cons:** + - «Half feature» if framed as marketplace — мы НЕ frame'им так + - Not consumer-visible (no UI screenshot for demo deck) + - 2-3 days still > 0 days (Approach A); risk founder drifts beyond cap + +--- + +## Recommended Approach: **C-revised — Bundle Hygiene** + +Rationale per `/office-hours` cross-model synthesis: + +1. **Java handoff debt avoidance:** Manifest spec mandatory anyway when team scales beyond zimin.an. Build now while context fresh, cheaper than retro-spec under deadline. + +2. **Sales conversation enablement:** Prospects asking «can we extend this с our own dictionaries?» get pointed at published bundle artifact + manifest spec — concrete answer без «we'll build it for you». + +3. **Founder behavior containment:** Cross-model challenge правильно отметил: pure defer has ~20% follow-through odds. Pre-committed 2-3d scope легитимизирует bundle work, prevents weekend prototyping что expanded beyond bounds. + +4. **Optionality preserved для customer #2:** If LOI signs Q3-Q4 — UI work spawn separate design doc, builds on this manifest, ships 5-7 days (smaller scope than ahead-of-time 10-14d). + +5. **Engineering preference «explicit over clever»:** Manifest format defined upfront prevents implicit conventions, helpful for Java team handoff. + +--- + +## Implementation plan + +| Step | Effort (CC) | Notes | |---|---|---| -| `BundleResolver` (fetch + verify + parse) | ordinis-app or new ordinis-bundles module | ~300 | -| `BundleCatalogController` (read endpoints) | ordinis-rest-api | ~150 | -| `BundleInstallController` (dry-run + apply) | ordinis-rest-api | ~200 | -| `BundleSignatureVerifier` (ed25519) | ordinis-bundles | ~100 | -| `InstalledBundle` JPA entity + repo | ordinis-domain | ~80 | -| Migration 00XX: `installed_bundles` table | ordinis-migrations | ~30 | -| `BundleCatalogPage.tsx` (UI) | ordinis-admin-ui | ~250 | -| `BundleInstallModal.tsx` (dry-run preview) | ordinis-admin-ui | ~200 | -| `InstalledBundlesTab.tsx` | ordinis-admin-ui | ~150 | -| Bundle CLI publisher (corp Maven plugin / scripts) | new ordinis-bundle-publisher | ~250 | +| 1. New module `ordinis-bundle-spec` (manifest POJO + verifier) | 3h | Pure Java, no Spring dep | +| 2. New module `ordinis-bundle-publisher` (Maven plugin) | 4h | Standard Maven plugin scaffolding | +| 3. `manifest.yaml` for `ordinis-cuod-bundle` (1.0.0 release) | 1h | Generated from existing bundle structure | +| 4. Migrate ad-hoc bundle structure → match manifest paths | 2h | File moves, no logic change | +| 5. Publisher CLI: tar.gz + ed25519 sign + Nexus upload | 4h | BouncyCastle for ed25519 | +| 6. Manifest schema validation в publisher | 2h | Reuses existing SchemaValidator | +| 7. GitLab CI integration (publish job on bundle tag) | 2h | New `.gitlab-ci.yml` job | +| 8. Tests (publisher Mojo + verifier + manifest parse) | 4h | unit tests, no integration tests needed | +| 9. `docs/user-guide/bundle-authoring.md` + `docs/integration/bundle-format-spec.md` | 3h | Java handoff artifact | +| **Total** | **~25h (2-3 days CC)** | **Hard cap. No scope creep.** | -Total: ~1700 LOC. Plus ~600 LOC tests. - ---- - -## Registry choice - -### Option A: Reuse corp Nexus (RECOMMENDED для v1) - -- Pros: уже есть в инфре (corp packages), self-hosted, signed artifacts -- Cons: Nexus ориентирован на Maven/npm, нужна custom REST query layer -- Path: `nexus.corp/repository/ordinis-bundles/{bundle-id}/{version}/manifest.yaml + sampledata.tar.gz` - -### Option B: GitLab Package Registry - -- Pros: уже используем GitLab, native CI publish from bundle source repos -- Cons: less flexible queries, GitLab CE может иметь limits - -### Option C: Custom registry service (NEW) - -- Pros: tailored to bundles (search, semver, dependencies) -- Cons: yet another service to ops, +2 weeks effort - -**Recommendation:** A (Nexus) для v1. C если customer growth >10 bundles published. - ---- - -## Multi-tenancy - -Каждый customer install ordinis имеет свой namespace в registry: -- `nexus.corp/ordinis-bundles/private/{customer-id}/` — corp private bundles -- `nexus.corp/ordinis-bundles/public/dzz/` — curated public ДЗЗ catalog - -Customer A не видит customer B's private bundles. Public catalog visible всем authenticated installs. - -Trust model: -- Private bundles: signed customer's own key -- Public ДЗЗ catalog: signed ЦУОД editorial team -- Admin UI shows badge: «✅ Verified ЦУОД» / «🏢 Private (your org)» / «⚠️ Unverified» - ---- - -## Non-goals (v1) - -- ❌ Public internet-facing marketplace UI (browser) — only corp Nexus discoverable through admin UI -- ❌ Paid bundles / billing -- ❌ Bundle rating / reviews / comments -- ❌ Automatic bundle updates (admin manually triggers upgrade) -- ❌ Bundle composition (compose multiple bundles into super-bundle) -- ❌ Bundle export from running instance back to registry (one-way: registry → install) -- ❌ Migration scripts execution (DB schema changes) — v1 только additive (new dicts, new fields), no DB migration - ---- - -## Risks - -1. **Malicious bundle uploaded в Nexus** — bundle с `x-id-source` указывающим на JNDI или другой attack vector. Mitigation: - - All bundles require ed25519 signature от trusted author - - Schema validator strict mode (no eval, no JNDI, allowlist `x-*` annotations) - - Sandboxed sampleData parse (no executable code, JSON only) - -2. **Bundle version conflict** — bundle X v1.4 deps require shared/countries v2.x, but другой bundle Y deps require shared/countries v1.x. Mitigation: - - Resolver detect conflict at dry-run, return 409 conflict с explanation - - Recommend admin: deinstall Y or wait until Y updates - -3. **Breaking schema change в new version** — bundle v2.0 removes field `mass_kg`, existing records have it. Mitigation: - - `migrations` block в manifest documents breaking changes - - Upgrade endpoint requires `acknowledgeBreaking: true` flag - - Audit log records upgrade event with migration summary - -4. **Registry down при install** — admin clicks install, Nexus 503. Mitigation: - - User-friendly error «Catalog временно недоступен, повторите через минуту» - - Local cache: recently viewed manifests cached 1h - - Circuit breaker на Resolver level - -5. **Schema-level FK references to non-existing dicts** — bundle dict A references dict B, B installed позже. Mitigation: - - Resolver topologically sort install order - - Atomic transaction (all-or-nothing) - - Cross-bundle FK: explicit dependency declaration в manifest, resolver enforces - -6. **Sample data сбивает existing records** — install bundle с sample data, customer уже имеет records с теми же businessKeys. Mitigation: - - Default: `seedSampleData: false` - - If true: dry-run shows count of records, admin confirms - - Skip records existing с тем же businessKey (idempotent) +**Anti-creep checkpoint:** After step 9, **STOP**. Don't add «just one consumer endpoint». Don't add «just a tiny browse list». Those are new design doc territory. --- @@ -306,94 +249,90 @@ Trust model: | # | Test | Type | |---|---|---| -| 1 | Happy path: install bundle с 5 dicts → все 5 created, sample data seeded | integration | -| 2 | Dry-run mode returns preview without DB changes | integration | -| 3 | Conflict detection: install bundle с existing dict name → 409 | integration | -| 4 | Namespace strategy: prefix всех dict names с bundle id | integration | -| 5 | Signature verification: tampered manifest → 422 | integration | -| 6 | Signature verification: unknown author → 422 with «author not trusted» | integration | -| 7 | Dependency resolution: install bundle X depending on Y → 409 если Y absent | integration | -| 8 | Topological install order: bundle с inter-dict FK → outer FK created first | integration | -| 9 | Atomic transaction: failure mid-install rolls back ALL changes | integration | -| 10 | Outbox events: install 5 dicts → 5 NewDictionaryCreated events in outbox | integration | -| 11 | Registry timeout: Nexus 503 → user-friendly 502 + retry | integration | -| 12 | Circuit breaker: 10 fails → 5min cool-down | integration | -| 13 | Bundle uninstall: deletes dicts + records + emits delete events | integration | -| 14 | Uninstall blocked если dicts have records authored locally (non-sample) | integration | -| 15 | Frontend BundleCatalogPage shows verified badge | RTL | -| 16 | Frontend BundleInstallModal — preview correctly counts new/updated/conflicts | RTL | -| 17 | Multi-tenancy: customer A не видит customer B private bundles | integration | +| 1 | Manifest parser: valid YAML → typed object | unit | +| 2 | Manifest validation: missing required field → error | unit | +| 3 | Publisher Mojo: happy path → archive + signature generated | unit (Mojo) | +| 4 | Publisher Mojo: invalid schemaRef → fail fast | unit | +| 5 | Signature verifier: signed archive → returns true | unit | +| 6 | Signature verifier: tampered archive → returns false | unit | +| 7 | Signature verifier: wrong public key → returns false | unit | +| 8 | Schema validation reuse: invalid JSON Schema в dict → publisher fails | unit | ---- - -## Effort - -| Step | Effort (CC) | Notes | -|---|---|---| -| 1. `installed_bundles` table migration | 1h | Standard Liquibase | -| 2. `InstalledBundle` JPA entity + repo | 1h | Standard JPA | -| 3. `BundleResolver` (fetch + parse) | 6h | HTTP client + YAML + JSON parse | -| 4. `BundleSignatureVerifier` (ed25519) | 4h | BouncyCastle JCA | -| 5. `BundleCatalogController` (read endpoints) | 3h | Standard REST | -| 6. `BundleInstallController` (dry-run + apply) | 8h | Transactional, conflict detection, atomic | -| 7. Topological install order + dep resolution | 4h | Kahn's algorithm на dict deps | -| 8. Frontend `BundleCatalogPage.tsx` | 6h | Standard list + search + filter | -| 9. Frontend `BundleInstallModal.tsx` (dry-run preview) | 6h | Preview UI, diff visualization | -| 10. Frontend `InstalledBundlesTab.tsx` | 4h | List + uninstall action | -| 11. Bundle publisher CLI (Maven plugin) | 8h | Sign + upload to Nexus | -| 12. i18n keys (~25 strings ru/en) | 1h | Standard | -| 13. Tests (17 cases) | 16h | testcontainers + RTL | -| 14. Docs (admin guide + publisher guide + ops runbook) | 6h | docs/user-guide/marketplace.md | -| **Total** | **~74h (~10 рабочих дней)** | within 1.5 sprints | +No integration / E2E tests — no runtime behavior changes. --- ## Open questions -1. **Где будут жить public ДЗЗ catalog editors?** Кто signs verified bundles от имени community? Suggestion: ЦУОД core team initially, expand by invitation later. - -2. **Bundle CI/CD pipeline для авторов?** Authors write bundle.yaml + sample data → CI signs + uploads to Nexus? Yes — provide Maven plugin (`ordinis-bundle-publisher`) which integrates с GitLab CI. ~1 day extra effort. - -3. **Versioning policy?** SemVer (major.minor.patch). Breaking schema changes = major bump. Recommend documenting в `docs/user-guide/bundle-authoring.md`. - -4. **Sample data ownership после install?** Records seeded из bundle — owned by «system» user, audit log helps trace. Customer-edited records → owned by customer. - -5. **Cross-bundle FK?** Bundle X dict X-A references bundle Y dict Y-B. Allowed? Suggestion: yes, declare explicitly в `dependencies`, resolver enforces install order. - -6. **Bundle update flow?** v1: deinstall + install new version (loses customer-edited records). v2: incremental upgrade с migration. Add to backlog. - -7. **Integration with AI schema assist?** AI suggestion → save as bundle → publish? Это **product-market-fit gold**: customer создаёт schema с AI, publish'нет в catalog, monetize'нет / share'нет. v2 idea, defer. +1. **Where do signing keys live?** GitLab CI variables protected? Vault? Corp HSM? → Defer to ops decision при implement. +2. **Nexus namespace structure?** `ordinis-bundles/private////` — fine для v1. Multi-tenant adjustments в v2. +3. **Migration breaking?** Existing `ordinis-cuod-bundle` deployment relies on Maven module loaded at app startup. Manifest spec adds metadata, doesn't change loading. **Verify:** ordinis-app still loads bundle when manifest.yaml present но new spec module not on classpath (graceful degradation для existing v2.14.0 prod). --- -## Combined value with `ai-schema-assist.md` +## Distribution plan -Эти два дизайна имеют **massive synergy**: +Publisher Maven plugin distributed как corp internal artifact в Nexus. Authors add к their bundle's `pom.xml`: -| Без AI + без marketplace | + AI | + Marketplace | + Оба | -|---|---|---|---| -| Customer пишет 40 schemas вручную ~3 недели | Customer пишет 40 schemas с AI ~3 дня | Customer берёт готовый bundle ~30 сек | Customer описывает домен («гидрометеостанции») → AI генерит bundle → publishes → другие installer'ят | +```xml + + + + cloud.nstart.terravault.ordinis + ordinis-bundle-publisher + 1.0.0 + + + +``` -**Compounding value loop:** AI lowers bar to create bundles → marketplace enables sharing → more bundles → more demand → more AI usage → more bundles. +Then `mvn ordinis:bundle-publish` triggered by GitLab CI on tag push (e.g. `cuod-bundle-1.0.0`). + +Bundle.tar.gz uploaded к Nexus. No deployment changes к prod ordinis cluster — bundle archive consumed только by external authors / sales demos. --- -## Recommendation +## Success criteria -**Defer until после v2.14.0 prod stable + 2 weeks dogfood, + AI design `/office-hours`'d first** (поскольку AI affects bundle authoring flow). +**Quantitative:** +- Publisher Mojo completes < 60 sec на ЦУОД bundle (~40 dicts) +- Signature verification < 10ms +- Manifest validation catches 100% malformed manifests in unit tests -**Sequencing:** -1. v2.14.0 prod stable + dogfood ~2 weeks (current) -2. `ai-schema-assist.md` — `/office-hours` → `/plan-eng-review` → implement (5-7 days) -3. Use AI assist для production, gather feedback ~1 month -4. `dictionary-marketplace.md` — `/office-hours` → `/plan-eng-review` → implement (10-14 days) -5. Public catalog launch — Q3-Q4 2026 +**Qualitative:** +- Java team handoff doc references `bundle-format-spec.md` as authoritative spec +- Sales conversations с prospects use «signed versioned bundle» as differentiator +- After 1 month: zero «founder drifted into consumer UI work» violations (scope cap held) -**Это потенциально strongest product differentiator** для МДМ-продукта на рынке — combine AI + Marketplace + ДЗЗ domain expertise = **product nobody else has** в RU/CIS МДМ-сегменте. +**Failure criteria (rollback):** +- Publisher Mojo bugs surface в > 2 releases of ordinis-cuod-bundle → reconsider as build-time vs separate plugin +- No sales conversation uses bundle artifact within 3 months → format спека correct, distribution wrong (rare; spec stays anyway) + +--- + +## What I noticed about how you think (during /office-hours) + +- **Initial position «build full marketplace» (v1 doc) → accepted «defer entirely» recommendation от me → accepted «bundle hygiene» revision после cross-model challenge.** Two reversals in one session = openness to push-back. Это редко — большинство founders defend original scope harder. + +- **«давай как рекомендуешь»** = trust calibrated на анализ + cross-model agreement. You didn't dismiss subagent challenge OR over-weight it — accepted synthesis as honest reading. + +- **Cross-model challenge value:** subagent found 4 things I missed (sales artifact, Java handoff, founder psychology drift risk, «treading water» mis-framing). Без cross-model call recommendation бы вышел overly conservative. **Session demonstrates value of /office-hours Phase 3.5 even когда первый instinct = skip it.** + +- **Anti-scope-creep pre-commitment** — `Hard cap 2-3 days. No scope creep.` line — это founder-level discipline, не engineering preference. Most founders write «target effort 2-3 days» что effectively unconstrained. + +--- + +## Reviewer Concerns + +(Spec review skipped в этой session due to context length. Recommend `/plan-eng-review` separately когда implement starts, especially on:) +- Module dependency graph (`ordinis-bundle-spec` standalone OK? circular dep risk если consumed by ordinis-app later?) +- Nexus auth credentials lifecycle (GitLab CI vs Vault tradeoff) +- Compatibility: existing prod v2.14.0 deployment should keep working без bundle-spec module --- ## See also -- Companion: `ai-schema-assist.md` — AI-assisted authoring (sequencing prerequisite) -- Roadmap mention: `docs-internal/status/2026-05-05-state.md` § "v2 (governance / data quality / federation)" — Federation MDM-кластеров (related but not same) +- v1 SUPERSEDED (был «Dictionary Marketplace», full UI + registry + 10-14d). Reasoning for v1 → v2 revision lives в section «Approaches Considered». +- Companion: `ai-schema-assist.md` — separate /office-hours design, AI assist authorship. **Sequencing:** AI assist baseline measurement (P5 prereq) before this. This work doesn't compete для bandwidth — separable. +- Office-hours formal doc: `~/.gstack/projects/claude/zimin-main-design-bundle-hygiene-20260512.md` (cross-skill discoverable)