Agent skill

Relay Mutation Store Updates

by lablup in lablup/backend.ai-webui

A skill your agent uses when writing if (success) updateFetchKey(), an onRequestClose handler, or any refetch after a mutation; when a setting modal handles both create and update behind one…

LGPL-3.0Auto-check passedDevelopment

Install Relay Mutation Store Updates

skills CLI
$ npx skills add lablup/backend.ai-webui --skill relay-mutation-store-updates -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install lablup/backend.ai-webui relay-mutation-store-updates --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/lablup/backend.ai-webui.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/relay-mutation-store-updates .claude/skills/relay-mutation-store-updates && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
relay-mutation-store-updates
GitHub stars
133
Token cost
~2.8k tokens
SKILL.md length
1,295 words
Files
1
Skills in repo
13
Repo updated
First seen
Licence
LGPL-3.0

At a glance

A skill your agent uses when writing if (success) updateFetchKey(), an onRequestClose handler, or any refetch after a mutation; when a setting modal handles both create and update behind one…

  • Works in 7 steps: The decision table → Update: fill the selection set → Is the node even available? Check the… → …
  • Writing if (success) updateFetchKey()
  • SKILL.md covers Activation Triggers, 1. The decision table, 2. Update: fill the selection… and 3. Is the node even available?…, plus 7 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Relay Mutation Store Updates is an agent skill from lablup/backend.ai-webui. Use when writing if (success) updateFetchKey(), an onRequestClose handler, or any refetch after a mutation; when a setting modal handles both create and update behind one fragment prop; when choosing a mutation's response selection set; or when debugging "the list doesn't refresh after saving" / "why is it fetching twice". Covers when Relay patches the normalized store on its own, and when a refetch is genuinely the right answer.

Its SKILL.md is about 2.8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Development. The repository describes itself as: Backend.AI Web UI for web / desktop app (Windows/Linux/macOS). Backend.AI Web UI provides a convenient environment for users, while allowing various commands to be executed… The licence is LGPL-3.0.

When your agent uses it

  • Writing if (success) updateFetchKey()
  • An onRequestClose handler
  • Any refetch after a mutation
  • A setting modal handles both create and update behind one fragment prop

Example prompts

  • “s response selection set; or when debugging”
  • “t refresh after saving”
  • “why is it fetching twice”
  • “/relay-mutation-store-updates”

Workflow steps

7 steps, taken from the step headings in SKILL.md.

  1. The decision table
  2. Update: fill the selection set
  3. Is the node even available? Check the schema first
  4. Legacy ok/msg mutations: keep the refetch
  5. Keep success honest — decide the refetch at the call site
  6. Create and delete: refetch
  7. When an update may refetch too

What it can do on your machine

Read from SKILL.md and the folder at commit d6afd57. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are graphql and typescript).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Relay Mutation Store Updates loads about 2.8k tokens when it runs. Until then it costs about 117 tokens; SKILL.md has 1,295 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~117
When it runs · the whole SKILL.md, loaded when a task matches
~2.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from lablup/backend.ai-webui at commit d6afd57, republished under its LGPL-3.0 licence (© lablup). 1,295 words, ~2,793 tokens.

Download SKILL.mdSave it as .claude/skills/relay-mutation-store-updates/SKILL.md (or your agent's skills folder).
name
relay-mutation-store-updates
description
Use when writing `if (success) updateFetchKey()`, an `onRequestClose` handler, or any refetch after a mutation; when a setting modal handles both create and update behind one fragment prop; when choosing a mutation's response selection set; or when debugging "the list doesn't refresh after saving" / "why is it fetching twice". Covers when Relay patches the normalized store on its own, and when a refetch is genuinely the right answer.

Relay Mutation → Store Updates

The rule this skill exists to enforce:

A refetch after an update mutation is a bug, not a refresh. Update mutations should return the changed fields so Relay patches the normalized store by id. Refetch only when list membership changes.

This skill is the detailed treatment: how to tell the cases apart, and what to do in each.

Activation Triggers

  • Writing or reviewing onRequestClose={(success) => { if (success) updateFetchKey(); }}
  • A modal takes a nullable *Frgmt prop and branches create vs update inside
  • Choosing the selection set of a mutation payload
  • "The list doesn't refresh after saving" / "why is it fetching twice?"

1. The decision table

Ask what changed, not did it succeed.

What the mutation changedWhat to do
Fields of an entity already in the store (update)Select the changed fields on the returned node. Relay merges by id. No refetch.
Same, but a changed field is in the list's filter/orderByStill no refetch — patch the row. The row may stop matching the active filter, and that is accepted: the user should see the values they just edited on the row they touched, and every list carries a manual refresh (BAIFetchKeyButton) for re-evaluating the predicate.
Same, but the payload returns only ok/msg (legacy)Keep the refetch and comment why (§4). Nothing to merge.
List membership — a row added or removed (create/delete)Refetch the list (§6). Don't patch the connection.
Server-derived fields you did not send (computed status, timestamps)Select those too; refetch only if the server cannot return them (§7).

2. Update: fill the selection set

An update mutation must return the entity and every field the calling UI reads that the mutation could have changed. Relay merges the payload into the normalized record by id, and every component holding that fragment re-renders — with no extra network traffic.

graphql
# ❌ Payload carries no data, so the store goes stale
# and the caller is forced to requery the whole list.
modify_user(email: $email, props: $props) {
  ok
  msg
}
graphql
# ✅ Spread the fragment the UI actually reads, so the selection set
# cannot drift out of sync with the component.
modify_user(email: $email, props: $props) {
  ok
  msg
  user {
    id
    ...YourListRow_user # ← the fragment your list row already renders
  }
}

If the consumer has no reusable fragment, hand-list every field the UI reads. Relay keys normalized records by node id, but on types that implement Node the compiler adds id to the network operation automatically — writing it explicitly is a readability convention, not a correctness requirement. The real gaps are always missing fields, not a missing id.

⚠️ Partial coverage is worse than refetching

Dropping the refetch is only safe if the payload covers every field the UI reads that the mutation could change. A partial selection set gives you the worst of both: no refetch and no store update.

UserSettingModal demonstrates the trap. It sends groupIds and its fragment reads projects { edges { node { id } } }, but the mutation payload never selects projects — and the update path skips the refetch. Changing a user's project membership succeeds on the server and leaves the UI showing the old projects until a manual refresh.

Before removing a refetch, diff the two lists:

  1. Fields the component's fragment reads.
  2. Fields the mutation payload returns.

Anything in (1) that the mutation input can change and (2) omits is a stale-UI bug. Add it to the selection set — or keep the refetch and say why.

3. Is the node even available? Check the schema first

Most of this backend already returns the node; the frontend just isn't asking for it. Before concluding a refetch is required, grep the payload type in data/schema.graphql — if it has a node field, there is no excuse.

4. Legacy ok/msg mutations: keep the refetch

A handful of mutations (ModifyAgent, ModifyImage, ModifyKeyPair, ModifyScalingGroup, and the three Modify*ResourcePolicy) return only ok/msg. There is nothing for Relay to merge, so keep the refetch and move on — that is the accepted answer, not a gap to close. Leave a one-line comment saying the payload carries no node.

Do not open a migration to their node-returning successors as part of an unrelated change: it drags in backend version compatibility for little gain. If you are already rewriting one of these call sites for another reason and a successor exists (e.g. ModifyResourcePreset → UpdateResourcePresetPayload), using it is a bonus, not a requirement.

Writing an updater by hand is also possible but rarely worth it. One component does this today — react/src/components/AgentSettingModal.tsx:

tsx
commitModifyAgentSetting({
  variables: { id: toLocalId(agent?.id ?? ""), props: { ... } },
  updater: (store) => {
    const agentRecord = store.get(agent?.id || "");
    if (agentRecord) {
      agentRecord.setValue(values.schedulable, "schedulable");
      agentRecord.setValue(values.scaling_group, "scaling_group");
    }
  },
  onCompleted(res, errors) { ... },
});

Its call site (AgentNodeItems/AgentActionButtons.tsx) correspondingly does not refetch — it only closes. Leave that one as it is.

If you do write an updater, cover only fields you sent and know the server accepted verbatim. If the server transforms a value, you cannot guess it — keep the refetch instead.

Show full SKILL.md (575 more words)Show less

5. Keep success honest — decide the refetch at the call site

onRequestClose(success: boolean) means the mutation succeeded. Pass it truthfully.

Do not pass false after a successful update to suppress a refetch. false already means "cancelled", so overloading it makes the two indistinguishable to the caller — anything the caller later wants to do on success (a toast, clearing a selection, closing a drawer) silently stops firing after updates. UserSettingModal currently does this; it is a bug to copy from, not a pattern.

The fragment prop is the discriminator. A setting modal that handles both paths takes a nullable *Frgmt — null means create, non-null means update. The caller passes that prop, so it can branch on it with no signature change: if (success && entityFrgmt === null) refetch(). Create adds a row the connection doesn't know about; update has already been patched into the store.

One instance serving both paths:

tsx
<ResourcePresetSettingModal
  resourcePresetFrgmt={editingResourcePreset}
  open={!!editingResourcePreset || isCreating}
  onRequestClose={(success) => {
    // read the fragment BEFORE the resets below; the handler closes over the
    // render-time value, so check first and reset after.
    if (success && !editingResourcePreset) {
      startRefetchTransition(() => updateResourcePresetsFetchKey());
    }
    setEditingResourcePreset(null);
    setIsCreating(false);
  }}
/>

When the caller renders separate instances for create and edit — as AdminUserManagement does with userSettingFrgmt={selectedUser} and ={null} — the branch collapses. Its edit path still keeps the refetch, for the §2 reason: the status-toggle payload returns only user { id }, so the changed fields never come back and there is nothing to patch. Fill the payload first if you want to drop that one.

If a caller genuinely cannot know, enrich the result rather than lying about success — ContainerRegistryEditorModal already passes onOk('create' | 'modify').

6. Create and delete: refetch

Refetch the list. Don't patch the connection.

Connection directives (@appendEdge, @deleteRecord) look like the tidy answer, but they break under pagination: appending to a page-sized connection pushes rows past the cursor boundary, so the client's idea of the list drifts from the server's. Add sorting and filtering — where the new row's position is decided server-side — and a client-side insert is simply guessing.

One component uses @appendEdge today (packages/backend.ai-ui/src/components/fragments/BAIImportArtifactModal.tsx). Treat it as an exception that predates this rule, not a pattern to copy.

7. When an update may refetch too

Create and delete are settled by §6. An update may also legitimately refetch when:

  • The mutation has side effects on other entities the payload doesn't cover (e.g. changing a resource policy recomputes several users' quotas).
  • Aggregates shown alongside the list (counts, totals, usage) are computed server-side.
  • Bulk mutations where per-record payloads are impractical.

Leave a one-line reason in these cases. After an update, an unexplained refetch reads as the anti-pattern.

Enforcement

There is none — this skill and code review are the only guardrail, across ~149 updateFetchKey() call sites in react/src. A lint rule was considered and deliberately left out of scope (FR-3372). If that decision is revisited, the landing spot already exists: react/eslint.config.js uses no-restricted-syntax with an AST selector for the CSP <style> ban, and the onRequestClose={(success) => { if (success) updateFetchKey(); }} shape is matchable the same way.

Review Checklist

  • No if (success) updateFetchKey() where the mutation was an update
  • No refetch added because a changed field is a filter/orderBy property — stale-under-filter is accepted; the manual refresh covers re-evaluation
  • Every update mutation payload selects the fields the UI reads
  • Prefer spreading the consumer's fragment over hand-listing fields
  • Schema checked (data/schema.graphql) before concluding a refetch is required
  • Legacy ok/msg-only mutation → refetch kept, with a comment saying why
  • success is passed truthfully; the refetch decision lives at the call site
  • Create/delete refetch the list — no @appendEdge/@deleteRecord added
  • A refetch after an update has a comment naming why the store can't be patched
  • .specs/FR-3372-refetch-after-mutation/spec.md — the remediation spec this skill distills
  • FR-3170 — the audit epic; FR-3372 — this analysis

© lablup, LGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/relay-mutation-store-updates of lablup/backend.ai-webui.

Open the folder on GitHubat commit d6afd57

Compare with similar skills

Relay Mutation Store Updates next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Relay Mutation Store Updates compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Relay Mutation Store Updates this skilllablup/backend.ai-webui133—~2.8kAutomated safety check: PassLGPL-3.0
Backend Code Reviewlanggenius/dify158k—~676Automated safety check: PassCustom licence
Native Data FetchingCherryHQ/cherry-studio-app4k6 repos~2.9kAutomated safety check: NotesMIT
Twenty App Entity Developmenttwentyhq/twenty58k—~1.8kAutomated safety check: PassCustom licence
Go Pedantrychromedp/chromedp13k—~3.7kAutomated safety check: PassMIT
Gumroad Prod Consoleantiwork/gumroad9.8k—~2.9kAutomated safety check: NotesMIT

Similar skills

  • Backend Code Review

    langgenius/dify

    Reviews backend code under api/ for concrete, reproducible defects, routes to rule packs for architecture, schema, repositories and SQLAlchemy, and ranks findings from P0 to P3.

    158k GitHub stars~676 tokensUpdated today
    DevelopmentAuto-check passed
  • Native Data Fetching

    CherryHQ/cherry-studio-app

    A skill your agent uses when implementing or debugging ANY network request, API call, or data fetching.

    4k GitHub starsUsed in 6 repos~2.9k tokens
    DevelopmentAuto-check: notes
  • Guides changes to an existing Twenty app: adding or editing objects, layouts, logic functions and front components, with a plan stated before multi-entity edits.

    58k GitHub stars~1.8k tokensUpdated today
    DevelopmentAuto-check passed
  • Go Pedantry

    chromedp/chromedp

    This skill should be used when the user is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w, interface design (accept interfaces return structs)…

    13k GitHub stars~3.7k tokensUpdated 2 days ago
    DevelopmentAuto-check passed
  • Gumroad Prod Console

    antiwork/gumroad

    Execute read-only Ruby/Rails commands against Gumroad's production database for debugging and investigation.

    9.8k GitHub stars~2.9k tokensUpdated today
    DevelopmentAuto-check: notes
  • LangBot Plugin Development

    langbot-app/LangBot

    Guides building, debugging and testing LangBot plugins: components, SDK calls, README and locale rules, SDK pitfalls and WebSocket-based testing.

    18k GitHub stars~3.9k tokensUpdated yesterday
    DevelopmentAuto-check passed

More from lablup/backend.ai-webui

All 13 skills in this repo
  • Walkthrough

    lablup/backend.ai-webui

    Mint a walkthrough for the PR this session just implemented: a set of numbered stops a reviewer opens in the live dev server, each one marking an element on screen with what changed and what to check.

    133 GitHub stars~4.9k tokensUpdated today
    Auto-check: notes
  • Docs Lead

    lablup/backend.ai-webui

    A skill your agent uses whenever the user mentions docs, the manual, documentation, terminology, translations, or screenshots — including indirect mentions like "이 PR 문서 영향 봐줘", "문서 점검", "용어 통일"…

    133 GitHub stars~2.9k tokensUpdated today
    Auto-check passed
  • Backend AI Guide

    lablup/backend.ai-webui

    Expert guide for Backend.AI distributed computing platform. An agent skill from lablup/backend.ai-webui.

    133 GitHub starsUsed in 1 repo~1.8k tokens
    Auto-check passed
  • Dev Server

    lablup/backend.ai-webui

    Start the project's development server (pnpm dev for backend.ai-webui; discovered from README/package.json elsewhere), deriving the header color, app name, default backend endpoint and login…

    133 GitHub stars~6.9k tokensUpdated today
    Auto-check: notes
  • Record E2E Gif

    lablup/backend.ai-webui

    Record Playwright e2e tests as one GIF per test case (video → ffmpeg palette GIF) and return a markdown table for a PR description.

    133 GitHub stars~907 tokensUpdated today
    Auto-check: notes
  • Release Train Prep

    lablup/backend.ai-webui

    Post a Korean release risk digest to a Microsoft Teams thread, grouped by risk category.

    133 GitHub stars~5k tokensUpdated today
    Auto-check passed

Questions about Relay Mutation Store Updates

What does Relay Mutation Store Updates do?

A skill your agent uses when writing if (success) updateFetchKey(), an onRequestClose handler, or any refetch after a mutation; when a setting modal handles both create and update behind one…. ai-webui. Use when writing if (success) updateFetchKey(), an onRequestClose handler, or any refetch after a mutation; when a setting modal handles both create and update behind one fragment prop; when choosing a mutation's response selection set; or when debugging "the list doesn't refresh after saving" / "why is it fetching twice".

When should I use Relay Mutation Store Updates?

Relay Mutation Store Updates fits situations like: writing if (success) updateFetchKey(); an onRequestClose handler; any refetch after a mutation; A setting modal handles both create and update behind one fragment prop.

How do I install Relay Mutation Store Updates in Claude Code?

Run `npx skills add lablup/backend.ai-webui --skill relay-mutation-store-updates -a claude-code`. Or copy the skill folder (.claude/skills/relay-mutation-store-updates in lablup/backend.ai-webui) into .claude/skills/relay-mutation-store-updates in your project. Claude Code loads it when a task matches its description.

How do I install Relay Mutation Store Updates in Codex?

Run `npx skills add lablup/backend.ai-webui --skill relay-mutation-store-updates -a codex`. Or copy the skill folder (.claude/skills/relay-mutation-store-updates in lablup/backend.ai-webui) into .agents/skills/relay-mutation-store-updates in your project. Codex loads it when a task matches its description.

Can I use Relay Mutation Store Updates in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add lablup/backend.ai-webui --skill relay-mutation-store-updates -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/relay-mutation-store-updates, .gemini/skills/relay-mutation-store-updates, .github/skills/relay-mutation-store-updates and .opencode/skills/relay-mutation-store-updates in your project.

What does Relay Mutation Store Updates need to run?

SKILL.md names no scripts, command-line tools or credentials: Relay Mutation Store Updates is instructions for the agent only.

Does Relay Mutation Store Updates access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Relay Mutation Store Updates safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Relay Mutation Store Updates use?

Relay Mutation Store Updates is published under the LGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Relay Mutation Store Updates use?

About 2.8k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Relay Mutation Store Updates?

Skills that share tags, products or a category with Relay Mutation Store Updates: Backend Code Review (langgenius/dify, 158k stars), Native Data Fetching (CherryHQ/cherry-studio-app, 4k stars), Twenty App Entity Development (twentyhq/twenty, 58k stars) and Go Pedantry (chromedp/chromedp, 13k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Relay Mutation Store Updates?

lablup (a GitHub organization) maintains it in lablup/backend.ai-webui, which has 133 GitHub stars. The repository holds 13 skills in this directory. The repository was last updated on October 8, 2026.

Source: lablup/backend.ai-webui on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.