# Component Integrity: Repo to Database Reconciliation

> A `remits-cli` skill reference. **Load this when** you are about to run `components sync` or `components commit`, or a sync reported something you did not expect. This is the highest-impact failure mode in remits-cli.
>
> The table of contents below carries **real line numbers** (`- L84  Some Heading`), resolved when
> this file is installed, so they are never stale. Read the head, pick your sections, and offset-read
> only those. The entry text is the heading verbatim, so it also greps.

## Table of Contents

- [Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)](#component-integrity-rules-repo--database-reconciliation-read-before-any-synccommit)
  - [How the platform reconciles the repo into the database (the mechanism you must understand)](#how-the-platform-reconciles-the-repo-into-the-database-the-mechanism-you-must-understand)
  - [The surfaces and their intended behavior](#the-surfaces-and-their-intended-behavior)
  - [Intended workflows](#intended-workflows)
  - [Pre-sync safety check (confirm ALL before `components sync` or `components commit`)](#pre-sync-safety-check-confirm-all-before-components-sync-or-components-commit)
  - [If something looks wrong — stop, don't paper over](#if-something-looks-wrong--stop-dont-paper-over)

## Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)

Component source-of-truth mistakes are the highest-impact failure in remits-cli. A repo/DB mismatch can
hard-delete live components, spawn duplicates, renumber files, or leave the database and repo describing
different implementations. These rules override the normal fast loop whenever they conflict.

### How the platform reconciles the repo into the database (the mechanism you must understand)

> **First, check which branch you are on.** Everything in this section describes a **TRUNK** sync. Syncing
> from a **non-trunk branch** is a different, much safer operation — it writes `ComponentVariant` overlays
> only and can never create, delete, rename, or overwrite a live component row. Run
> `remits-cli components status` to see which mode your working tree is in, and read
> `branch-variants.md` for the variant-branch rules (which have their own hazard: tombstones).

`remits-cli components sync` (and the sync phase of `components commit`) calls
`GitHubClient.syncFromRepository`. On the account's **trunk branch** it is a **full two-way reconcile in
which the GitHub remote is authoritative over the database.** It reads the **remote repo ZIP — not your
local working tree** — and:

- **Match / update:** each component is matched to a DB row by the **numeric id prefix of its files**
  (`58_x.groovy` → component 58), not by name. Matching files overwrite that component's DB fields
  (source/schema/html/etc.).
- **Rename:** changing the component's `name:` in `.meta.yml` is supported as a normal update **as long as
  the numeric id prefix stays the same**. On staging, the CLI updates the id/name cache aliases and prunes
  stale old-name aliases for that id. On trunk sync, the platform canonicalizes the repo filenames to the
  current component name (`58_OldName.groovy` with `name: New Name` becomes `58_NewName.groovy`, plus
  sidecars) and reports those moves in `syncResults.renamed`.
- **Create:** a file whose id prefix is **not** a live component on the account — including any `new_*` file —
  is created as a **brand-new DB row with a fresh server-assigned id**, and the platform renames the repo files
  to that id (the `Rename X→Y after component creation` / `Delete old file` commits).
- **Delete:** after the create/update pass, **any live DB component whose id has no matching repo file is
  hard-deleted** (`deleteMissingComponentsFor`), in reverse-dependency order. This runs only when the ZIP
  "looks healthy" (contains `account-info.json` or `README.md`). Exempt from deletion: `auxiliary` components,
  README-purpose prompts, and AGENT-purpose prompts.

> ### `auxiliary: true` opts a component OUT of the repo entirely — in BOTH directions
>
> This is a silent trap, so know it before you author a sidecar. `auxiliary: true` does not merely
> "de-emphasize" a component:
>
> - **Repo → platform:** the sync **skips the file outright**. A `new_*` component whose `.meta.yml` says
>   `auxiliary: true` is never created, so it **never gets a real id** and the file is never renamed. It
>   looks like the sync silently ignored your work — because it did.
> - **Platform → repo:** the component's save hooks skip pushing source to GitHub, and repo initialization
>   omits it.
> - It is also excluded from `getInformation()` (so AI agents do not discover it) and exempt from the
>   deletion pass above.
>
> **Use `auxiliary: true` only for genuinely throwaway components** — ad-hoc reports, experiments, and the
> ephemeral fixtures a Test creates and deletes at runtime (those are created in Groovy with
> `auxiliary: true` and must never touch the repo).
>
> **Use `auxiliary: false` for anything durable** — above all a Test suite that is a regression guard. If you
> want it versioned in git, addressable by a stable id, or discoverable by another agent, it is not
> auxiliary. Symptom to recognize: *"I added `new_Foo.groovy`, synced, and it neither appeared on the account
> nor got renamed."* Check the sidecar's `auxiliary` flag first.

**The single most important consequence:** the id in a component's **filename is load-bearing**. If a file's id
no longer matches its DB row (a renumber or move across ids), the next sync will **create a duplicate at the
new id and hard-delete the original at the old id**. If a component's files are missing from the repo at sync
time, that component is **hard-deleted from the DB**. This is exactly how a prior session deleted live schemas
and an embeddable.

**Therefore: never renumber, rename-across-ids, or remove component files as a side effect.** Name-only
renames are fine when every file keeps the same numeric id; either rename the local filename stem yourself or
let trunk sync canonicalize it from `.meta.yml`. Before any sync, the repo must already mirror the live DB:
every live component present at its real id, and nothing extra.

### The surfaces and their intended behavior

| Command | What it touches | Danger |
|---|---|---|
| `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. A plain stage or `--workset` RECONCILES the lane (entries outside the manifest are dropped); `--changed-only` merges. | none for the DB; a lane you share with another agent is reconciled by the first two |
| `remits-cli components status` | Reads this lane's staging scope (account/user/branch/workspace) and branch resolution, and lists every other lane on the branch. | none |
| `remits-cli components clear` | Clears THIS lane's staging cache without changing DB or git. Never touches another workspace lane. | none |
| `remits-cli components sync` **on trunk** | **Server-side git→DB reconcile of the whole account** (create/update/**delete**/rename). Reads the pushed remote; ignores local files. | **high** |
| `remits-cli components sync` **on a variant branch** | Writes `ComponentVariant` overlays for that branch only. Never touches trunk rows or the account's trunk branch. When the checkout identifies a subscribing account, the branch-local `account-info.json` is refreshed for that subscriber; `--dry-run` reports the plan without writes. | medium (a missing file becomes a **tombstone** that hides the component from subscribers) |
| `remits-cli components commit` | **One shot:** changed-source compile validation (a `--changed-only` MERGE stage, so it never reconciles the lane) + `git add -A` + commit + `git push` + **`components sync`** + `git pull --ff-only`. Blindly stages the *entire* working tree (including any drift) and reconciles it into prod. Inherits the danger of whichever sync mode the branch selects. | **highest on trunk** |

Key implications:
- **`stage` never touches the DB or git** — stage and test as much as you want. It is safe *for your
  components*; it is not inert *for the lane*. A full snapshot and `--workset` both reconcile the lane to
  their manifest, so on a lane shared with another agent they drop that agent's staged entries. Use
  `--changed-only` when you mean "add mine, leave theirs".
- **A COMPILE-VALIDATION refusal still wrote the lane; a policy refusal does not.** Read the status code,
  because the two refusals leave opposite states behind:
  - **422 (compile validation)** — the entries are already stored. Validation runs after the write because
    the compile has to resolve through the staged overlay to see the source it is judging. The response
    says so with `laneHoldsRejectedSource`. Until you fix the source and re-stage, a run in that lane
    resolves the broken component.
  - **409 (account policy, or another agent's edit lease)** — refused before anything was written. The lane
    is exactly as it was; nothing to undo.
- **"Compile validation: NOT RUN" is a real answer.** The check keys off the git changed set. If git
  cannot identify one — you are not in a working tree, or git failed — there is no workset to validate and
  the CLI says `workset-unknown` rather than implying a pass.
- **`components commit` is the most dangerous command**, not a mere convenience wrapper: it first
  merge-stages and compile-validates the changed runtime source, then `git add -A`, commits, pushes, and
  immediately syncs. Never run it while the tree contains drift or unexplained changes. Prefer the explicit,
  observable `components stage → git commit → git push → components sync → git pull` sequence so each
  phase can be inspected.
- **`components commit` on trunk refuses before any git write unless `--yes` is present** — with or
  without `--safe`, and with `--skip-git`. Trunk has no dry-run sync plan, so an explicit acknowledgement
  is the only honest gate; the CLI must not create or push a commit and only then discover it.
- **A commit that fails after the push says so.** `phase`, `pushed`, `pushedSha` and `nextStep` (in
  `--json`, one document on stdout) tell you whether the remote now holds work the platform has not
  reconciled. If it does, fix the cause and run `components sync` — do not re-run `components commit`.
- **A commit refuses when the push and the sync would hit different repositories** — this checkout's
  `origin` versus the repository the platform syncs for the account the command resolved. Fix the account
  (account-info.json, `--account-id`) or run from the right checkout; never force it.
- **A commit refuses while its branch is checked out in another worktree.** Worktrees of one branch share
  its ref: a pull in one moves `HEAD` under the others, and `git add -A` from a stale one reverts work that
  has already landed. Give each agent its own clone.
- **`components sync` acts on the pushed remote**, so local edits are invisible to it until committed **and
  pushed**, and a drifted **remote** is dangerous even when your local tree looks fine.
- **Runtime-compiled component source is validated before durable writes.** When a trunk sync or variant
  sync sees changed/new Groovy source for a `Reader`, `Action`, `Embeddable`, `HtmlTemplate`, `Rule`,
  `Test`, `Agent`/`Utility`, or `Tool`, the platform compiles it before accepting the DB row or
  `ComponentVariant` overlay. A compile failure lands in `syncResults.errors` and the branch SHA cache is
  not advanced, so fix the source and retry the same sync.
- After a successful non-dry-run `components sync` / `components commit`, the server clears the full
  staging scope for that lane (account/user/branch/workspace). This is the expected clean state: old Redis aliases should not keep shadowing
  the newly synced DB rows. `components sync --dry-run` intentionally leaves staging untouched.

### Intended workflows

**Change existing components (normal path):**
1. Edit files under `components/` **keeping each component's existing numeric id** (use `new_*` only for
   genuinely new components). To rename a component, update `name:` in its `.meta.yml`; keep the id prefix
   fixed. Renaming the file stem is optional before trunk sync because the platform will canonicalize it, but
   doing it locally keeps the working tree easier to read.
2. `remits-cli components stage` → verify in test mode. Iterate (edit → stage → run).
3. When ready to promote: pass the pre-sync safety check below, then
   `git add -A && git commit && git push`, `remits-cli components sync`, `git pull --ff-only`.

**Create a new component:** add `new_Name.groovy` (+ `.json` / `.meta.yml` as applicable). Sync assigns the
durable id and renames the files. Standalone Prompts live in `components/prompts/new_Name.md`, and their
sidecar must include `name`, `summary`, `description`, and `purpose` (usually `CUSTOM`). AGENT prompts do
not live there; they are the `.md` sidecar beside the Utility in `components/agents/`. Do not create a
direct database row to work around an id/name mismatch, and never create a replacement for a component
that was unexpectedly deleted or renumbered.

**Delete a component (deliberate only):** remove **all** of that component's files from the repo, confirm via
`git status` that only those files are gone, then sync — the delete phase removes exactly that DB row. Deletion
is a real, supported outcome of a missing file, which is precisely why an *accidentally* missing or renamed
file is catastrophic.

### Pre-sync safety check (confirm ALL before `components sync` or `components commit`)

- **You know which sync mode this branch selects.** `remits-cli components status` states it outright. On a
  variant branch the id/delete/renumber checks below apply to the **overlay set** instead: confirm every
  component absent from the branch is *meant* to be tombstoned for subscribers.
- The user intends durable platform promotion now — not just local edits, staging, or verification.
- `git fetch origin` has run, and this checkout matches the remote branch the platform will sync:
  `git log origin/<branch>..<branch>` and `git log <branch>..origin/<branch>` are both empty. A local
  commit that is not pushed is invisible to Remits; a local branch behind the remote is a stale baseline.
- `git status --short` shows only intended changes; every rename/delete is explained. **No component file has
  been renumbered to a different id.**
- Local branch is committed and pushed; sync will read the intended remote commit. If a sync just
  completed, `git pull --ff-only origin <branch>` has run before any next git operation, because sync may
  push generated artifacts back to the branch.
- Local filenames and live inventory (`mcp_account_view`) **agree on id and name for every component**: no live
  component appears locally under a different id, and no expected component is missing a repo file.
  > **Do not run this comparison against `account-info.json` alone — it can still be incomplete.** That
  > file omits `auxiliary: true` components by design, so every auxiliary component looks like a repo file
  > with no DB row, i.e. exactly the "a trunk sync will CREATE a duplicate" signal this check exists to
  > catch. README- and AGENT-purpose Prompts are also intentionally absent from the `Prompts` collection:
  > they live at repo-root `README.md` and as `.md` sidecars in `components/agents/`, not in
  > `components/prompts/`. Before treating a flagged component as drift, confirm against the live row:
  > `mcp_component_view`, or `mcp_run_action controlAction:"describe"` for an Action. A component that
  > answers is not an orphan.
- You can state the expected create/update/delete set. **If any delete or renumber is unexpected, stop.**

### If something looks wrong — stop, don't paper over

If sync reports unexpected `deleted` / `created` / `renamed`, uniqueness errors, or missing components — or you
discover id drift — **stop. Do not re-run sync, do not `components commit`, and do not create replacement
components to "make ids line up" or replace a deleted component.** Those actions compound the corruption.
Run `remits-cli doctor local-state`, preserve the active actor's repo-local session log and tool
responses, and reconcile source-of-truth first.

**Safe recovery pattern (repo ↔ DB drift):**
1. Establish DB truth: `mcp_account_view` for the full live inventory; `mcp_component_view` to confirm and read
   exact sources.
2. Fix the **local tree to mirror the live DB** — rename component files back to their real DB ids, reassemble
   any split components, remove orphan/duplicate files, and **refresh any local source that differs from the
   live DB** (the DB is the running truth; a stale local file would overwrite good DB source on sync).
3. Keep files for any components that were wrongly deleted so sync **re-creates** them (new ids are fine —
   schemas are keyed by title, agents link tools by name).
4. Make the remote authoritative **non-destructively**: commit the corrected tree, then
   `git merge -s ours origin/<branch>` (keeps your tree, supersedes drifted remote history) and a fast-forward
   `git push` — no force-push.
5. Run **one** `components sync`: it updates everything to identical, creates the missing components, and
   deletes nothing. Verify with `mcp_account_view`.

If the mismatch is a genuine platform/tooling defect (not agent drift), follow the Back-Stage Escalation
Workflow in `troubleshooting.md` instead of improvising.
