# Gallery Entry Shapes — Revised Position (after operator's "slightly different case" feedback)
> **Status**: DRAFT v2 for operator review (2026-07-22)
> **Author**: Mavis
> **Replaces**: `/workspace/position-gallery-entry-shapes-recipes-and-code.md` v1
> **Key change**: **Promotion to top-level is earned by reusability.** App-specific modules stay inside the app entry. Cross-app modules get promoted to `gallery/modules/`. The gallery is a *promotion system*, not just a *list system*.
---
## TL;DR
The operator is right — vibecoder and freshcloud-mail are different cases:
- **VibeCoder's modules are CROSS-APP** (ai-toast, ai-mic, etc. — any FreshVibe app uses them). They live at `gallery/modules/`. VibeCoder itself is just a consumer; it doesn't need a gallery entry.
- **freshcloud-mail's modules are APP-SPECIFIC** (mail-engine-smtp, mail-engine-imap — only freshcloud-mail uses them). They live at `gallery/apps/freshcloud-mail/modules/`, not at top-level. freshcloud-mail itself IS a gallery entry because it's a platform-level FreshCloud app.
The "slightly different case" is: **where the modules live depends on whether they cross apps, not on whether they're a module or a bundle**. The promotion rule is the same either way.
The constitutional rule:
- Recipe books **always** in the gallery (non-negotiable)
- Code defaults to "in the gallery" but can be external
- Location is the discriminator: top-level `modules/` = cross-app; inside `apps//` = app-internal
---
## 1. The "promotion" model
The gallery is a **promotion system**. A module is born inside an app. If multiple apps start using it, it gets **promoted** to `gallery/modules/`. If only one app uses it, it stays inside `gallery/apps//modules/`.
**Why this matters:**
- Cross-app modules get the full gallery treatment: top-level listing, registry index, consumers tracked, facets, dependency graph, swappability
- App-specific modules get the app's namespace: bundled under the app entry, recipe book inside the app, code inside the app
- The gallery's `modules/` directory is the *honour roll* of reusable components
- The gallery's `apps//modules/` is the *private* subdirectory of platform apps
**For freshcloud-mail:**
- The 17 mail-engine modules are mail-specific
- They stay inside `apps/freshcloud-mail/modules/`
- They do NOT promote to `gallery/modules/` (because no one else needs an SMTP client)
- The `freshcloud-mail-engine-bundle` lives at `apps/freshcloud-mail/bundles/freshcloud-mail-engine-bundle/`
**For VibeCoder:**
- The ai-bundle modules (ai-toast, ai-mic, etc.) are cross-app
- They live at `gallery/modules/`
- They are tracked in `registry.json` with consumers: `[vibecoder, freshcards, vibescope, oscar]`
- The `ai-bundle` lives at `gallery/bundles/ai-bundle/`
- VibeCoder itself is NOT a gallery entry — it's a sovereign consumer
---
## 2. The new structure
```
gallery/
├── modules/ ← CROSS-APP modules (the "promoted" ones)
│ ├── ai-toast/ ✅ extracted
│ ├── ai-mic/ ❌ TODO (lives in ai-shell)
│ └── ...
│
├── bundles/ ← CROSS-APP bundles
│ ├── ai-bundle/ (11 modules, all cross-app chrome/AI)
│ └── ...
│
└── apps/ ← APP entries (NEW — proposed 2026-07-22)
├── freshcloud-mail/ ← the platform's email app
│ ├── module.json (kind: "app", code_location: "external",
│ │ source_location: "avidtech6/freshmail-2")
│ ├── module-meta.json
│ ├── recipe-book/ ← FVW §11 (always in gallery)
│ │ ├── recipe.md
│ │ ├── codex.md
│ │ ├── rules.md
│ │ ├── module-meta.json
│ │ ├── ingredients.json
│ │ ├── diffs/
│ │ ├── trace-atlas/
│ │ ├── dna/
│ │ ├── plan.md
│ │ └── coverage-matrix.md
│ ├── modules/ ← APP-INTERNAL modules (the 17 mail-engine ones)
│ │ ├── mail-engine-smtp/
│ │ │ ├── module.json (kind: "app-internal-module", code_location: ???)
│ │ │ ├── recipe-book/ (always)
│ │ │ └── src/ (if small enough to vendor)
│ │ ├── mail-engine-imap-client/
│ │ └── ... (15 more)
│ └── bundles/ ← APP-INTERNAL bundles
│ └── freshcloud-mail-engine-bundle/
│ ├── module.json (kind: "bundle", kind_subtype: "app-internal")
│ ├── bundle-manifest.json
│ └── bundle-features.md
│
└── (other platform apps, future: freshcloud-calendar, freshcloud-contacts, etc.)
```
**For sovereign apps that are NOT in the gallery (like vibecoder-standalone, fvs):**
They don't appear in the gallery at all. They just consume `gallery/modules/` and `gallery/bundles/`. The fact that they're sovereign consumers doesn't need a gallery entry.
But should they have a recipe book in the gallery? Two options:
- **Option A: Yes, register them as `apps//` with `code_location: "external"` and recipe book in gallery.** Same shape as freshcloud-mail, but the recipe book is "for reference" not "canonical".
- **Option B: No, they're not in the gallery.** Only platform apps (FreshCloud suite) get gallery entries.
**My recommendation: Option A.** Every sovereign app should be discoverable in the gallery, with a recipe book (for reference) and a pointer to the source repo. This way:
- The gallery is the discovery surface for "what apps exist in the FreshVibe ecosystem"
- VibeCoder, freshcards, vibescope, oscar, fvs all show up in the gallery
- The gallery has both: cross-app modules (top-level) + apps + their internal modules
- The pattern is consistent: recipe book always in gallery, code in gallery or external
So the structure becomes:
```
gallery/
├── modules/ ← cross-app modules
├── bundles/ ← cross-app bundles
└── apps/
├── freshcloud-mail/ ← platform app (FreshCloud)
├── vibecoder-standalone/ ← sovereign consumer (kind: "app", code_location: "external")
├── freshcards-standalone/← sovereign consumer
├── vibescope-standalone/ ← sovereign consumer
├── fvs/ ← sovereign consumer (kind: "app", code_location: "external")
└── ...
```
Each `apps//` has:
- `module.json` with `kind: "app"`, `code_location: "gallery" | "external"`, `source_location: "..."` (if external)
- `recipe-book/` (always)
- `modules/` (if app has internal modules)
- `src/` (if `code_location: "gallery"`)
---
## 3. The constitutional rule (final)
> **Every gallery entry has a recipe book in the gallery.** Always. Non-negotiable. The recipe book is the constitutional shape.
>
> **Location is the discriminator:**
> - `gallery/modules//` → cross-app module (promoted)
> - `gallery/bundles//` → cross-app bundle
> - `gallery/apps//` → app (sovereign consumer OR platform app)
> - `gallery/apps//modules//` → app-internal module (not promoted)
> - `gallery/apps//bundles//` → app-internal bundle
>
> **Code defaults to "in the gallery"** for new modules. Code may be external if the entry is too large or sovereign. The `code_location: "gallery" | "external"` field declares it.
>
> **Promotion rule**: a module lives at `apps//modules//` until 2+ apps use it, at which point it can be promoted to `modules//`. The operator decides when to promote.
---
## 4. The "tag it differently" rule (operator's insight, refined)
The operator said: "if a module has code in it, it needs to be tagged in one way. Well if it's only got recipe books then it means to be typed in another way."
**Refined: it's not about code vs no-code, it's about location + code_location:**
| Path | What | `kind` | `code_location` |
|---|---|---|---|
| `gallery/modules/ai-toast/` | cross-app module, code in gallery | `module` | `gallery` |
| `gallery/modules//` (hypothetical) | cross-app module, code elsewhere | `module` | `external` |
| `gallery/bundles/ai-bundle/` | cross-app bundle | `bundle` | n/a |
| `gallery/apps/freshcloud-mail/` | platform app, code in freshmail-2 | `app` | `external` |
| `gallery/apps/vibecoder-standalone/` | sovereign app, code in vibecoder-standalone | `app` | `external` |
| `gallery/apps//modules/mail-engine-smtp/` | app-internal module | `app-internal-module` | `gallery` or `external` |
| `gallery/apps//bundles/freshcloud-mail-engine-bundle/` | app-internal bundle | `app-internal-bundle` | n/a |
**The "tag" is the path + the `kind` field together.** No new "code_location" discriminator needed beyond what's already in v1.
---
## 5. The freshcloud-mail migration (revised)
Per the v1 plan, freshcloud-mail would be at `gallery/apps/freshcloud-mail/` with `code_location: "external"`. The 17 mail-engine modules would also be at `apps/freshcloud-mail/modules//`, NOT at top-level.
**Why this is right:**
- The 17 modules are mail-specific. They don't generalize. They don't promote.
- Putting them at `gallery/modules/` would inflate the registry with non-reusable entries
- The recipe books still live in the gallery (in the right subdirectory)
- The 17 modules are discoverable *through* the freshcloud-mail app entry
- The freshcloud-mail-engine-bundle is an internal bundle that lists them
**One concern**: what if someone in the future wants to use a single mail-engine module in another app? Then it gets *promoted* — moved to `gallery/modules/mail-engine-smtp/`, with a `promoted_from: "apps/freshcloud-mail/modules/mail-engine-smtp"` field in `module-meta.json` for traceability.
---
## 6. What about VibeCoder being a "collection of modules"?
The operator said: "apps can sometimes be a collection of modules in the gallery already eg vibecoder has many modules"
I now think this means: VibeCoder is a *consumer* of many gallery modules (its 11 chrome/AI modules). It's a "collection of modules" in the sense that it composes them. The modules themselves are at `gallery/modules/`. VibeCoder itself doesn't need to be at `gallery/apps/vibecoder-standalone/` *unless* it has its own internal modules that aren't in the gallery.
But per my refined recommendation, sovereign consumers SHOULD be in the gallery as `apps//` with `code_location: "external"`. So VibeCoder would be at `gallery/apps/vibecoder-standalone/` with:
- A recipe book (for reference — VibeCoder's constitutional shape)
- A `module.json` with `kind: "app"`, `code_location: "external"`, `source_location: "avidtech6/vibecoder-standalone"`
- An empty `modules/` (because VibeCoder's modules are all in `gallery/modules/`, not internal)
- A note in `module-meta.json`: "consumer of: [ai-toast, ai-mic, ai-stt, ...]"
This way VibeCoder is discoverable in the gallery, and you can see "this app uses these gallery modules" by looking at its entry.
---
## 7. Comparison: v1 vs v2
| Question | v1 (wrong) | v2 (revised) |
|---|---|---|
| Where do freshcloud-mail's 17 modules live? | `gallery/modules/mail-engine-*/` (top-level) | `gallery/apps/freshcloud-mail/modules/mail-engine-*/` (internal) |
| What's the "slightly different case" for freshmail? | "It's an app, not a module" | "Its modules are mail-specific, not cross-app" |
| Is VibeCoder a gallery entry? | Not addressed | Yes, as `gallery/apps/vibecoder-standalone/` with `code_location: "external"` |
| Is there an `apps/` folder? | Yes (new) | Yes (new), with both platform apps and sovereign consumers |
| Promotion rule? | Not stated | Stated explicitly: "promoted when 2+ apps use it" |
---
## 8. Open questions (revised)
1. **The promotion threshold** — is "2+ apps" right, or should it be "explicitly promoted by operator"? I'd say operator, since the operator knows the strategic intent.
2. **Sovereign consumer apps** — should all sovereign consumers be in the gallery? Or only those that have internal modules? My recommendation: all, for discoverability.
3. **The freshcloud-mail-engine-bundle** — is it an "app-internal bundle" or just a regular bundle listed at `gallery/bundles/`? My recommendation: internal, because it lists app-internal modules.
4. **The promotion direction** — when a module gets promoted, does its OLD location (inside the app) become a pointer, or does the code get deleted? My recommendation: pointer (for backwards compat), with `promoted_to: "gallery/modules//"`.
5. **The 17 mail-engine modules and `code_location`** — should the small ones (e.g. simple IMAP wrapper) be vendored into the gallery, and the large ones (e.g. SMTP server) stay external? Or all external because freshmail-2 is the source of truth? My recommendation: all external for now, with the recipe book in gallery. Promotion to gallery code can happen later if specific modules are reused.
---
## 9. What to do next (revised)
### Step 1: Operator approval
Read this doc, confirm or amend the promotion rule + the apps/ folder scope.
### Step 2: Add FVW v8.2 doctrine
Section: "Gallery Entry Shapes (Revised)" — covers the promotion rule, the apps/ folder, the location-based discrimination. Update CHANGELOG (v8.2 minor bump).
### Step 3: Add `apps/` to the gallery
- Create `gallery/apps/` folder
- Write `apps/README.md` documenting the pattern (both platform apps and sovereign consumers)
- Update registry schema to include `kind: "app"`, `kind: "app-internal-module"`, `kind: "app-internal-bundle"`
### Step 4: Migrate freshcloud-mail (the test case)
- `gallery/apps/freshcloud-mail/` with the structure from §2
- 17 modules at `apps/freshcloud-mail/modules//`
- 1 bundle at `apps/freshcloud-mail/bundles/freshcloud-mail-engine-bundle/`
- Recipe book pulled from `avidtech6/freshmail-2/recipes/`
- Update `registry.json`
### Step 5: Migrate other apps (sweep)
- `gallery/apps/vibecoder-standalone/` (code_location: "external")
- `gallery/apps/freshcards-standalone/`
- `gallery/apps/vibescope-standalone/`
- `gallery/apps/fvs/` (FreshVibeStudio — recipe book is for reference, code is in freshvibestudio)
- Each is small: just module.json + recipe-book/ + module-meta.json
- This is optional but recommended for discoverability
### Step 6: Sync workflow
For entries with `code_location: "external"`, sync the recipe book from `source_location/recipes/` to the gallery copy. Daily cron + webhook.
### Estimated effort
- Step 1: 0 (read this doc)
- Step 2: 1 hour (FVW v8.2 doctrine)
- Step 3: 1 hour (apps/ + registry schema)
- Step 4: 3-4 hours (freshcloud-mail migration + 17 modules)
- Step 5: 2-3 hours (sweep all 5 apps)
- Step 6: 1-2 hours (sync workflow)
- Total: ~10 hours, can be spread over a week
---
## 10. The "app gallery" question, again
**Was there an app gallery built?** No (confirmed earlier).
**Should there be one?** Yes, as `apps/` in the existing `avidtech6/fv-module-gallery`.
**What goes in it?** Sovereign consumer apps + platform apps + their internal modules/bundles.
**Is this the same as the module gallery?** Yes — one gallery, three top-level folders: `modules/`, `bundles/`, `apps/`.
---
## Related docs
- `/workspace/position-gallery-entry-shapes-recipes-and-code.md` (v1, superseded)
- `/workspace/gallery-investigation.md`
- `/workspace/canonical-home-proposal.md`
- `/workspace/facets-in-fvw-v8.md`
- `/workspace/recipe-book-shape.md`
- `/workspace/audit/freshcloud-mail-recipes/README.md` (recipes merged into freshmail-2 on 2026-07-21)
- `avidtech6/fv-module-gallery/gallery-pact/registry-plan.md` (the gallery's design doc)