# AI Publishing Guide — How to Publish a Module to the Gallery > **Audience**: AI sessions that are told to "publish X to the gallery" or "sync X with the gallery" or "mirror X to the gallery" > **Doctrine**: FVW v8.2 §17.5-§17.7 > **Status**: ACTIVE (per the 2026-07-22 gallery entry shapes decision) > **TL;DR**: Ask 4 questions. Never assume. The gallery is a publisher, not an author. --- ## When to read this Read this guide when you (the AI) are told ANY of the following: - "Publish X to the gallery" - "Sync X with the gallery" - "Mirror X to the gallery" - "Add X to the gallery" - "Make X discoverable" - "Push X to fv-module-gallery" **Don't** read this for "search the gallery" or "find modules in the gallery" — that's a different operation (read `gallery-pact/registry.json` and follow the links). --- ## The 4 Questions (ask the operator) When told to publish X, you MUST ask the operator these 4 questions. Don't assume any of the answers. Each answer becomes a field in X's metadata. ### Question 1: New or revibe? ``` Is X a brand-new module that has never existed before, or is X a revibe (a customisation/fork) of an existing module? ``` - **New** → X is a fresh thing, no precedent. Proceed to Q2. - **Revibe** → X is a fork of Y. Set `_forked_from: ""` in X's metadata. Proceed to Q2. ### Question 2: Where's the canonical home? ``` Where will the work on X actually happen? Which repo, which folder? ``` This is the **most important question**. The canonical home is where the work lives, where bugs get fixed, where versions get released. The gallery is **never** the default for this. - **Existing app repo** (e.g. `avidtech6/freshmail-2/modules/X/`) → canonical is the app, gallery gets a mirror - **The gallery itself** (only if no app fits) → canonical IS the gallery - **A new repo** (operator creates it) → canonical is the new repo, gallery gets a mirror **Do NOT default to the gallery.** Even if "the gallery feels like home", the operator decides. ### Question 3: Publish what? ``` What gets published to the gallery? - Recipes only (just the constitutional shape, ~30-200 KB) - Recipes + code (the full module, ~100 KB - few MB) - Full module + tests + bundle (everything, ~few MB) ``` - **Recipes only** → publish just `recipe-book/` (FVW §11: 3+5+2 = 10 items). No `src/`, no `tests/`. - **Recipes + code** → publish `recipe-book/` + `src/`. No `tests/` unless asked. - **Full** → publish everything: `recipe-book/`, `src/`, `tests/`, `package.json` or `composer.json`, etc. **Do NOT default to "with code"** unless the module is small or the operator explicitly says so. The 17 mail-engine modules in freshmail-2 are mail-specific — they get recipes-only, not code. ### Question 4: Fork of anything? ``` Is X a fork (or revibe) of an existing module? If yes, what? ``` - **Yes** → set `_forked_from: ""` in X's metadata - **No** → X is its own thing, no `_forked_from` This is the same as Q1's revibe question but more general — X could be a fork without being a "revibe" (e.g. X is a clean-room rewrite of Y). --- ## The `_module_kind` Decision After Q1-Q4, determine the `_module_kind`: | Q1 | Q2 | Q4 | `_module_kind` | |---|---|---|---| | New | app repo | not a fork | `original` (in app) + `gallery-mirror` (in gallery) | | New | app repo | fork of Y | `app-fork` (in app) + fork entry in gallery | | New | gallery | not a fork | `gallery-original` | | New | gallery | fork of Y | `app-fork` in `gallery/forks///` | | New | new repo | not a fork | `original` (in new repo) + `gallery-mirror` (in gallery) | | Revibe | app repo | fork of Y | `app-fork` (in app) + fork entry in gallery | | Revibe | gallery | fork of Y | `app-fork` in `gallery/forks///` | The gallery is NOT `original` — only apps are. The gallery can be `gallery-original` (canonical is gallery) or `gallery-mirror` (canonical is elsewhere). --- ## The Publish Workflow Once Q1-Q4 are answered, here's what you (the AI) do: ### Step 1: Create or update X in its canonical home ``` # If canonical is in an app repo cd /path/to/app-repo mkdir -p modules/X # Create module.json, module-meta.json, recipe-book/, src/, tests/ # Annotate _module_kind and _canonical_source per the answers # If canonical is in the gallery cd /path/to/fv-module-gallery mkdir -p modules/X # or forks//X for forks # Create the same files, _module_kind: "gallery-original" or "app-fork" ``` ### Step 2: Set up the mirror workflow (if canonical is NOT in the gallery) If the canonical is in an app repo, the app's CI must push mirrors to the gallery on release tags. Either: - **Push from app CI** (recommended): add a workflow to the app's repo that on release tag, pushes the module mirror to the gallery via PR or direct push - **Pull from source by gallery** (fallback): the gallery's `pull-mirrors.yml` workflow runs daily, pulling from `_canonical_source` ### Step 3: Push the initial mirror to the gallery If the canonical is in an app repo: ``` # Use the gallery's accept-mirrors workflow or open a PR git checkout -b add-X-mirror cp -r /path/to/app-repo/modules/X gallery/modules/X # Update module-meta.json: _module_kind: "gallery-mirror" # Update _canonical_source: "avidtech6//modules/X" # Update version, _last_verified, etc. git add gallery/modules/X git commit -m "Mirror: /modules/X" # Open PR against fv-module-gallery ``` If the canonical is in the gallery: nothing more to do, X is already in place. ### Step 4: Update `registry.json` Add X to the gallery's `registry.json`: ```json { "modules": { "X": { "module_kind": "gallery-mirror", // or gallery-original, app-fork, etc. "canonical_source": "avidtech6//modules/X", "version": "1.0.0", "last_verified": "", "tier": "generic", // per FVW §17 "tags": ["module", "..."] } } } ``` ### Step 5: Report back to the operator ``` Done. Module X is: - Canonical: avidtech6//modules/X - Gallery mirror: gallery/modules/X (kind: gallery-mirror) - Published: - Fork of: ``` --- ## The Cleanup Workflow (when source dies) You may also be told to "run cleanup" or "check for orphans". For that, see `gallery-pact/CLEANUP-WORKFLOW.md`. The short version: - Every `gallery-mirror` is checked daily - If source is dead for 30+ days, auto-promote to `gallery-original` - If source comes back, reattach --- ## Examples ### Example 1: Publishing freshcloud-mail-engine-bundle to the gallery ``` Operator: "Publish the freshcloud-mail-engine-bundle to the gallery" AI: "Before I do, 4 questions: 1. New or revibe? — New 2. Where's the canonical home? — avidtech6/freshmail-2/bundles/freshcloud-mail-engine-bundle 3. Publish what? — Recipes + bundle-manifest (no code, no module sources) 4. Fork of anything? — No" AI: "Got it. Creating gallery mirror at gallery/bundles/freshcloud-mail-engine-bundle/ as gallery-mirror." ``` ### Example 2: VibeCoder revibes ai-toast ``` Operator: "Revibe ai-toast in vibecoder" AI: "Before I do, 4 questions: 1. New or revibe? — Revibe (you said so) 2. Where's the canonical home? — avidtech6/vibecoder-standalone/modules/revibe/ai-toast-vibecoder-style 3. Publish what? — Recipes + code (default for revibes, confirm?) 4. Fork of anything? — Yes, fork of ai-toast (canonical at avidtech6/ai-shell/ai-toast)" AI: "Got it. Creating the revibe at vibecoder-standalone/modules/revibe/ai-toast-vibecoder-style/ as app-fork, and pushing fork entry to gallery/forks/vibecoder-standalone/ai-toast-vibecoder-style/." ``` ### Example 3: New module born in the gallery ``` Operator: "Create a new module called ai-helper, no specific app" AI: "Before I do, 4 questions: 1. New or revibe? — New 2. Where's the canonical home? — The gallery itself? (no app fits?)" Operator: "Yeah, the gallery for now. We don't have an app for it yet." AI: "Got it. Creating gallery/modules/ai-helper/ as gallery-original." ``` --- ## What NOT to do - ❌ Don't assume the gallery is canonical - ❌ Don't default to "publish with code" - ❌ Don't create a mirror without telling the operator - ❌ Don't skip any of the 4 questions - ❌ Don't publish a fork as `original` or `gallery-original` - ❌ Don't publish code from a `gallery-original` if the operator said recipes only - ❌ Don't auto-promote an orphan before 30 days (unless the operator sets a different threshold) --- ## See also - FVW v8.2 §17.5-§17.7 — Module Canonical Home + Mirror Model + Revibe-as-Fork - `gallery-pact/MIRROR-WORKFLOW.md` — how mirrors stay in sync - `gallery-pact/CLEANUP-WORKFLOW.md` — orphan → gallery-original flow - `gallery-pact/registry.json` — the index of all modules in the gallery - `/workspace/position-gallery-entry-shapes-v4.md` — the full position doc