# Design: Bundle Hygiene (formerly "Dictionary Marketplace")

**Author:** zimin.an
**Date:** 2026-05-12
**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

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 сегодня).

**Revised:** Formalize bundle format spec + publisher CLI. **No consumer-side UI.** This is **bundle hygiene**, не marketplace.

**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)

**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.

---

## Premises (validated через `/office-hours` 2026-05-12, including cross-model challenge)

**P1.** Marketplace UI/registry **value scales с N customers.** At N=1 → near-zero. Build **only когда customer #2 LOI/contract стадия с extensibility language**.

**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
```

**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
```

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-id>/<version>/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

```
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)

                          │
                          │ mvn ordinis:bundle-publish
                          ▼
                  Corp Nexus
                  ordinis-bundles/private/cuod/
                  └── ru.cuod.dzz-ground-segment/
                      └── 1.0.0/
                          ├── bundle.tar.gz
                          └── bundle.sig
```

No new pods. No runtime changes к ordinis-app / ordinis-admin-ui. Only build-time tooling + Nexus artifacts.

---

## Approaches Considered

### 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 |
|---|---|---|
| 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.** |

**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.

---

## Test plan

| # | Test | Type |
|---|---|---|
| 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 |

No integration / E2E tests — no runtime behavior changes.

---

## Open questions

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/<customer>/<bundle-id>/<version>/` — 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).

---

## Distribution plan

Publisher Maven plugin distributed как corp internal artifact в Nexus. Authors add к their bundle's `pom.xml`:

```xml
<build>
  <plugins>
    <plugin>
      <groupId>cloud.nstart.terravault.ordinis</groupId>
      <artifactId>ordinis-bundle-publisher</artifactId>
      <version>1.0.0</version>
    </plugin>
  </plugins>
</build>
```

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.

---

## Success criteria

**Quantitative:**
- Publisher Mojo completes < 60 sec на ЦУОД bundle (~40 dicts)
- Signature verification < 10ms
- Manifest validation catches 100% malformed manifests in unit tests

**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)

**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

- 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)
