Anna Developer Hub docs — a batch of confirmed errors (verified against the shipped CLI/SDK/schema)

Hi all :waving_hand:

While building an Anna App, I went through most of the Developer Hub docs page by page and cross-checked every concrete API claim against what actually ships. Below are the issues I could confirm against the published packages.

## 1. Confirmed factual errors

These are cases where the documented behaviour is contradicted by the shipped CLI, SDK, schema, or official scaffold templates.

### 1.1 Plugin protocol: the run method is `invoke`, not `call`

- **Docs (Concepts / Architecture):** “Implements `describe` … and **`call`** (run a tool)”; “Runtime sends **`call`** as a single JSON-RPC line.”

- **Actual:** The JSON-RPC method is **`invoke`**. There is no `call` method anywhere. All three official templates handle `case “invoke”` (`templates/executa/{python,node,go}`), and the host issues it via `runner` (`invoke()` → `call(“invoke”, { tool, arguments })`). The protocol method set is `describe` / `invoke` / `health` / `initialize`.

- **Fix:** Replace `call` with `invoke` in both pages. A plugin that implements `call` will never be reached.

### 1.2 v2 `initialize` handshake uses snake_case on the wire, not camelCase

- **Docs (Lifecycle & Capability Negotiation):** request uses `clientInfo`, `capabilities`; response uses `serverInfo`; sampling caps shown as `capabilities.sampling.{modalities,maxTokensPerCall,maxCallsPerInvoke}` plus `fileTransport: true`.

- **Actual:** The host sends `HOST_INITIALIZE_PARAMS` with **snake_case** keys: `client_info`, `client_capabilities`, and the only camelCase key is `protocolVersion`. The capability set is `sampling / agent / storage / image / image.edit / upload` (each `{}`). There is **no** `fileTransport`, and the sampling sub-fields shown in the docs are not present. Official templates respond with `server_info` (snake_case).

- **Why it matters:** A plugin reading `params.clientInfo` / `params.capabilities` per the docs gets `undefined`.

- **Fix:** Use `client_info` / `client_capabilities` in the request example, the capability key set above, and `server_info` in the response example.

### 1.3 `health` status: templates return `“ok”`, which is not in the documented enum

- **Docs (Protocol / Lifecycle):** “`status` must be one of `ready`, `error`, `initializing`.”

- **Actual:** All three official standalone templates return `{“status”:“ok”}` (`python`, `node`, `go`). Several Quickstart pages also use `“ready”`. So the official artifacts disagree with each other and with this enum.

- **Fix:** Either add `ok` to the enum (and clarify it’s the recommended value), or fix the templates. Right now a plugin copied from a template returns a status the spec says is invalid.

### 1.4 The manifest `name` “required plugin identifier” model is outdated

- **Docs (Protocol / Common Pitfalls):** `name` is “Required: yes / the plugin identifier”; Pitfall asks you to make `tool_id` == describe `name` == archive `manifest.json` `name` all equal.

- **Actual:** Identity is the **server-minted `tool_id`**. The current standalone scaffold **omits the top-level `name`** and uses `display_name` only; the templates carry an explicit comment: *“The manifest no longer declares a top-level name; the plugin’s identity is the server-assigned tool_id … not a self-declared manifest name.”* The Binary Distribution and Publishing pages confirm the Agent **no longer reads a self-reported name** and “mismatches no longer matter.”

- **Nuance (so the fix is correct):** the app-bundled *minimal* scaffold still sets `name: “_TOOL_ID_”`, so a literal `name` isn’t *rejected* — it’s just no longer the identifier. The wrong claim is “name = the required identifier.”

- **Fix:** State that identity = minted `tool_id`; `name`/`display_name` do not participate in any join. Remove the “make all three names equal” guidance.

-–

## 2. CLI commands / package names that don’t exist as documented

These will fail the moment a reader copy-pastes them.

### 2.1 `executa dev` sampling flags are wrong

- **Docs (Sampling):** `anna-app executa dev ./my-plugin --sampling mock --sampling-unsupported-format`

- **Actual flags:** `–dir `, `–no-sampling`, `–mock-sampling `, `–app-slug`, `–sampling-account`. There is **no** `–sampling ` and **no** `–sampling-unsupported-format`; the target is `–dir`, not a positional arg.

- **Fix:** `anna-app executa dev --dir ./my-plugin --mock-sampling ./sampling-fixture.jsonl`. To exercise the unsupported-format path, put a downgraded response in the fixture (the python template ships `sampling-fixture.jsonl` as a reference).

### 2.2 `anna-app dev --llm ` does not exist

- **Docs (Local Dev with --llm):** `–llm real`, `–llm mock --mock-llm …`, `–llm off`.

- **Actual:** The LLM flags are `–no-llm`, `–mock-llm `, `–llm-account`, `–llm-app-slug`. There is no bare `–llm ` (the two `–llm…` options are `–llm-account` / `–llm-app-slug`).

- **Mapping:** `–llm real` → default (no flag); `–llm mock --mock-llm X` → just `–mock-llm X`; `–llm off` → `–no-llm`.

### 2.3 `–slug` default is not `manifest.tags[0]`

- **Docs (Local Development):** `–slug` “Default: from manifest `tags[0]`.”

- **Actual:** `deriveSlug` = `manifest.slug ?? manifest.name`. Unrelated to `tags`. (Concrete counter-example: an app with `slug: “avalon-v2”` and `tags[0] = “game”` would be mis-derived as `game` per the docs.)

- **Fix:** “Default: `manifest.slug`, falling back to `manifest.name`.”

### 2.4 “No `llm.complete` in dev — returns `not_implemented`” is outdated

- **Docs (Local Development):** “No `llm.complete` — the dispatcher returns `not_implemented` … use a dev.mocks entry.”

- **Actual:** dev has a full LLM bridge with three modes (off / mock / real). `–no-llm` returns `llm_disabled` (not `not_implemented`); `–mock-llm` serves a fixture; default goes real. The newer --llm/Agent pages already describe this.

- **Fix:** Replace with “use `–mock-llm `, or `–llm-account` for real.”

### 2.5 “Publishing happens entirely in the Console — there is no CLI” is outdated

- **Docs (Publishing an App):** “There is no zip upload, no CLI, and no separate submission portal.”

- **Actual:** CLI 0.1.28 has the full developer-side lifecycle: `apps push`, `apps cut `, `apps release `, `apps publish`, `apps submit-review`, `apps archive`, `apps unpublish`, `apps status` / `versions` / `grants`, plus the bundle upload pipeline (`bundle/init|file|finalize`). Only admin approve/reject is server-side.

- **Fix:** “Publishing can be done via the Developer Console **or** the `anna-app` CLI (`apps push/cut/release/publish/submit-review/archive`); only admin review (approve/reject) is server-side.”

### 2.6 Wrong package name in the test import

- **Docs (Recording & Replay):** `import { mountBundle } from “@anna/cli/test”;`

- **Actual:** the package is `@anna-ai/cli` (sub-export `@anna-ai/cli/test`). `@anna/cli/test` does not resolve — `-ai` is missing.

### 2.7 `@anna-ai/app-ui-sdk` is not a real package

- **Docs (App UI Host API):** `import { hostBridge } from “@anna-ai/app-ui-sdk”`

- **Actual:** there is no `app-ui-sdk`. The real SDK is `@anna-ai/app-runtime`, exporting `AnnaAppRuntime` (imported via the runtime URL `/static/anna-apps/_sdk/latest/index.js`). The `hostBridge.invoke(…)` shape here also disagrees with the `AnnaAppRuntime.connect()` shape used on the SDK page.

- **Fix:** standardize on `@anna-ai/app-runtime` + `AnnaAppRuntime`.

-–

## 3. Schema / SDK reference errors

### 3.1 `dispatcher_version` is `0.10.0`, not `0.4.0`

- **Docs (Host API vs Executa Tool):** “schema pinned at `dispatcher_version=0.4.0`.”

- **Actual:** `@anna-ai/app-schema@0.10.0` ships `dispatcher_version.txt = 0.10.0` (it tracks the app-schema version).

- **Fix:** Update to `0.10.0`, or drop the hard-coded number and say it tracks app-schema.

### 3.2 `host_api.storage` allowed values are `get`/`set`, not `read`/`write`

- **Docs (App UI Manifest):** host_api table lists `storage | read, write`.

- **Actual:** the storage namespace methods are `get` / `set` / `delete` / `list` (`host_api/methods.json`; the SDK is `anna.storage.get/set`). `read` / `write` are methods of the **`fs`** namespace. The docs appear to have copied the **permission verbs** (`storage.read` / `storage.write`, which belong to the root `permissions` field) into the **method-name** column.

- **Why it matters:** declaring `ui.host_api.storage: [“read”,“write”]` makes `anna.storage.get/set` fail the ACL with `permission_denied`.

- **Fix:** `ui.host_api.storage` values are `[“get”,“set”]` (and `delete`/`list`).

### 3.3 The Host API namespace tables omit `agent`

- **Docs (App UI Manifest + App UI Host API):** the namespace tables list `tools / chat / artifact / llm / fs / storage / prefs / window` — no `agent`.

- **Actual:** `host_api/methods.json` has a full `agent` namespace with **7 `session.*` methods** (`session.create/run/cancel/delete/history/list/refresh`). (Note: `complete` is `llm.complete`, not `agent.complete`.) This is how a UI app drives multi-turn AI runs from the iframe, so it really should be in the general tables. There’s a dedicated App-Side LLM & Agent page that documents `agent`, but the two general Host API tables don’t cross-reference it.

- **Fix:** add an `agent` row (with `session.*` methods + the matching `host_api.agent` ACL) to both tables, or at least a pointer to the dedicated page. (`image` and `upload` namespaces are also absent from the table.)

### 3.4 SSE `event` kind table doesn’t match `AnnaAppEvent.json`

- **Docs (App UI Windows):** lists `entry_payload` as an event kind and is missing several real ones.

- **Actual:** `AnnaAppEvent.json` (schema 0.10.0) defines exactly **11 kinds**: `artifact_appended`, `chat_message_from_app`, `close_view`, `geometry_changed`, `open_view`, `ping`, `rpc.stream`, `runtime_state_synced`, `status_changed`, `title_changed`, `window_focus_changed`.

- `entry_payload` is **not** a kind (it’s carried inside another kind’s payload; the SDK also exposes an `on(“entry_payload”)` callback, but that’s an SDK-layer name, not a wire kind).

- The table is **missing** `artifact_appended`, `chat_message_from_app`, `ping`, `rpc.stream`.

- **Fix:** align the table with the 11-kind enum; drop `entry_payload` from the wire-kind list.

### 3.5 SSE envelope field is `by_client_id`, not `client_id`

- **Docs (App UI Windows):** envelope example uses `“client_id”` (originating client, for echo suppression).

- **Actual:** `AnnaAppEvent.json` defines `by_client_id` (string|null).

### 3.6 iframe Agent session frames are OpenAI-style chunks, not `token`/`complete`/`delta`

- **Docs (Agent Sessions / App-Side LLM & Agent):** iframe streaming shown as `frame.event === “delta”` / `frame.text` (and older `token` / `complete`), described as “same frame shapes” as the plugin side.

- **Actual:** `@anna-ai/app-runtime@0.8.0` **does not normalize frames** — the SDK source contains no `token` / `complete` / `choices` / `delta` literals; it forwards the host frames verbatim. The real iframe frames are **OpenAI-style chunks**: text is in `choices[0].delta.content`, completion/usage in `choices[0].delta.task_complete.token_usage`, ending with `event_type: ‘stream_end’` → `[DONE]`.

- **Fix:** show iframe consumers reading `frame.choices?.[0]?.delta?.content`, or explicitly state the SDK does not normalize and the raw frames are OpenAI chunks. (This is an iframe-vs-iframe comparison, confirmed against both the shipped SDK and a running app — not a cross-surface mix-up.)

-–

## 4. Local-validation divergences (docs describe server rules; the CLI validator differs)

Flagging these because they bite developers running `anna-app validate` locally — the doc’s stated rule and the CLI’s actual regex/constant disagree, so “valid per the docs” can fail locally (or vice-versa).

### 4.1 slug regex — CLI rejects trailing hyphen, docs allow it

- **Docs (Listing Fields):** `^[a-z0-9][a-z0-9-]{2,79}$` (allows a trailing `-`).

- **CLI:** `SLUG_RE = /^[a-z0-9][a-z0-9-]{1,78}[a-z0-9]$/` — must end in `[a-z0-9]`, no trailing hyphen. So `my-app-` passes the documented regex but is rejected by `anna-app validate`.

- **Fix:** align the doc regex to the CLI form (or clarify which the server enforces).

### 4.2 storage error codes — docs disagree with the CLI `StorageBridge` constants

- **CLI `StorageBridge` defines:** `INVALID_REQUEST = -32020`, `NOT_GRANTED = -32021`, `NOT_FOUND = -32022`, `PRECONDITION_FAILED = -32023`, `UPSTREAM = -32026`.

- **Docs:** put `INVALID_REQUEST` at `-32028`, label `-32026` as `RATE_LIMITED`, and put `UPSTREAM_ERROR` at `-32029`. (`-32021/-32022/-32023` do match.)

- The image (`-32101…-32105`) and upload (`-32201…`) code tables, by contrast, match the CLI exactly — which suggests the storage table is genuinely off rather than a convention I’m misreading.

- **Fix:** reconcile the storage table with the shipped constants (or document the server-vs-client split if they really differ).

-–

## 5. Internal contradictions within the doc set

Independently verifiable just by reading the docs against each other (no baseline needed).

### 5.1 Binary Distribution page contradicts itself on `name`

The IMPORTANT box says the Agent “no longer reads a self-reported manifest name … mismatches no longer matter,” but the same page’s field table says `name` “must equal the `tool_id` you minted **and the name your binary returns from describe**.” Pick one (the IMPORTANT box matches the current identity model).

### 5.2 `host_api.storage` — Manifest page vs Host API page

The App UI Manifest page lists storage as `read, write`; the Host API page correctly lists `get/set/delete/list` and separates the permission verbs. The Host API page is the correct one (see §3.2).

### 5.3 Sampling error codes — Sampling page vs Common Pitfalls

- The Sampling page’s code table and the Common Pitfalls page assign **different codes to the same scenario**: e.g. “manifest missing `llm.sample`” is `-32008` on the Sampling page but `-32001` in Pitfall 8; “cumulative tokens exceeded” is `-32007` vs `-32005` in Pitfall 10.

- I can’t tell which set is correct (the sampling code values aren’t observable from the client baseline), but the two pages **contradict each other** and at least one is wrong. Pitfall 8 is also internally inconsistent: it pairs `-32001` with the message `“not_negotiated”`.

- **Fix:** make the two pages agree, and reconcile against the real Nexus code table.

Happy to file these individually as issues if that’s more useful — just let me know the preferred format. Thanks for the docs overall; most of the API surface was accurate and easy to follow. :folded_hands:

:yellow_heart: Thank you for this incredible report!

Hey @YuntianBi — wow, this is exactly the kind of careful, verified-against-the-shipped-code feedback that makes the docs better for everyone. :raising_hands: We went through every single item you raised, double-checked each one against the production host, and shipped a full round of fixes. Here’s where things landed:

:white_check_mark: Docs corrected

Most of your findings were spot-on and are now fixed across the developer hub:

  • callinvoke in the protocol overview & architecture :memo:
  • Manifest name clarified — identity is the server-minted tool_id, not a self-reported name :id_button:
  • executa dev / sampling flags updated to the real --dir / --mock-sampling / --no-llm / --mock-llm forms :triangular_flag:
  • Package & import names@anna-ai/cli/test, and the App UI examples now use AnnaAppRuntime.connect() :package:
  • Host API — added the missing agent / image / upload namespaces, fixed ui.host_api.storage to ["get","set","delete","list"] :card_index_dividers:
  • Window events — envelope field is by_client_id, and the kind list now matches the schema exactly (no entry_payload; added artifact_appended, chat_message_from_app, ping, rpc.stream) :window:
  • Agent stream frames — corrected to the real shape (choices[0].delta.content, task_complete.token_usage, terminal {event:"end"}) :ocean:
  • Slug regex, dispatcher_version, publishing flow, and the error-code mix-ups in the pitfalls page (-32008 not_negotiated, -32007 MAX_TOKENS_EXCEEDED) — all fixed :gear:

:magnifying_glass_tilted_left: A couple of friendly clarifications

For three items, we dug in and found the docs were actually matching production — the real fix belonged in the CLI/templates instead. So we fixed those there:

  • Handshake casing (§1.2): production sends camelCase (clientInfo / capabilities) — the docs were right; the CLI dev runner was the one drifting. :sparkles:
  • Storage error codes (§4.2): the docs matched the production host; the CLI’s mock codes were off. Now aligned. :1234:
  • health status (§1.3): production accepts ready / error / initializing — and the starter templates were returning "ok". Fixed in all templates! :stethoscope:
  • (Tiny note on §3.6: the terminal frame is {event:"end"}, not stream_end/[DONE] — everything else you described was accurate.) :+1:

:rocket: Net result

Cleaner docs, and a few latent template/CLI bugs squashed thanks to you. This was a genuinely high-quality report — please keep them coming, we love this stuff! :speech_balloon::yellow_heart: