Skip to content
Agentmart
Submit a tool

model-command-response

Derive the exact shape of a model command's response before consuming it in a screen — never hand-write the interface from memory.

Skill by IgorAIvanov

IndexedMIT★ 1
Write a review

Chalk Labs

Need help deploying model-command-response?

Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.

Book a call

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/igoraivanov-model-command-response

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Derive the exact shape of a model command's response before consuming it in a screen — never hand-write the interface from memory. Use whenever a component reads `data.rows`, `data.item`, `data.extra` or any field of a command's answer, especially for core commands (menu/current, printPdf) whose SQL lives inside the framework package.

Use cases: frontend, backend, data, writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/model-command-response

What's inside

Skill name
model-command-response
Description
Derive the exact shape of a model command's response before consuming it in a screen — never hand-write the interface from memory. Use whenever a component reads `data.rows`, `data.item`, `data.extra` or any field of a command's answer, especially for core commands (menu/current, printPdf) whose SQL lives inside the framework package.
License
MIT
Metadata
audience: app
Instructions
121 lines in SKILL.md · view on GitHub

Files (1)

  • SKILL.md

SKILL.md (first lines)

# Response shape of a model command

The **envelope** is fixed and identical everywhere:

```json
{ "ok": true, "data": { "item": …, "rows": [], "options": {}, "totals": {} }, "messages": [] }
```

What is **inside** `item` / `rows` / `extra` is not. It is whatever the SQL function
put there with `jsonb_build_object`, key by key. The runtime does not rename
anything: no snake_case → camelCase conversion happens on the way out. If the
function writes `'route'`, the client receives `route` — not `routePath`.

So: **read the source, do not recall it.** A hand-written interface is
self-consistent, so TypeScript stays green and the screen silently renders
nothing useful.

## How to find the shape (in this order)

**1. Your own model** — the SQL is in the repository:

```
app/<family>/<model>/db/_generated/<model>.crud.gen.sql   ← standard commands
app/<family>/<model>/db/<model>.custom.sql                ← everything else
```

Find `jsonb_build_object(` inside the function and copy the keys.

**2. A core model** (`menu`, `users`, `user_group`, print, attachments) — the SQL
ships inside `@altera/server` as an embedded text map, and after `deno install`
it is a real file on disk:

```
vendor/jsr.io/@altera/server/<version>/sql/core-sql.generated.ts
```

Grep it for the function name (`menu_current`, `user_list`, …). The text is the
same SQL, escaped into a string — the `jsonb_build_object` keys are readable.

**3. Call the command and look.** The screen itself does it through
`bus.request("data.load", { model, command, payload })`; from outside, POST
`/api/model/<model>/<command>` with the session cookie. Printing the envelope
once is cheaper than debugging a component that renders blanks.

## Type it at the call site, and say where it came from

```ts
/** Рядок відповіді `menu/current` (server/sql/menu/db/menu.sql). */
interface MenuRow {
  id: string;
  parentId: string | null;
  name: string;
  icon: string | null;
  route: string | null;
}

const envelope = await bus.request("data.load", {
  model: "menu", command: "current", payload: {},
}) as { data?: { rows?: MenuRow[] } } | undefined;
```

`bus.request` is typed generically on purpose — only the caller knows the shape of
the command it invoked. Narrow explicitly, and put the source function in a
comment above the interface so the next reader can re-check it.

For your own models the interface must come from the model's TypeBox schema
instead — see [typebox-model-schema](../typebox-model-schema/SKILL.md). Never
re-declare a row type that already exists there.

## The trap this skill exists for

`menu/current` returns a **flat** list, and the route key is `route`:

```sql
jsonb_build_object(
  'id',       m.path,          -- ланцюжок кодів, не число
  'parentId', m.parent_path,
  'name',     m.name,
  'icon',     m.icon_key,
  'route',    m.route_path     -- null → тека
)
```

A menu component was written against an imagined `{ code, name, routePath,
children }`. Result: every `routePath` was `undefined`, so every item took the
"folder" branch and rendered as a grey section header — nothing was clickable,
and the tree was never built because nothing populated `children`. Types were
green, the request succeeded, the envelope was `ok: true`. The only symptom was
a menu that looked like two headings.

Two lessons encoded here:

- **flat vs nested is part of the contract.** If the answer is flat with
  `id`/`parentId`, the client builds the tree — and decides what to do with an
  orphan (a child whose parent is hidden by permissions should be lifted to the
  root, not dropped);
- **a wrong key name never throws.** It reads as `undefined`, and `undefined`
  usually means "empty" somewhere downstream. Nothing fails loudly, so the only
  defence is deriving the shape rather than assuming it.

## Core shapes worth knowing

| command | what arrives |
|---------|--------------|
| `menu/current` | `rows: { id, parentId, name, icon, route }` — flat, pre-sorted; `route` is a **view route** (`family/model/view`), `null` for a folder |
| `<model>/printPdf` | `data.extra: { fileName, mimeType, pdfBase64 }` — the PDF is base64 in `extra`, not a download URL |
| `<model>/list` | `rows` + `totals: { count, page, pageSize }`; the effective query mirrors back into `$query` |
| `<model>/get` | `item` (or `null` when not found) + `options` |
| `<model>/lookup` | `rows` of `{ id, name, … }` — only the fields marked `x-lookup` |

Attachment fields are the one place where the runtime **does** rewrite a value:
SQL returns the raw `access_key` in `token` / `<field>Token`, and the runtime
swaps it for a session-scoped token. The key name stays as SQL wrote it.

## Related

- [db-function-contract](../db-function-contract/SKILL.md) — the other side: writing the function that produces this answer.
- [model-form-root](../model-form-root/SKILL.md) — how `data` becomes `$root` in a screen.
- [model-list-form](../model-list-form/SKILL.md) — list screens, where `rows`/`totals` are handled by the base class.

From IgorAIvanov/altera03, MIT.

More from IgorAIvanov/altera03

Signals

Stars
1
Forks
0
Open issues
0
Last commit (this folder)
Aug 4, 2026 (2 months ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from IgorAIvanov

Done for you

Rather not set it up yourself?

Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.

See how we build AI agents →Read the case studies

Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.

launches shipped
50+
launches shipped
media placements
500+
media placements
founders on every account
2
founders on every account

Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block

Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.

Get model-command-response running in your business

Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying model-command-response?

CallGet help
Cite this page

model-command-response skill. Agent Mart. https://agentmart.dev/skills/igoraivanov-model-command-response (last updated 2026-10-24)