# Account Targeting and Resolution

> A `remits-cli` skill reference. **Load this when** you need to know which account and which repo a piece of work belongs to, why a request resolved the way it did, or whether a record is test or production data.
>
> 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

- [Account Targeting Model](#account-targeting-model)
  - [Read the shape first](#read-the-shape-first)
  - [Which repo does the work belong in](#which-repo-does-the-work-belong-in)
  - [Repo selection rules](#repo-selection-rules)
- [Account Resolution: how a request travels the account graph](#account-resolution-how-a-request-travels-the-account-graph)
  - [Seeing an account's edges](#seeing-an-accounts-edges)
  - [When a subscription "doesn't work"](#when-a-subscription-doesnt-work)
  - [The same block answers the non-branch questions](#the-same-block-answers-the-non-branch-questions)

## Account Targeting Model

**Establish the account's shape before you touch anything.** Which repo you work in, which account you run
against, and where a fix belongs are answered by the account's structure — never by its name.

The account model itself — types, component inheritance, primary vs membership edges, the three independent
edge properties, the user model — is in the always-loaded `platform-overview.md` and in depth in
`features/account-management.md` (`mcp_get_guide`). What follows is only what changes **what you type**.

### Read the shape first

`account-info.json` (in a repo) and `mcp_account_view` (remotely) both carry a `resolution` block — the one
place these facts appear. Field-by-field detail is under **`mcp_account_view`** in `tool-reference.md`. The
four that decide a CLI action:

| Read | To decide |
|---|---|
| `role` + `summary` | `OWNER` (the files here **are** its components) vs `SUBSCRIBER` (it resolves another account's components under a variant branch). Read this before you touch anything. |
| `type` | whether this repo is where the change belongs — see below |
| `resolvedDatabaseName` | where its data actually lands. **Check this first when documents are "missing".** |
| `relationships` | every link upward, primary first, each with its own `branchName` / `databaseName` / `domainName`. **More than one entry means the account can legitimately resolve differently depending on the path a request travelled** — establish which one a failing request used before comparing behavior. |

Two more, easily confused: top-level **`componentBranches`** lists the variant branches this account
**owns**, with drift and subscriber counts — check it before editing a shared component. And
`resolution.branchName` is the **repo's trunk sync branch**, *not* a component-variant branch.

### Which repo does the work belong in

| Situation | Target |
|---|---|
| Feature, enhancement, shared behavior fix | usually the owning `PLATFORM` / `PRODUCT` account — **not** the `CLIENT` that reported it |
| Production investigation, client-specific data issue | the affected `CLIENT` account's data and runtime history |
| Both | confirm the symptom on the `CLIENT`, then move to the owning repo to change code |

### Repo selection rules

- **Inside the target implementation repo**: make sure you are on the relevant branch and the checkout is
  current before reading source or editing. Run `git fetch origin`; when the tree is clean,
  `git pull --ff-only origin <branch>`; then confirm `git log origin/<branch>..<branch>` and
  `git log <branch>..origin/<branch>` are both empty. A worktree can have an isolated staging workspace
  and still be based on a stale commit. Then read `account-info.json` and inspect `components/` directly.
- **Inside one repo but supporting a different account**: switch to that account's indexed repo if it
  exists. For tickets, prefer `implementationAccountId` / `implementationAccountName` over the reporting
  `accountId` when choosing that repo; a subscriber or client often reports the symptom while the
  implementation lives in an upstream `PLATFORM` / `PRODUCT` repo.
- **Only use `mcp_component_view` / `mcp_component_grep` when the correct repo is not available locally,**
  or when you are deliberately comparing local source to the live DB after reading the files. They are not
  the normal way to learn source on a machine with the account repo.
- **Outside any repo**: rely on the tools, and `mcp_get_guide` for the front-stage guides.
- **Never create a new local repo/directory just because a ticket references an account name.** Resolve the
  account type and parent hierarchy first, and work only from an existing indexed repo unless the user
  explicitly asks you to clone one.

For access questions — "who can see this client account?", "why does this user see the wrong data?" — use
`mcp_account_user_admin` first (`action:'users'`, `action:'user'`, or `action:'user_update'`). Use
`mcp_sql_query` only when you need raw join-table investigation. Remember a user's custom fields are stored
**per bound account**, so the same person can differ per account.

**Test-data flags are first-class safety evidence.** `Account.testAccount` and `User.testUser` are set by
the `account(...)` / `user(...)` factories when the execution data lane is `test` (which is how
`mcp_account_user_admin` creates); direct inserts such as `new Account(...).save()` are also flagged by the
domain `beforeInsert` hook in the test lane. Prod-data creates leave them false. Updating an existing real
account/user in test mode does not convert it into test data, though its Firestore extension-field writes
still go to the test lane. `Object.testMode` /
`Event.testMode` / `Alert.testMode` (and `testMode` inside test-lane Audit documents) identify lifecycle
rows in the test data lane. Agent-facing surfaces expose these fields:

- `account-info.json`, `account-hierarchy.json`, and `mcp_account_view`: `resolution.testAccount` for the
  described account, and `testAccount` on returned hierarchy nodes.
- `mcp_account_user_admin`: `testAccount` on `hierarchy`, `account`, and `account_create` results;
  `testUser` on `users` / `user` results.
- `mcp_record_listing`: top-level `dataMode` (the lane it searched — listings are **filtered** by lane,
  so `totalItems:0` in the wrong lane reads exactly like "no such record"), plus `testMode` per record.
- `mcp_record_view`: `record.dataMode` (the lane read in) next to `record.testMode` (the lane the row
  belongs to). This one loads **by id and does not filter**, so those two can legitimately disagree —
  and when they do, that is the finding.
- `mcp_object_activity`: top-level `dataMode`, `object.testMode`, and `testMode` on Event/Alert
  timeline entries.
- `mcp_event_diagnostics`: top-level `dataMode` and `result.event.testMode`.
- `remits-cli token inspect`: owner `account.testAccount` and `user.testUser`, plus a `safety` block
  carrying `dataMode`, `dataModeDeclared`, and an explicit warning when the token does not declare one.
- `remits-cli whoami`: the resolved account / user / branch / lane / host tuple for the **next tool
  call**. It does not describe `remits-cli test run`, which ignores the stored session lane — see below.

Two surfaces deliberately have **no** lane flags, and both mislead if you forget it:

- **`mcp_sql_query` reads MySQL directly and is lane-blind.** `object` / `event` / `alert` come back with
  **both lanes mixed**, and `user` / `account` with test fixtures mixed into real records. Filter
  explicitly — `test_mode = 0`, `test_user = 0`, `test_account = 0` — or use the purpose-built tool.
- **Persisted AI session rows** store no durable per-grouping `testMode` / `dataMode`. Use
  `mcp_ai_session_search.dataMode` as the current execution lane only, never as proof of the historical
  grouping's lane.

If any of those fields contradict the lane you intended, stop and rerun the command with an explicit
`--data-mode test` or `--data-mode prod`. Never infer prod/test from an account name, URL, branch name, or
the mere existence of a created account.

**A tool's `dataMode` input never widens the lane.** Tools that accept a `dataMode` argument clamp it
against the lane the *command* was launched with: it may narrow `prod` -> `test`, never escalate
`test` -> `prod`. So `--data-mode test --input '{"dataMode":"prod"}'` stays in **test**, and the response's
`dataMode` — not your input — is the truth. To reach prod data, pass `--data-mode prod` on the command
line. (Under MCP, the launch lane is the caller's own `dataMode` argument, which defaults to `prod`.)

## Account Resolution: how a request travels the account graph

Component inheritance, branch variants, and where data physically lives are all decided by **how the
current request reached the executing account**. Read this before debugging *"my subscriber isn't picking
up the branch"* or *"why is this account reading the wrong collection"* — those are almost always
resolution questions, not component bugs.

> The model behind it — the linear inheritance walk, the ambiguity rule, the three orthogonal edge
> properties, and path-scoped storage namespaces — is in **`platform-overview.md` → *Account Structure***,
> which is already loaded. What follows is how to *observe* it from the CLI.

**The anchor is the branch of the graph you travelled.** At runtime it is `scope_account_id`, stamped onto
the `Object` / `Event` / `Alert` / `ObjectLog` records a run creates, so async workers re-resolve on the
same branch. A **null** anchor means "walk the structural chain". Entry points that already know the
branch supply an anchor; from the CLI you supply it explicitly with `--as-account`, `--variant-branch`, or
by using the edge's own host.

### Seeing an account's edges

```bash
remits-cli tool --name mcp_account_view --input '{"accountId": 101}' --data-mode prod
# then read: resolution.relationships, resolution.resolvedDatabaseName, resolution.componentBranch
```

`resolution.relationships` returns one entry per structural link, primary first, each carrying its own
`branchName` / `databaseName` / `domainName`. The hierarchy tree flattens every link into one shape, so
this block is the **only** place that answers "how many parents does this account really have, and which
link carries what?"

### When a subscription "doesn't work"

1. Does the account have a primary parent, or is it membership-only? Count `resolution.relationships` and
   see which is `primary: true`. (Membership-only **and** several edges ⇒ ambiguous ⇒ trunk, by design.)
2. Which **edge** carries the `branchName`? `remits-cli components branch <name> --subscribers` prints
   `via primary|membership edge -> parent N`.
3. Was the run anchored through *that* edge's parent? Re-run with `--as-account <subscriberId>` so the
   account's own edge selects the branch, exactly as production would.
4. Check the resolved layer, not the source text: `testComponentSource`, or the `variant:<id>:<hash>`
   compile signature (`branch-variants.md` → *Diagnosing a variant*).

### CLI tool account roles

Generic `remits-cli tools` / `remits-cli tool` calls have three account roles. Keep them separate in
commands and reports:

- `--account-id` is the **repo/scope account**. It is the account whose checkout/context you are working
  from and the ceiling for scoped discovery.
- `--as-account` is the **execution account**. Use it when a tool should resolve components, branch
  subscriptions and staging exactly as a reachable subscriber/client account.
- `--target-account` / `--target-account-id` is the **data target**. Use it when a tool operates on records
  in another account but should not change component resolution. It is delivered to the tool as
  `input.accountId` and `input.targetAccountId` (an explicit `--input` value wins), because `input.accountId`
  is the key the platform's account-targeting tools actually read.

A target alone does not move the run. `--target-account 36` changes which records the tool touches;
component resolution, the branch subscription and the staging lane still belong to `--account-id`. When the
work should resolve as the other account — any subscriber or client account — pass `--as-account 36` as
well. If that is refused, the account is not reachable downward from anything you hold; ask for access
rather than reaching it through `input.accountId`, which no longer changes execution.

Legacy `input.accountId` still reaches tools as their target input, but it no longer silently turns the
whole CLI command into that account when a repo/scope account is present. The CLI says so on stderr and in
`warnings[]` in the response, so `--json` callers see it too.

The response `world` block is the truth: read `repoAccountId`, `executionAccountId`, `targetAccountId`,
`componentOwnerAccountId`, `componentBranch`, `workspace`, and `dataMode` before attaching the result to a
verification claim. `world.accountId` is the **execution** account — the same thing a verification manifest's
`accountId` declares — while the account a command was addressed to is `repoAccountId`/`checkoutAccountId`.

The `component world:` line names the branch AND why it applies (`via subscription`, `via
branch-has-variants`, `via explicit`, `via subscription-fallback`). That reason comes from the platform, not
from the CLI comparing two fields: an explicit `--variant-branch` probe REPLACES the subscription and a
variant branch's working tree decides before any edge is read, even when all three name the same branch.

### The same block answers the non-branch questions

- *"Why are this account's documents not where I expect?"* → compare `databaseName` vs
  `resolvedDatabaseName`, and check for a `databaseName` on one of the `relationships` edges — it applies
  to everything below that edge.
- *"Why does this hostname land on the wrong account?"* → compare `domainName` vs `resolvedDomainName` and
  the edge `domainName`. An **edge** host wins over the account's own, and additionally supplies the path
  travelled — which is what makes that edge's branch variants apply.

**Users are not part of this graph** (see `platform-overview.md`). Use `mcp_account_user_admin`
(`action:'users'`, `action:'user'`, or `action:'user_update'`) for user membership and account-scoped user
fields. Drop to `mcp_sql_query` against `user` / `user_account` only for raw join-table evidence.
