# resolutions.md — the first move for a known symptom

> **Answers:** a lookup table from a symptom you can see — a doctor line, a
> refusal code, a write that did not land — to the first thing to do about it.
>
> **Read it when:** a command stopped with a message you can quote, or
> `a2a doctor` printed a FAIL and you need the fix rather than the meaning.
>
> **Not here:** what each `a2a doctor` check inspects and what its PASS does
> not prove ([troubleshooting.md](/docs/troubleshooting.md)); what to do when the
> tool worked and still got in your way ([loops/feedback.md](/docs/loop-feedback.md)).

## Common resolutions

| Symptom | First move |
|---------|-----------|
| `cannot load project/machine config` (exit 1, stderr) | Run `a2a init` / `a2a connect` first — the config does not exist or is malformed. See [onboarding.md](/docs/getting-started.md). |
| `credentials: FAIL` | Add or fix the space's credential reference in the machine config; confirm the token is valid. |
| `space access: FAIL` | Read the whole line first — on a `Repository not found` it already names the cause. "the repository EXISTS and is private/internal" means set the credential, not fix the URL; "ran unauthenticated" names the variable to set. Absent such a sentence, check the repo URL and your network/auth; a first `a2a sync` clones the mirror. |
| `participant avatars: PASS · local participant avatars are missing …` | No human action is required. The agent runs `a2a sync`; the next dashboard build uses the local images and initials remain available until then. |
| `participant avatars: PASS · owner identifiers cannot be fetched …` | Correct the named owner in the shared `space.yaml` to its GitHub login. Sync is deliberately not prescribed because it cannot repair an invalid identifier. In a local space the owner is `local:<system>`, which has no avatar to fetch; the GitHub-login fix does not apply there. |
| `versions: FAIL: … older than min_binary_version` | Upgrade the `a2a` binary to at least the pinned version. |
| `CI presence: FAIL` | The space repo is missing the validate workflow — a space-admin fix (see [onboarding.md](/docs/getting-started.md), space-admin profile). |
| `notification components: FAIL` | Inspect `a2a notifications status --probe --json`; repair the named channel with `a2a notifications install --channel <channel>`. |
| `statusline wiring: FAIL` | Install `git` / put it on `PATH`. |
| `doctor --space: v1-min: not available` (exit 2) | The host-drift diff is v2; drop the flag. |
| `auto-merge enabled: FAIL` | The space repo has GitHub's `allow_auto_merge` setting off — every write stalls behind a pull request nothing will merge. A space admin enables Settings → General → "Allow auto-merge". This is not your local setup. |
| `auto-merge enabled: PASS · auto-merge unverified …` | **Not a failure.** Your credential cannot read repository settings, so the answer is unknown rather than bad. A fine-grained token needs `Repository metadata: read`. Ignore it unless writes are actually stalling. |
| **My inbox is empty but I was told something was sent** | CLI reads and MCP tool calls both fetch before reading. Check stderr/server logs for `pre-call mirror refresh failed; serving from the last good view`: that warning means MCP deliberately served the last good mirror after a network/auth failure. Restore access and retry (or run `a2a sync` to diagnose it). Without a refresh warning or skipped-file advisory, the current mirror has no matching actionable item. See [SKILL.md](/docs/overview.md) § "Which surface to work through". |
| **`a2a submit` said it succeeded, but the artifact is not on `main`** | Expected before v0.8.0 and fixed in it: GitHub declines to arm auto-merge on a pull request that is already mergeable, and the write used to report success over it. Update the binary (`a2a update`); a2a now lands such a request itself once its required check is green. If it persists, the required check is not green — look at the PR. |
| **`a2a submit` refused with REF-027 "a document naming a parent is submitted with no parent transition"** | You are submitting a document whose own envelope schema REQUIRES `parent` — today that is `response` alone — in a write batch that moves nothing on that parent. The refusal is about the BATCH, not the file: the document is well-formed, and landing it alone is the failure. It would appear, the parent would stay where it was, and the agent waiting on it would be left in a state with no legal move — having done nothing wrong. **The fix is `a2a respond`**, which authors the parent's own `respond` event in the same write and never trips this. If the response is already filed and stranded, `a2a respond --response <RS-id>` adopts it: the parent moves and your existing body is kept. The rule reads each type's own schema `required` array rather than a hardcoded list, so a kind that gains `parent` is covered the moment its schema ships. Scope: this refuses at the write funnel below BOTH surfaces (CLI and MCP inherit it through `funnel.Submit`) and returns no verdict in the post-merge audit — a merged write is itself proof a parent transition accompanied it. |
| **`a2a submit` refused with POL-010 "still the template's own placeholder value"** | Exactly what it says: a frontmatter field still carries the literal `<…>` the template shipped, and the refusal names the field by its full path. `a2a validate` passes on the same file ON PURPOSE — a draft is allowed to hold its placeholders so you can inspect it; the refusal lands where the draft becomes a shared record. Two ways to clear it, and the message names the one that applies: **fill it** (`a2a new … --field <path>=<value>` — the path may be dotted, e.g. `--field expected_response.shape="yes/no plus the flag that controls it"`), or **delete the line** when the field is genuinely unused (`needed_by` only when no response is expected; `valid_until` / `env_requirements` when inapplicable). A response-bearing cross-system ask gets its autonomous +1/+2-day `needed_by` per [the send loop](/docs/work-loops.md). For a value inside a LIST (`refs`, `deliverables`, `acceptance_criteria`) `--field` cannot reach it — edit the drafted file. |
| **A write refused with POL-029 "G1 requires --approve in a confirm space"** (G2, G3 or G4 in place of G1) | You wrote into a space whose `gates` mode is `confirm` and the write sits behind a human gate you did not confirm. **Why it is refused:** in a `confirm` space a gated write needs your explicit say-so, recorded with the write; the refusal names the gate (G1 a first contract publish, G2 a breaking or major contract version, G3 `a2a approve` or `a2a reject` on a decision, G4 any change to `space.yaml`, which includes `a2a space join` and `a2a space update`). It is refused before anything is written, so nothing is half-done. **Fix:** re-run the same command with **`--approve`**; the approval is recorded on the commit (`A2A-Approved-Gates`), and you are confirming a human act, so do it only when you mean it. The land check judges the same thing at the end, so a write that bypassed the command and carries no approval is refused there too, and an approval for G1 does not cover a G2 that appeared because a rival published first: re-run, and approve what the new refusal names. **Not this error:** `--approve` has no effect on a space whose host reviews writes (GitHub: the pull-request review is the gate) and the command says so; a space set to `autonomous` asks for no approval; and classification and secret refusals (POL-001 and its peers) are never approvable. The current mode is the `space policy` row of `a2a doctor`. |
| **A manifest write refused with POL-028 "gates: autonomous is refused on a host that reviews writes"** | You set `gates: autonomous` in a space whose host reviews writes (GitHub). **Why:** on such a host the pull-request review and CODEOWNERS are the gate, so a manifest that switches the gates off is refused at the write, and a space promoted from a local one cannot carry its relaxation over silently. **Fix:** use `default` or `confirm` there; `autonomous` is accepted only in a local space (one on this machine, where there is no reviewer). A manifest that already says `autonomous` on a reviewing host is not refused retroactively: the host ignores the word (the effective mode stays `review`) and `a2a doctor` shows a FAIL on the `space policy` row until you change it. |
| **A manifest write refused with POL-027 "gates is … ; the legal values are default, confirm or autonomous" (or "flow is … ; the legal values are full or express")** | `gates` or `flow` in `space.yaml` carries a word outside its vocabulary. **The two vocabularies:** `gates` is one of `default`, `confirm`, `autonomous`; `flow` is one of `full`, `express`; an absent key (or an explicit `null`) is the key's default (`default`, `full`). Case matters (`Confirm` is refused), and a number, a list or a mapping is refused too: the message says "a value that is not a string". **Fix:** write one of the legal words, by hand in `space.yaml` or with the command that sets it. This is judged only at the write that introduces the value, never on an unrelated pull request, and on read an unknown `gates` is treated as `confirm` and an unknown `flow` as `full`, so an old odd value is never silently permissive. |
| **`git push` to a local space refused with ``a2a: refusing refs/heads/main: this local space lands writes only through a2a (run `a2a submit`), never through git push``** | Git prints it with a `remote: ` prefix, and the ref may be `refs/a2a/...` or another base branch in place of `refs/heads/main`. **Why:** a local space (one bare repository on this machine) has no reviewer, so its own hook refuses every push that bypasses the land check. **Fix:** write through `a2a submit` or the verb that produced the change; nothing was written. |
| **A write to a local space refused with `host: OpenPR: <head>: land contended:` and the refs kept changing** | The space's base branch (`main`) kept moving, or a ref lock was held, while this write tried to land, and its retry budget (8 attempts) ran out. The head branch is kept, so nothing is lost and a retry resumes it. **Fix:** `a2a sync`, then re-run the same command; it resumes. If it repeats, look for another a2a process looping on the same space, a stale `.lock` file under the space repository's `refs/` left by a killed process, or a `main` rewound outside a2a. |
| **A resumed write refused with `space: prepared-target-stale`** | The write was prepared against a space that has since moved: the local space's directory was moved or renamed after the write was prepared. **Fix:** `a2a connect <new path>` and re-run the command from the start; the old prepared write cannot resume. |
| **`a2a await` (or a verb waiting with `--wait`) fails with `space: required check failed: "<check>" completed with <conclusion>` followed by ` — this write was made by a local build (<stamp>); the space's CI judges it with its pinned released validator, so a data shape newer than v<version> is refused there until a public release ships it. Read the check's log: if it names a shape v<version> does not know, cut a release; otherwise fix the write.`** | The suffix appears only when the binary is a local build (`a2a version` says `local build of …`). The space's CI validates with its pinned RELEASED validator, which does not know a field or shape that exists only in unpublished code, so it refuses this write while your local `a2a validate` passes. Open the failed check's log. If it names a shape the stamped release does not know, the write cannot merge until a public release ships that shape: say so to your human (releasing is theirs) rather than rewriting the write. If it names anything else, the write is wrong; fix it as for a release build. A release build never gets the suffix. |
| **`a2a space delete --local <id>` printed `<id>: remove the local space (nothing was changed): the bare repository <path> holds N commits on main … — to delete it run … --approve`** (exit 1) | Not a failure: without `--approve` the verb only reports what it found (the repository path, its commit count on `main`, and the other participants not marked as left) and changes nothing. The path defaults to this project's connection to `<id>` when that is a path on this machine, else `<data-root>/spaces/<id>.git`; `--dir <path>` overrides it. Re-run with `--approve` on the operator's word: it removes the repository and, when this project is connected to that repository, disconnects it (`space delete: removed the local space …`, `space delete: disconnected "<id>" from this project (config entry + mirror)`). Each other participant then gets a line `space delete: next: in <system>'s repository run \`a2a disconnect <id>\``; run it in their repository. Re-running after the repository is gone prints `space delete: space "<id>" already deleted (no repository at <path>)`, exits `0`, and still disconnects this project if it was connected to that path. |
| **`space delete: only a local space can be deleted — a GitHub space is deleted on GitHub; pass --local`** (exit 2; also exit 2 with the `usage:` line when the id is missing or given twice) | The verb deletes only a local space and refuses to guess: add `--local`. A GitHub space is deleted on GitHub; here, `a2a disconnect <id>` is all that stops using it. |
| **`a2a space delete --local <id>` refused with `find the local space: this project's "<id>" is connected to <url>, which is not a space on this machine`** | This project's `<id>` connection is a URL, not a path. Nothing was touched. Run `a2a disconnect <id>` to stop using it here, or pass `--dir <path>` for a local space that has the same id. |
| **`a2a space delete --local <id>` refused with `… is not a bare git repository`, `… is a bare repository that a2a did not create as a local space (it has no a2a.host=local marker)`, `… has no main branch with a readable space.yaml`, or `the space.yaml on main of <path> names the space "<other>", not "<id>"`** (exit 1; each says `nothing was touched`) | The path is not provably a local space this id names, so the verb will not remove it. Pass the right directory with `--dir` (the path `a2a space init --local` printed). For the last form, run `a2a space delete --local <other>` for that space. For a repository without the marker, a2a deletes only a space `a2a space init --local` made: remove it yourself only if you are sure it is unwanted. |
| **`connect`/`init --space` refused with `… must be one path segment (no separators, not . or ..)`** (`init --space <s>: name the space`, `connect <url>: name its mirror`, or `connect <url>: register the id its space.yaml declares (nothing was registered)`) | A space id or `mirror_location` has to be exactly one directory name, because `connect` and `disconnect` remove the directory it names. For `init`/`connect` the id comes from the last element of the URL or path: pass the space repository's own URL or path. For the `register the id` form the id is the `space:` field of the space's own `space.yaml`: its owner corrects it, then re-run `a2a connect`. A project config that already holds such an `id` or `mirror_location` is refused at load with the same sentence: edit the value in `.a2a/config.yaml`. |
| **`connect <url>: read .a2a/config.yaml (it was not rewritten)`, or `disconnect <id>: read .a2a/config.yaml (nothing was removed)`** | The project config exists but could not be read or parsed, so the verb stopped before changing anything. Fix the entry the error names in `.a2a/config.yaml`, then re-run the same command. |
| **A document I know exists does not appear in `search`/`inbox`/`thread`** | Run `a2a doctor` and read the `skipped mirror files` row. If the file could not be decoded it is missing from every read verb and the verbs now say so on **stderr** (`note: … could not be decoded …`) — check whether you were discarding stderr. `a2a show <id>` still prints such a file, which is why the two can disagree. A skipped file is fixable only by whoever owns its section. |
| **`a2a validate` / `a2a submit` refused with REF-017 "body names a digest no declared attachment carries"** | Three things are true at once and all three must be, so read which one you can change: the body names a `sha256:` no `attachments[].digest` carries, **and** the body enumerates what reads as a file tree, **and** the envelope declares no attachment at all. This is the "referenced but not received" shape — the artifact promises bytes and carries none. **The fix is `a2a attach`**, not a reworded sentence. A digest named in ordinary prose, with no file list, does **not** refuse: it warns (POL-017), because a pin is not a possession claim. Note the scope: this refuses at the write gate (`a2a validate`, `a2a submit`, `a2a validate --ci` on a pull request) and returns no verdict in the post-merge audit — a rule that could only be satisfied by editing an immutable artifact must not judge history that cannot be repaired. |
| **`a2a validate` / `a2a submit` refused with POL-022 "handoff body carries N of 5 §16.2 required section(s) with no content"** | A `handoff`'s five body sections (`## Context`, `## What was built`, `## How to verify`, `## How to operate`, `## Limitations & next steps` — the exact headings [`schemas/templates/v1/handoff.md`](../../schemas/templates/v1/handoff.md) ships) are REQUIRED CONTENT per [the-plan §16.2](../../docs/the-plan/plan/16-handoff-directive.md#162-required-content-envelope-extension--body-sections), written for a reader with **no access to your private repo, chats, or history** — not a template's suggestion. The message names every section that is either missing its heading or carries zero non-whitespace text beneath it; the fix is to write that section, not to pad it. There is no length floor: one real sentence, a fenced command block, a bullet list, or a single link all count as content — the rule measures **emptiness**, never quality, so a judgement call ("is this section good enough") is never what refuses. Scope, corrected 2026-08-20 after it was measured rather than read: this refuses at `a2a validate` and at `a2a validate --ci` on a pull request. It does **NOT** refuse at `a2a submit` — on either surface. `SubmitValidatorAdapter.ValidateSubmit` (`internal/cli/adapters.go`, and its `internal/mcp` twin) partitions the outgoing `FileWrite` set into events and drafts and runs the referential checks over DRAFTS only; a `verify`/`close` submission carries event files exclusively, so that loop never executes. `a2a verify` with an incomplete `verdicts[]` therefore exits 0 locally and is refused later, at the merge gate. Filed in `docs/backlog.md` and returns no verdict in the post-merge audit, mirroring REF-017's own reasoning — a merged handoff's body is immutable, and no verb rewrites it. |
| **`a2a verify` / `a2a close` refused with REF-019 "verdicts\[\] names criterion ... which does not resolve to an entry in ...'s acceptance_criteria\[\]"** | An entry in your event's `verdicts[]` does not resolve into the parent's own declared `acceptance_criteria[]` — the message names the specific 0-based `index` or `criterion` id (P3) that does not resolve. Usually a stale `--verdict` composed against an earlier draft of the parent, or a parent that never declared the criterion you pointed at. Re-read the parent's own `acceptance_criteria[]` (`a2a show <parent-id>`) and re-compose `--verdict <index-or-criterion-id>:<verdict>:<cause_owner>` against what it actually declares. Scope, rules-that-reach-2026-08 P1: this refuses at the write gate (`a2a validate`, `a2a submit`/`a2a verify`/`a2a close` on the **CLI surface**, `a2a validate --ci` on a pull request) and returns no verdict in the post-merge audit, the same ADR-011 D3 seat REF-023 below shares — a merged verify/close event is immutable and no verb rewrites its `verdicts[]`. **The MCP surface checks this at submit too** — `a2a mcp`'s `a2a_exchange` verify/close actions resolve the parent through the same path the CLI uses and refuse with the same code, rather than degrading to "cannot check" and leaving it to the merge gate. |
| **`a2a verify` / `a2a close` refused with REF-023 "verdicts\[\] names N of M declared acceptance criteria... missing: ..."** | The parent you are verifying or closing declares `M` `acceptance_criteria[]`, and your event's `verdicts[]` names fewer than `M` of them (measured by DISTINCT criteria, not by entry count — naming the same one twice still leaves the others un-judged). The message lists every criterion left out, by its `id` when the parent declares ids (P3) or by its 0-based ordinal position otherwise. This is deliberately strict, and it is affordable because it is: the enum carries `not_warranted` and `not_exercised` alongside `met`/`unmet`, so a criterion that genuinely does not apply still gets a real entry — **"every criterion needs a verdict" never forces you to assert something false.** `--verdict <index-or-criterion-id>:<verdict>:<cause_owner>` (repeatable) is how `a2a verify`/`a2a close` compose one; run without `--verdict` and the tool writes `verdicts: []`, which this rule now refuses the moment the parent declares even one criterion. Scope, corrected 2026-08-20 (rules-that-reach-2026-08 P1): this refuses at the write gate (`a2a validate`, `a2a submit`/`a2a verify`/`a2a close` on the **CLI surface**, `a2a validate --ci` on a pull request) and returns no verdict in the post-merge audit, the same ADR-011 D3 seat as REF-017/POL-022 above — a merged verify/close event is immutable and no verb rewrites its `verdicts[]`. **The MCP surface checks this at submit too** — see REF-019's own row just above. |
| **`a2a submit` refused with `space: attachment does not resolve through the space's own resolution path`** | `a2a attach` writes its blob into the space as its own commit before it ever writes the local `attachments:` entry, so this should not fire for anything `a2a attach` itself minted. It still fires on a hand-written `ref` (never run through `a2a attach`), or a blob whose attach commit did not actually land. Run `a2a sync` to refresh the mirror, confirm the blob resolves with `a2a fetch <BL-id> --to <dir>`, and re-run `a2a attach` from the source file if it does not. |
| **An MCP tool call is refused with `<tool>: invalid input: json: unknown field "<name>"`** | **New in v0.25.6, and a behaviour change rather than a new rule.** Every MCP tool's published `inputSchema` is now closed and every decode site refuses an unrecognized field BY NAME. Before this, most sites silently dropped it: a call misspelling `expect_pack` as `expectPack` returned **success** having done less than it was asked. The refusal names the key, so the cause is almost always a typo or a camelCase/snake_case slip — read the tool's own `inputSchema` from `tools/list`, where every property now carries a `description`, rather than inferring a name from a CLI flag that sometimes differs. **For a harness author**: a field can no longer be sent ahead of the binary that understands it, so client rollout is coupled to `min_binary_version`. `a2a_exchange` also WITHDREW its `refs` property, which no handler had ever read. |
| **An MCP write is refused with `space is required when multiple spaces are connected` or `space "X" is not connected`** | The session is connected to more than one space, and this call could not name which one it targets. **There is no default, deliberately** — picking the first connected space is how a write lands in the wrong repo. Each tool resolves its own target: `a2a_submit` reads the DRAFT's own `space:` field (so the fix is the draft, not the call — an unfilled `<...>` placeholder gets its own message saying so); `a2a_lifecycle` and `a2a_exchange` derive it from the artifact ids you passed and refuse REF-025 (see the row below) when none of your connected spaces' mirrors hold one; `a2a_contract`'s `deprecate`/`retire`/`adopt`/`activate` take an explicit `"space"` input, the same field its `publish`/`preflight`/`materialize`/`check` siblings already take — a contract's committed path carries no artifact id, so it cannot be derived. A batch whose ids span two spaces is refused naming both: one call targets one space. A session with ONE connected space never sees any of this. |
| **`a2a` refused with `REF-025 "artifact ... is not held by any connected space's mirror; spaces walked ..."`** | You have MORE THAN ONE connected space (a project with exactly one never reaches this rule at all — there is no second space to have picked wrong), and the id you named is not held by any of them, as of the last time each was synced. The message names the id and every space whose mirror was WALKED — read that list first. It says "walked", not "searched", on purpose: a connected space whose mirror has never been cloned is a directory that is not there, so the walk finds nothing and the space is still listed. Being on the list means it was looked at, not that its contents were examined. if the space you expect the id to live in is not on it, `a2a connect` never registered it and that is the real fix, not this refusal. If it IS on the list, two things to try, in order: **(1) `a2a sync`.** The resolution walk reads each connected space's mirror as it stood after the LAST sync, not a live fetch — an id a teammate published moments ago is invisible to it until you sync. **(2) Check you have the right id.** This replaces a worse failure mode: before this rule, an unresolved id silently fell back to your FIRST connected space and ran the verb there anyway — right id, wrong space, with no warning. A `contract` id (`XC-<slug>`) or a data-package id (`DP-<system>-...`) is never found by this walk EVEN WHEN the space that holds it is connected and synced: a contract's committed path is `<slug>/provides/<name>/contract.md` and a data package's is `<system>/data/<DP-id>/manifest.json`, neither shaped like the `<id>.md` this rule looks for. Those two resolve by an EXPLICIT space input instead (`a2a_contract`'s `"space"` field on the MCP surface; on the CLI, `a2a contract`/`a2a data fetch`/`a2a data verify` target your project's first connected space directly rather than deriving one from the id) — if a contract or data-package verb needs to reach a SPECIFIC one of several connected spaces, `a2a disconnect` the others first rather than expecting the id to route you there. |
| **`a2a submit` / `a2a space update` / an MCP write refused with `REF-026 "the remote mirrored at ... publishes no default branch (refs/remotes/origin/HEAD) — refusing to guess a push target"`** | A space's base branch is DERIVED from its own remote's `refs/remotes/origin/HEAD` — there is no `base_branch` manifest key (D2, `DECISIONS.md`), on purpose: one fact, one source. This refuses instead of falling back to `main` (the exact "joined a `master`-default space, got silently pushed at a branch nobody named" defect no-silent-yes-2026-08 exists to end). Two real causes: **(1)** the space's mirror was cloned before the remote's default branch was ever set (a very new/scripted repo) — on GitHub itself this means no branch has been pushed yet, or the repo's Settings → Branches "Default branch" points at nothing; set it, then `a2a sync`. **(2)** the local mirror is stale or was assembled unusually (a hand-copied `.git` dir, a shallow/partial clone that dropped the symbolic ref) — `a2a sync` re-fetches; if it still refuses, re-`a2a connect` the space to get a clean clone. `a2a doctor`'s `default branch healthy` row names each connected space's own derived branch (or that it could not be determined) BEFORE your first submit, so check that first rather than discovering this at write time. |
| **`a2a validate --ci` refused with `this cwd (<path>) is a participant project, not a space checkout`** | **New in v0.25.7.** `--ci` validates a SPACE and needs a `space.yaml` at the working directory; a participant project has none. Two ways out, and the refusal prints whichever applies: run it inside a connected space's own synced mirror — it names EVERY connected space and its mirror path, not a guess — or stay put and pass `--space <id>`, which runs the same validation against that space's mirror. An unknown id is refused naming the ids that ARE connected; a checkout with none is told to `a2a init` first. Both read the mirror as of the last `a2a sync`. Not to be confused with `a2a doctor --space`, which is a v2 diff and is rejected in v1-min. |
| **`a2a attach` sits for a long time and then fails offline, naming `FindPRByHeadBranch`** | `attach` is a network write, same as `a2a data deliver` — it needs `a2a init`, a connected space, and a resolvable credential. Offline or against an unreachable host, the write funnel calls GitHub before anything else runs, so you wait out the timeout and then see a raw transport error (`host: FindPRByHeadBranch: github api request failed: status 404`) rather than a clean "you're offline" message (epic-backlog B29). It is not a sign of a corrupt draft or a bad blob: restore network/host access and retry. |
| **`a2a mcp` exits at once with `space: LoadProjectConfig: <somewhere>/.a2a/config.yaml: no such file or directory`, and `<somewhere>` is not your repository** | **New in v0.26.0.** `a2a mcp` takes its space from the directory it was started in. Editors start it inside your project; an `.mcpb` bundle host starts it beside the bundle, because that format cannot declare a working directory. Set **`A2A_PROJECT_ROOT`** to your connected space — the directory holding `.a2a/config.yaml` — which the bundle prompts for at install time. It is validated: a relative path, a missing one, or a file is refused by name. Leave it unset when your host supplies a working directory. |
