# The Development Loop

> A `remits-cli` skill reference. **Load this when** you are building or changing a component: the edit to stage to verify to commit fast loop, end to end.
>
> 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

- [Two Workflows](#two-workflows)
  - [Test Mode vs Prod Mode](#test-mode-vs-prod-mode)
- [Verification Envelopes](#verification-envelopes)
- [Development Workflow](#development-workflow)
  - [The Golden Rule: Writing Code Is Not Finishing the Job](#the-golden-rule-writing-code-is-not-finishing-the-job)
  - [The Development Fast Loop](#the-development-fast-loop)
    - [Step 1: Understand the Request](#step-1-understand-the-request)
    - [Step 2: Make the Change](#step-2-make-the-change)
    - [Step 3: Stage to Platform](#step-3-stage-to-platform)
      - [Stage your workset, not the whole repo](#stage-your-workset-not-the-whole-repo)
      - [Three numbers, three questions](#three-numbers-three-questions)
    - [Step 4: Verify the Change](#step-4-verify-the-change)
      - [Many failures are usually few causes](#many-failures-are-usually-few-causes)
    - [Step 5: Iterate If Needed](#step-5-iterate-if-needed)
    - [Step 6: Update Documentation](#step-6-update-documentation)
    - [Temporary Experiment Workflow](#temporary-experiment-workflow)
    - [Step 7: Commit and Durable Sync](#step-7-commit-and-durable-sync)
      - [Verifying the COMMITTED variant, not your staging](#verifying-the-committed-variant-not-your-staging)
    - [Step 8: Close the Ticket](#step-8-close-the-ticket)
  - [User Confirmation Preferences](#user-confirmation-preferences)

## Two Workflows

1. **Development** (test mode) — Build, modify, and verify components using isolated test data.
2. **Production Support** (prod mode) — Investigate live data, debug issues, trace execution.

Every CLI response includes `dataMode` so you always know which context you're in.

### Test Mode vs Prod Mode

Treat these as two different jobs:

- **Prod mode** is for investigation.
  - Read live Firestore documents.
  - Inspect live object activity, events, alerts, and logs.
  - Confirm what actually happened to a customer.
  - Do not use prod mode as your final verification environment for a code fix.

- **Test mode** is for verification.
  - Stage local component changes.
  - Run Test components.
  - Generate token URLs and verify behavior in isolated browser flows.
  - Confirm the fix without mutating or depending on live customer processing.

The correct support loop is usually:
1. Investigate in **prod mode**
2. Identify the responsible implementation repo and make the code change locally
3. Move back to **test mode** for verification
4. Verify with a Test component, Playwright/browser confirmation, or both

If a production issue needs realistic verification, do **not** copy live customer data from a production account into another account's test collection.

The right model is:
- investigate the source document in **prod mode**
- model the relevant conditions in a **Test** component
- or reproduce the scenario through a controlled **test-mode** embeddable/browser flow
- verify the fix there

Never treat "it looks right in prod data inspection" as sufficient proof that a code change is verified.

## Verification Envelopes

For a concrete user workflow, browser-facing change, branch-variant release, support ticket, or any loop
where earlier manual testing found a gap, start a verification envelope before editing:

```bash
remits-cli verify start --summary "Hosted upload updates an existing profile" --manifest acceptance.json
```

The envelope records the account, host, data lane, branch, workspace, git heads, staging lane, and the
acceptance manifest. It is mirrored locally under
`.remits-cli/actors/<local-agent>/verification/<envelopeId>/` and becomes active for this local actor and
command world. While active, `components stage`, `components status`, `test run`, `token`,
`token inspect`, `tool`, and `components sync` attach evidence packets automatically; pass
`--verify-envelope <id>` to name one explicitly or `--no-verify-envelope` when a command should not be
attached.

When one human task spans several local actors, keep actor isolation but give the task one workstream id:
`--workstream <id>` or `REMITS_WORKSTREAM_ID`. It defaults to the current workspace, and
`remits-cli workstream status` shows the local response files, packet ids, actors and account roles for that
campaign without copying large payloads.

`verify start` proves in the **test** lane unless you pass `--data-mode prod` (your session's lane does not
decide it). One envelope is active per checkout world (host, account, git branch, workspace); the data lane
is not part of that selection.

Before stage, test run, token, tool, sync or commit runs, the CLI asks the platform whether its evidence
would count for the active envelope:

- **attach**: same world, so the evidence attaches.
- **refuse**: different world and a required evidence item could use this packet. Nothing ran, and the
  refusal names the fix (`--data-mode prod`, `--as-account 21`, `--workspace x`, ...). Apply the fix that
  matches your intent. Use `--no-verify-envelope` when the run is not meant as proof, or `verify start` when
  it is different work. `--allow-wrong-world-evidence` only records context; that evidence never satisfies
  anything.
- **detach**: different world and nothing in the manifest could use it. The command runs and one line says
  nothing was attached.

`verify current` / `verify use` print whether a plain `test run` from this checkout would attach, so you can
catch a mismatch before the first run.

Use the wrappers when you want the intent to be unmistakable:

```bash
remits-cli verify stage --workset
remits-cli verify test --test "Suite" --names "case name"
remits-cli verify token --path /page/example
remits-cli verify sync --safe
remits-cli verify attach --artifact sample.zip --label "user supplied ZIP"
remits-cli verify report
```

The report is the final-response source. It separates verified claims, missing evidence, stale packets,
and the source/account/lane tuple, so do not replace it with a generic "verified" sentence.

Use proof-level words precisely in the final response and on tickets. A passing Test is Test proof in
the recorded world, a token is URL/resolution proof, a browser step is user-journey proof, a corpus
comparison is measurement proof, a sync packet is durable source movement, and a ticket `complete` is
lifecycle state. Only `verify report` decides which of those packets satisfy the manifest's `Verified`
section.

For Test requirements, be specific enough for the evaluator to know what a pass means:

```json
{"id":"api_flow", "packetType":"test_run", "suite":"Statement API Flow", "allCases":true}
{"id":"duplicate_fee_case", "packetType":"test_run", "suite":"Statement API Flow", "cases":["rejects duplicated fee evidence"]}
```

A suite-only Test requirement is treated as `allCases:true`. A category-only Test requirement matches only
a run carrying that exact evidence category, or the case whose name the category names. A requirement that
says only `{"packetType":"test_run"}` is intentionally only a warning-worthy sketch: it will not turn a
random Test packet into acceptance. Full-suite runs emit suite and passed-case categories automatically,
so case-level requirements can be satisfied by a real full run. The evaluator excludes packets collected
in the wrong account/data lane/git branch/component branch/workspace before satisfying requirements, and
pending async packets do not count until a result packet arrives.

## Development Workflow

### The Golden Rule: Writing Code Is Not Finishing the Job

**A change is not complete until it is verified.** Writing the component is the first step, not the last.
Two ways to prove it:

1. **A Test component** (preferred) — it exercises the change *and* becomes permanent regression
   protection. Run it with `remits-cli test run`.
2. **Visual verification** — `remits-cli token` for a browser URL, then drive it with `playwright-cli`.
   This is how most users think about verification: "let me see it working."

Use a Test when the behavior can be asserted programmatically, a browser when the change is visual.
Often both.

**Never skip verification.** "Just do it" and "that's fine, commit it" are not evidence. If you genuinely
cannot verify — no Test component, no relevant embeddable — say what you would need and ask how the user
wants to proceed rather than reporting the work as done.

> The *design* rule that pairs with this — never solve interpretive problems with regex cascades, keyword
> lists, or layout-specific branching when the platform's AI surface is the right tool — is in the account
> repo's `CLAUDE.md` and, in depth, in `features/ai-strategy.md`.

### The Development Fast Loop

This is how every development task should flow:

#### Step 1: Understand the Request
Read the user's request. If you may need a repo other than the current one, read `~/.remits-cli/account-repos.json` first. Then review `account-info.json` and `README.md` to understand what components exist and how they relate. Read the source of any component you'll modify before changing it.

**Establish a steady git baseline before the first edit.** The platform syncs from the GitHub remote, not
from your local files, and a worktree can be stale even when its staging lane is isolated. In the checkout
you will edit:

```bash
git fetch origin
git status --porcelain                         # empty, or only generated guide files you will discard
git log origin/<branch>..<branch>              # empty: no local commits invisible to the platform
git log <branch>..origin/<branch>              # empty: not behind the remote the platform syncs
remits-cli components status
```

For a non-trunk variant branch, also run `remits-cli components promotion --branch <branch>`. If it reports
that the branch is behind trunk or that overlays were computed from an old SHA, settle that before changing
code. A workspace prevents staged-cache collisions; it does not make a stale branch current.

**Establish the account's shape too, not just its components.** Read the `resolution` block in
`account-info.json` (or `mcp_account_view`): the account `type` decides whether this repo is even the right
place to change code, `resolution.relationships` shows whether the account has more than one parent (and
which link carries a branch/namespace/host), and `resolvedDatabaseName` tells you where its data actually
lands. See `account-targeting.md` and `features/account-management.md` (`mcp_get_guide`).

**Also establish which world you are working in.** `remits-cli components status` reports whether the
working tree is a **trunk** checkout or a **variant branch** checkout — which decides both what your test
runs resolve and what a sync writes. If `account-info.json` carries a `componentBranches` section, branch
variants of these components exist: editing an origin component will drift them, so check
`remits-cli components branches` before changing shared code. See `branch-variants.md`.

**You do not need to start anything to have a record.** Every stage, test, token, tool and sync appends a
world-stamped line to your actor's evidence trail automatically. Read it with:

```bash
remits-cli evidence
```

That is the right tool for "what have I already run?", "did that test actually execute in the prod lane?",
and "what should I put in my final message?". It is per-actor, so agents sharing a checkout never read each
other's trail.

**Start a verification envelope only when someone else needs a verdict** — a support ticket, a human who
asked you to prove specific things, or a handoff another agent will act on. It is not a routine step before
editing, and an envelope you open for your own benefit is almost always `remits-cli evidence` in disguise.

When you do want a verdict, name what must be true. One command, no manifest file:

```bash
remits-cli verify start --summary "Hosted upload updates an existing profile"
remits-cli verify claim fees-balance --text "statement fee totals reconcile to source within five cents"
remits-cli verify claim pilot-green --text "the pilot suite passes" --test "Acquirer Pilot"
```

Prove a claim by naming it on the command that already proves it — `--claim <id>` works on `verify
test`, `token`, `tool`, `stage`, `sync` and `attach` alike:

```bash
remits-cli verify test --test "Acquirer Pilot" --claim pilot-green
remits-cli verify attach --claim fees-balance --note "34025 reconciles at 0.02 variance"
```

An envelope with no claims reports `evidence_only`. That is a complete, final state — a log, not a
half-finished exam. Nothing about it is outstanding.

#### Step 2: Make the Change
Edit component files under `components/`. This is local file editing — the platform doesn't know about your changes yet.

Before changing a displayed value, helper, calculation, schema field, or prompt contract, read that
component's existing `.meta.yml` sidecar too. Descriptions often carry dated decisions and line-number
references explaining why a value looks odd. If you are reversing one, say so in the new sidecar text;
otherwise you are probably reopening a closed bug.

**Creating a component that does not exist yet.** Files are named `<id>_<Name>.<ext>`, where the numeric
prefix is the platform's component id. A new component has no id, so name its files with the **`new_`
prefix** and let the sync assign one (it then renames the files to that id):

```
components/embeddables/new_MerchantPortal.groovy     # source
components/embeddables/new_MerchantPortal.html       # markup
components/embeddables/new_MerchantPortal.js         # client script
components/embeddables/new_MerchantPortal.meta.yml   # metadata sidecar
components/prompts/new_PricingReviewPrompt.md        # standalone Prompt body
components/prompts/new_PricingReviewPrompt.meta.yml  # standalone Prompt metadata
```

**Do NOT put an `id:` in a new component's sidecar.** `id` is what links a sidecar to an *existing*
component and is parsed as a number, so a placeholder (`id: new`, `id: TBD`) fails the **entire**
stage/sync request with a `NumberFormatException` — not just that one file, and the error does not name
the file. Omit the key; the platform fills it in on sync:

```yaml
# components/embeddables/new_MerchantPortal.meta.yml   — no `id:` yet
name: Merchant Portal
summary: One-line statement of what this component is for. This is the compact text account-info.json uses first.
description: |
  Longer technical description with line-number references to the key logic.
path: /page/merchant-portal        # Readers and Embeddables only
injectionType: DIRECT              # Embeddables only: DIRECT or IFRAME
category: default
auxiliary: false                   # `true` means the sync SKIPS the file entirely — see Auxiliary
mermaid: |
  graph TD
    A[Request] --> B[Load documents]
```

For `components/prompts/new_*.meta.yml`, include `description` and `purpose: CUSTOM`; missing
`description` fails trunk validation, and missing/mismatched `purpose` leaves a post-promotion Prompt
overlay instead of pruning cleanly.

> **Staging creates nothing in the database, so a `new_` component has no id yet — address it BY NAME.**
> `remits-cli test run --test "My Suite"`, not `--test <id>`. Component-to-component resolution and
> request-level addressing are name-based too; the component guides cover those. After a trunk sync the
> component has a real id and either form works.

#### Step 3: Stage to Platform

```bash
remits-cli workspace use --auto          # once per checkout: your own lane
remits-cli components stage --workset    # every edit: stage what you changed
```

This uploads your local component changes to the platform's staging cache (Redis, 240-minute TTL). It does NOT commit anything. The platform cannot see your local edits until you stage them.

For runtime-compiled components (`Reader`, `Action`, `Embeddable`, `HtmlTemplate`, `Rule`, `Test`,
`Agent`/`Utility`, and `Tool`), staging also validates the Groovy source that is part of the submitted
workset. Validation resolves through the same branch/workspace staging lane a later run will use and
compiles candidates in a small bounded pool, so a syntax/compile error is reported by `components stage`
instead of waiting for the next workflow to trip over it. A full repository stage does not compile every
component in the repo; it validates only entries known to be in the current workset, plus explicit partial
source updates.

Two things to read correctly when it refuses:

- **The lane already holds what it rejected — on a 422.** The compile has to resolve through the staged
  overlay to see the source it is judging, so the entries are written first and compiled second. `422`
  means "staged, and rejected", not "nothing happened" — fix the source and re-stage before running
  anything in that lane. A `409` is the other kind of refusal (account policy, or another agent's edit
  lease) and writes nothing at all.
- **Every failure is printed, not just the first.** The one-line error is the first failure; the full list
  (identified by type and id) follows it. Fix them in one pass rather than one round trip each.
- **`N NOT compile-checked` is not a pass.** A `new_` component whose source file is empty or blank cannot
  be judged, so it is listed by name with its reason instead of being counted as compiled. It will not run.
- **Deleting a component's source file while its sidecar stays is `content-file-deleted`.** The lane keeps
  the source it already held, and that is what gets compiled and run. Clear that one component with the
  command the CLI prints (`components clear --component-type <type> --component-id <id>`).

If git cannot identify the changed set — you are not in a working tree, or git failed — there is no
workset to validate, and the CLI prints `Compile validation: NOT RUN — workset-unknown` rather than
silently implying a pass.

##### Stage your workset, not the whole repo

There are three stage modes, and the difference decides what a run in your lane resolves and what a
human watching the console sees:

| Command | What it uploads | What the lane holds afterwards |
|---|---|---|
| `components stage --workset` | only the components git reports changed | **exactly those** — the lane is reconciled to your workset |
| `components stage` | the whole repository manifest | **every component in the repo** (a full snapshot) |
| `components stage --changed-only` | only the changed components | the changed ones **merged into whatever was already there** |

**Use `--workset` for normal iteration.** A full stage is correct and safe, but on a real repository it
puts a hundred-plus components into your lane, and every one of them then shadows committed source for
any run in that lane until it expires. A human looking at `/admin/platforms` sees "115 staged" and cannot
tell whether you edited 115 components or five.

`--changed-only` MERGES. It cannot shrink a lane it inherited from an earlier full stage, so a lane can
sit at 115 overlay entries while you are working on seven. The command warns when that happens; the fix
is `--workset` (or `components clear --all` once, then keep using `--workset`).

**When a full stage is the right answer:**

- you deliberately want a complete snapshot of the repo in the lane;
- you changed a `.meta.yml` sidecar and want removed keys reconciled against the whole repo;
- you cannot tell what is stale in the lane and want a clean, known state (then `components clear --all`
  when you are done).

**An empty workset never clears your lane.** `--workset` on a clean working tree stages nothing and
leaves the lane alone — reconciling to an empty manifest would delete the overlay your next test run
depends on. Clearing stays explicit: `components clear --all`.

**Deleting a component file cannot be verified by staging.** There is no staged "removal": clearing a
staged entry falls back to the committed row, so the component still resolves. `stage --workset` reports
those changes as NOT REPRESENTABLE.

On a non-trunk variant branch, prove the removal through the durable variant plan:
`remits-cli components sync --dry-run --summary --fail-on-errors` and read the removed/tombstone bucket.
On trunk there is no dry-run plan; treat deletion as a high-risk durable reconcile and pass the full
pre-sync safety check before running any mutating sync.

##### Three numbers, three questions

`components stage` and `components status` report all three, and so does the admin console. They are not
interchangeable:

- **workset** — components git reports this working tree changed. The work in flight.
- **submitted** — what this command uploaded.
- **overlay** — every staged entry the lane now holds. **This is what a run resolves.**

A missing answer is printed as `unknown`, never as `0`: "git could not answer" and "git says nothing
changed" are different facts and only one of them is a number.

##### Review lanes without changing them

`components status` is still the normal first read: it tells you which world this checkout resolves,
what this lane holds, and whether other account lanes are live. For review/debugging, use the dedicated
read-only surfaces:

```bash
remits-cli components lanes
remits-cli components entries --lane-id <id>
```

`components lanes` shows every indexed lane on the account across users, branches and workspaces.
`components entries` reads the authoritative staged files for one lane. It does not switch your
workspace, clear entries, or affect what your own runs resolve. Use it before assuming a shared lane is
safe to overwrite, and when reviewing another agent's workstream from the CLI.

**THE STAGE-BEFORE-RUN RULE:** You MUST run `remits-cli components stage` after EVERY file edit and BEFORE any test run or verification. The platform executes whatever version is in the staging cache at the moment the test starts. If you edit a file and run a test without staging first, the test runs the OLD code — not your changes. This is the single most common mistake. Never skip staging. The sequence is always: **edit → stage → run**.

This applies to:
- Creating new components (the platform won't find them until staged)
- Editing existing components (the platform runs the previously staged version until you re-stage)
- Every iteration of the fix loop — every edit requires a fresh stage before the next test run

#### Step 4: Verify the Change

**Option A — Run Tests** (if Test components exist for this area):

```bash
remits-cli test run --test <TEST_ID_OR_NAME>
remits-cli test run --test "Invoice Tests" --names "specific test case"
remits-cli test status --task-id <TASK_ID>
```

Tests run on the platform against your staged snapshot. They stream results in real-time. **Read the
`World:` block printed before the run** — host, execution account, data lane, component world, staging lane
id + content hash, platform sync vs local HEAD. If it is not the world you meant, stop: the result will be
about a different world.

The platform launch is asynchronous. By default `remits-cli test run` waits for its task to finish, and any
cases selected by `--names "a|b"` execute sequentially inside that suite task. If several slow cases are
independent, stage once, then start separate `test run --names "<case>" --wait false` commands from the same
lane and keep the task ids they print. Complete each proof with
`remits-cli test status --task-id <id>`; that terminal status read records and attaches the final `test_run`
evidence. Do not parallelize cases that mutate the same fixture, rely on shared suite setup state, or make
undeclared live AI/provider calls.

If cases fail, read the printed summary and pivots first: each case has an `outcome`
(`failed`, `error`, `budget_exceeded`, `provider_unavailable`, …) with its reason, timing, trace id, AI usage
(live vs mocked, cost), bounded `report(...)` diagnostics, live HTTP signals, and resolved component
provenance. Fix the code, re-stage, and re-run only after those pivots explain the failure.

##### Many failures are usually few causes

When several cases fail, the run leads with **failure roots** — the failures grouped by their assertion with
the particulars (ids, numbers, Groovy's `Expression:`/`Values:` decoration) stripped out, largest group first,
one pivot block per root rather than per case:

```text
Failure roots (33 failed case(s), 21 distinct root(s)):
  10x  assert statement.data.processingStatus == 'Analyzed'
        e.g. bundled UK statements  (+9 more)
Largest root first: remits-cli test run --test "Statement Reader Calculations" --names "bundled UK statements"
```

**Work the largest root against ONE named case, then re-run the suite.** A full re-run after every edit is the
most expensive way to learn nothing: a real account spent two days and twenty-five runs holding a suite at
56–59 of 92 while ten of its thirty-three failures were one cause. The narrow run is seconds, tells you
whether the cause moved, and leaves your context for the actual reasoning.

Two failure roots mean "stop and look elsewhere", not "iterate harder":

- **`aiMock ctx.replay(...) found no usable stored provider response`** — the message says which of three
  things happened. *No rows at all* for that sessionId means the stored session this fixture borrows is not
  on this platform and will not come back; the case cannot pass until the mock builds its own response with
  `ctx.toolCall(...)` / `ctx.content(...)`. Do not re-run it. (Replaying a session that IS there renews it,
  so a suite that runs regularly keeps its fixtures.)
- **`Method too large` / `Class too large` / a synthetic `_closureNN`** — a JVM limit on one method body, not
  a bug in the line it names. Staging now warns *before* the refusal and names the closure's source line span.
  Split that body; see `development-guide.md` → *Keep Component Bodies Split*.

Every finished run is recorded durably: `remits-cli test status --task-id <id>` answers after the live status
expires, `remits-cli test runs --test <name> --compare` compares the latest run with the newest **comparable**
one, and `remits-cli test compare --base <id> --head <id>` compares any two. Comparable means the same data
lane, branch, workspace and case count: a run's `dataMode` is the lane it was written in, so `64/92` in prod
and `59/92` in test are two facts and not a trend. `test runs` lists one lane at a time and names it
(`--all-lanes` to see both). For evaluation suites (a `corpus(name)` of
cases seeded with `remits-cli corpus import`, one case per corpus case, intentional live AI inside
`withAiBudget(...)`, measurements compared with `remits-cli corpus compare` / `corpus consistency`), read
`guides/test-components.md` → *Evaluation Suites And Corpora*.
Use `remits-cli corpus cases --corpus <name> --split dev --tag reviewed --key case-001` to inspect subsets;
`split`, repeated/comma-separated tags, keys and `limit` are applied on the platform before rows are returned.
For per-case AI totals, start and await the workflow inside that `test(...)` case; setup and late async calls are
only visible in the persisted run-level AI history.

Important test-runner constraints:
- `remits-cli test run` now defaults to `test` dataMode unless you explicitly pass `--data-mode prod`.
- **`--names` is delimited by `|`, and may be repeated.** A comma still splits a single `--names` value
  (legacy behaviour), which is why a case name containing a comma used to be cut in half and match
  nothing. Prefer `|` or repetition whenever a name might contain punctuation:
  ```bash
  remits-cli test run --test 13 --names "a case, with a comma|another case"
  remits-cli test run --test 13 --names "a case, with a comma" --names "another case"
  ```
- **A selector that matches no case FAILS the run.** It used to report `0 passed, 0 failed`,
  `completed`, and exit 0 — indistinguishable from a suite where everything passed. The run now names
  the unmatched selectors and lists the cases the suite actually declared, and exits non-zero.

If no relevant Test component exists yet, consider creating one. Test components live in `components/tests/` and follow the same component structure. They provide permanent regression protection — every test you write today saves debugging time tomorrow.

New test files use the `new_` prefix (e.g., `new_MyTest.groovy`) and no `id:` in the sidecar — see "Creating a component that does not exist yet" in Step 2. Run them **by name** (`remits-cli test run --test "My Test"`) until a sync assigns an id and renames the file.

A `remits-cli corpus import` packet is fixture ingestion, not a measurement and not acceptance. Corpus
evidence begins when an evaluation Test actually runs those cases and records measurements; the report
still has to say whether the run used live AI, mocked AI, or a declared `withAiBudget(...)`.

**How to write the Test itself is not a CLI concern** — what a suite can assert, how mocks behave across HTTP/relay boundaries, driving an embeddable in-process, and the front-stage-only rule all live in `guides/test-components.md`. Read that before authoring a suite.

**Option B — Visual verification with Playwright** (for UI changes or when the user wants to "see it"):

```bash
# Generate a browser-accessible URL for an embeddable
remits-cli token --path embeddable/index/<EMBEDDABLE_ID>
```

This returns an `embeddableUrl`. Use Playwright to open and interact with it:

```bash
# Open the embeddable in a headed browser
playwright-cli open --headed "<embeddableUrl>"

# Take a snapshot to see the current state
playwright-cli snapshot

# Interact with elements
playwright-cli click "text=Submit"
playwright-cli fill "#amount" "500.00"

# Verify specific content
playwright-cli eval "() => document.querySelector('.total-amount').textContent"
```

The `testMode` metadata confirms you're testing against staged changes, not production.

**Two different token keys come back, for two different jobs.** When `--path` resolves to an
Embeddable, the response carries an `embedTokenKey` and a paste-ready `embedSnippet` alongside the usual
`tokenKey` / `embeddableUrl`:

| Field | Use it for |
|---|---|
| `tokenKey` / `embeddableUrl` | Opening the page in a browser (Playwright, or clicking the link) |
| `embedTokenKey` / `embedSnippet` | The `<script>` embed loader — verifying the page as a HOST SITE embeds it |

They are not interchangeable. The loader's request carries **no path**, so it resolves the component
purely from the embeddable-scoped token key's persisted context. The browser `tokenKey` names the account
preview URL; `embedTokenKey` names the host-loader credential. The response also echoes `injectionType` /
`renderMode` / `headMode`, which decide what a host actually receives
(`guides/embeddable-components.md`).

**This works for a `new_` component that has never been synced.** The embed token key carries the
component NAME as well as its id, so a staged, id-less Embeddable is loader-addressable — you do not
have to sync it, or borrow another component's id, just to verify a host embed.

**Option C — Use investigation tools** (for backend/data changes):

For changes to Readers, Actions, or Rules that process data rather than display UI, verify by examining the data they produce:

```bash
# After triggering the component (via test or manual action), check the result
remits-cli tool --name "mcp_firestore_search" --input '{"accountId": <ID>, "collection": "<collection>", "limit": 5, "sort": [{"field": "_lastModifiedAt", "direction": "DESC"}]}'
```

For long-running backend verification, use the Action runner's own async mode (`executionMode:"async"`) and
poll by `actionRunId` rather than holding a single request open (see `command-reference.md` → *Tool Execution Lifecycle* for why not
to also stack the CLI `--async` flag):

```bash
remits-cli tool --name "mcp_run_action" --input '{"accountId": <ID>, "actionId": <ACTION_ID>, "executionMode": "async", "actionInput": {...}}' --data-mode test
# then poll:  {"controlAction":"status","accountId": <ID>, "actionRunId":"<actionRunId>"}
```

#### Step 5: Iterate If Needed

If verification reveals issues, repeat the loop: **edit → stage → run**. Every iteration must include a fresh `remits-cli components stage` after your edits and before the next test run. Never run a test immediately after editing without staging first — the platform will execute the previous version, not your latest changes.

Don't ask the user for permission to re-iterate — just do it. Only stop to ask if you're stuck or unsure about the intended behavior.

If the work is tied to a support ticket:
- Use `remits-cli ticket status --status in_progress` once you have started substantive work.
- If a new reply arrives, re-read the ticket and incorporate the reply into your current plan.

#### Step 6: Update Documentation

Before committing, update metadata so the next session understands what changed:

1. **`.meta.yml` sidecars** — Update `summary`, `description`, and `mermaid` for each modified component. `summary` is what drives the compact component description in generated `account-info.json`; `description` is the fallback when no summary is set and is capped in that file. Preserve or explicitly revise dated decision notes; do not delete the evidence the next agent needs.
2. **`README.md`** — If the change affects account-level capabilities or workflows.
3. **New components** — Always fill in `.meta.yml` immediately.

`account-info.json` is read-only — never edit it. It regenerates automatically after sync. Component
entries prefer `summary`, fall back to capped `description`, and cap `mermaid`; relationships remain as
generated. On a trunk sync it describes the owning repo account. On a subscriber-initiated variant sync it
describes the subscribing account reached through the branch edge, even though the component files still
belong to the owner's repo.

#### Temporary Experiment Workflow

Use this when you need to prove a guard or assertion by temporarily making a local component fail. The staged
Redis cache can affect later test/tool runs, so always clear it after restoring the file:

```bash
# make temporary local edit
remits-cli components stage --workset
remits-cli test run --test <id-or-name> --names "<case name>"
git restore <file>
remits-cli components clear --all
remits-cli components status
```

With `--workset` the restore-and-clear is belt and braces rather than the only thing standing between
the experiment and a later run: the lane only ever held the component you were experimenting on.

For narrower cleanup when only one staged component should be cleared:

```bash
remits-cli components clear --component-type Action --component-id 25
```

#### Step 7: Commit and Durable Sync

Once verified and documented, create a normal git commit first:

```bash
git add -A
git commit -m "description of what changed and why"
git push origin <branch>
```

This separates the local failure boundaries cleanly:
1. Local git commit
2. Remote push

After the push, run `git fetch origin` and confirm both local and remote agree on the branch you are about
to sync:

```bash
git log origin/<branch>..<branch>              # empty
git log <branch>..origin/<branch>              # empty
```

Then run the **`component-integrity.md`** safety checks before any durable platform sync. Do not run
`remits-cli components sync` when local files, `account-info.json`, and live inventory disagree about component
IDs or when unexpected deletes/renumbers are present.

Only after those checks pass, and only when the user intends to promote the repo to the platform database:

```bash
# On a VARIANT branch — the recommended path. Dry-runs first and refuses a surprising plan.
remits-cli components sync --safe
git pull --ff-only origin <branch>

# On TRUNK — there is no plan to gate, so --safe explains what a trunk reconcile does and needs --yes.
remits-cli components sync
git pull --ff-only origin <branch>
```

`--safe` expands to `--summary --changed-only --fail-on-errors --fail-on-removed`, resolves the
comparison base from this branch's merge base with trunk when you did not pass `--changed-since`, and
prints the planned writes before mutating unless you pass `--yes`. It refuses when the plan would write
components this checkout did not change — which is exactly what a branch that is BEHIND trunk produces,
because it still physically carries old copies of files nobody on it touched, and a variant sync turns
each of those into an unrelated override. When it refuses that way, merge trunk into your branch, push,
and re-run the same command.

When a removal is intended, name it rather than disabling the gate:

```bash
remits-cli components sync --safe --expected-removed action:50
```

`--force-tombstones` stays explicit and human-owned. Never pass it to get past a refusal.

##### Verifying the COMMITTED variant, not your staging

After a sync, staged entries still win for CLI-scoped runs, so a test that passes may be testing your
staging rather than what you just committed. Clear the lane first:

```bash
remits-cli components clear --all
remits-cli test run --test <id-or-name> --as-account <subscriber-id>
```

`remits-cli components sync` is the authoritative platform-sync step. It does not perform local git operations,
and it is capable of reconciling creates/deletes/renames from the remote repository into the database. Treat it
as a gated promote/reconciliation command, not as an exploratory command or fallback.

The CLI now returns the post-sync branch SHA from the platform and verifies that your `git fetch` and final `git pull --ff-only` land on that exact commit. If that SHA does not match `origin/<branch>` or local `HEAD`, stop immediately and investigate the race or branch drift instead of guessing.

`remits-cli components commit` still exists as a convenience wrapper, but agents should not call unsupported
subcommand help variants such as `remits-cli components commit --help` to discover behavior. Consult the `remits-cli` skill and its references,
the CLI source, or `remits-cli components` documentation instead. If an exploratory or commit command behaves
unexpectedly, stop and inspect the repo-local session log before running any mutating follow-up command.
Use `remits-cli doctor local-state` first if you did not create the checkout; it names the active local
actor and points at the actor-scoped session log/tool-response directories.

Its first phase internally stages changed runtime source with merge semantics for compile validation. That
is useful validation evidence, but it deliberately does not reconcile the lane or prove the overlay equals
your workset. Keep behavioral verification on the explicit loop: `components stage --workset`, then
`test run` or browser proof, then land.

**Git is required for durable sync.** The platform syncs by pulling from the git remote (`GitHubClient.syncFromRepository`). If `git push` fails, the server has nothing new to sync. You can still **stage** and **test** without git — only durable sync requires it.

**Landing is serial, and the platform now enforces it.** `components commit` takes a short exclusive
landing lease on `(account, branch)` before it pushes, and `components sync` takes it too. If another agent
is landing that branch you are refused with a sentence naming who holds it and how long is left:

```
Refused: Branch 'forked' on account 33 is being landed by dev@acme.test in /Users/dev/wt/acme-a ...
Keep staging and iterating — staging is lane-isolated — and land when this clears.
```

Do exactly that. **Do not loop on the refusal**: the lease is minutes at most, staging and testing are
unaffected because they are lane-isolated, and retrying in a tight loop just burns the run. The reason it
is serial at all is that `git add -A` sweeps a shared checkout and the platform pushes a regenerated
`account-info.json` back to the branch during sync, so two commits racing one branch collide on the remote.

**Staging also refreshes your presence.** You do not need `remits-cli agent register` for other agents to
see you: `components stage` records `(user, checkout, branch, workspace)` so headless workers on other
machines find you in their "current repository activity" block instead of assuming the repository is
theirs. A derived record is never routed work.

**An account may make `commit` a refusal outright.** A `no-commit` rule in its `OPERATIONS` process means
the platform refuses the landing lease and the sync — leave your changes in the working tree and describe
them on the ticket. Read `support-tickets.md` §"Some of that process is enforced, not requested".

#### Step 8: Close the Ticket

If the request came from a support ticket, the task is not complete until you update the ticket lifecycle yourself:

1. Re-read the ticket if needed to confirm the latest state and replies.
2. If the work is done and verified, call `remits-cli ticket complete` and include a concise resolution summary.
3. If you cannot finish, use `remits-cli ticket status` or `remits-cli ticket release` with clear notes so the next agent can continue.
4. Do this automatically. The human user should not need to instruct you to update the ticket.

Options:
```bash
--message "commit msg"         # Commit message (default: auto-generated timestamp)
--allow-empty true             # Allow empty git commits
```

### User Confirmation Preferences

Some users want to review every change before staging. Others want you to move fast and only stop if something breaks. **Pay attention to how the user communicates:**

- If they say "just fix it" or "go ahead" — move through the loop without asking for confirmation at each step. Stage, verify, commit.
- If they say "show me first" or "wait before committing" — pause at the appropriate step.
- If they say "you don't need to ask me" or "stop asking" — remember this preference and work autonomously through the full loop.

The default should be: make the change, stage it, verify it, and present the results. Only block on the user when you're genuinely unsure about intent.
