실제 제품을 적절한 조작 환경에서 실행해 변경 사항을 검증하고, 사용자가 확인할 수 있는 증거를 모아 인수 보고서로 게시합니다. 기존 검증 계획을 확인하거나 점검 항목을 만들고, `lh` CLI로 결과를 게시할 수 있습니다.
- 변경된 제품 동작을 실제로 확인하기
- 스크린샷 등 검증 증거 수집하기
- 검증 계획과 보고서 작성하기
SWR을 활용한 클라이언트 데이터 요청 구조를 안내합니다. API 호출을 서비스와 스토어 훅에 배치하고 로딩 및 오류 상태, 쓰기 후 캐시 갱신을 다루며 컴포넌트의 `useEffect` 요청을 옮기는 방법을 설명합니다.
Use for client APIs, SWR hooks, cache invalidation, async errors, useEffect migration, home first paint and persistent caches.
실행 점수 미검증
설치 방법 보기서비스, 스토어 훅, 컴포넌트로 클라이언트 데이터 요청을 나누는 방법을 안내합니다. 읽기에는 useClientDataSWR를 사용하고 API 호출은 서비스에 둡니다. 캐시 키 공유, 쓰기 후 데이터 갱신, 대기 상태 관리, 최초 로딩과 빈 결과 및 오류 구분을 다룹니다. useEffect 안의 요청과 컴포넌트 상태에 중복 저장된 데이터를 스토어 훅과 선택자로 옮기는 작업에도 활용할 수 있습니다. 로컬 복사본으로 렌더링하는 목록과 상세 화면에는 Replica 동기화 방법을 안내합니다.
화면에 오래된 데이터가 남거나 요청이 계속 로딩 중으로 보이는 문제, 최초 요청 오류가 빈 결과처럼 표시되는 문제를 살펴볼 때 유용합니다. 데이터 요청을 어디에 둘지 정리할 때도 사용할 수 있습니다.
LobeHub의 기존 래퍼와 코드 구조에 맞춰 구현합니다. SWR의 기본 동작을 그대로 가정하지 말고 실제 래퍼 설정을 확인해야 합니다.
이 소개문은 공개된 SKILL.md를 바탕으로 AI(OpenAI Codex)가 작성했습니다. 정확한 사양은 아래 원문을 확인하세요.
AI가 SKILL.md의 내용에서 주의가 필요한 작업과 도입 전제를 확인했습니다. 점수는 실제로 실행한 결과(실행 검증)만으로 매깁니다.
점수에는 넣지 않았습니다. 2026년 10월 26일 기준으로 AI(OpenAI Codex)가 판정했습니다. 참고용이므로 설치 전에 원문도 확인해 주세요.
설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
Component → Store useFetchXxx hook → Service → lambdaClient
← SWR request state + store data ← response
src/services/ own API calls: .query() for reads, .mutate() for writes.
Export a service instance per domain; components and stores do not call lambdaClient directly.useClientDataSWR and return its SWR response, including error
and mutate. Sync successful results into the store through the wrapper's supported
callback (onSuccess, or onData for the sync wrapper).@/libs/replica
instead: the store's useFetchXxx returns slice.useSync(params) (flags only) and
the engine writes the store. See the Replica section of zustand.useEffect or duplicate server data in component useState.useFetchXxx for read hooks and refreshXxx for cache invalidation.When changing home/sidebar first paint or persisted display data, read
references/home-first-paint.md. It covers
avoiding flicker, reusing persistence, and the user displaySnapshot.ts boundary.
Reuse the domain key factory in src/libs/swr/keys.ts; define a shared key if the
domain has none. Include every parameter that changes the response: entity or parent
id, filters, sort, and pagination. Read and refresh must construct the same key.
Import useClientDataSWR and mutate from @/libs/swr, keeping the application's
workspace/cache handling. Return a null key when a required id is absent; passing
undefined to a store hook only disables fetching if that hook maps it to null.
For example, inside an action class:
useFetchBenchmarks = () =>
useClientDataSWR(evalKeys.benchmarks(), () => agentEvalService.listBenchmarks(), {
onSuccess: (data) => {
this.#set({ benchmarkList: data, benchmarkListInit: true });
},
});
refreshBenchmarks = async () => {
await mutate(evalKeys.benchmarks());
};
Read the current benchmark action for store wiring and the SWR wrappers for their actual options. These links locate implementation; they do not make every existing call site a template to copy unchanged.
Hooks that share a known parent id can run together. If one request needs a value
returned by another, keep its key null until that value exists. Do not assume the
application wrapper uses upstream SWR's default deduplication interval.
For lists cached separately under multiple parents, see parent-keyed lists. Ordinary flat lists need no extra layer.
finally. Use per-id state for
row updates/deletes so unrelated rows remain usable; create can use a separate flag
because no persistent id exists yet.slice.optimistic / entity.optimistic;
a rejected call rolls back on its own, deletes included.finally block clears pending state
but does not by itself recover an optimistic write.Consume the read hook's SWR response. Success-only flags such as isInit, missing
map entries, and data ?? [] cannot distinguish an initial request failure from
loading or empty results.
Use AsyncBoundary for standard loading/error/empty/data surfaces; use AsyncError
for custom layouts and inline failures. Pass the original SWR data to the boundary:
undefined means no successful result, whereas [] is a settled empty result.
Replica useSync returns no data. Feed AsyncBoundary its error and a settled
signal from the store entry (data={isListInit || undefined}, where the selector is
entry !== undefined); see src/features/Pages/PageLayout/Body/index.tsx.
const BenchmarkList = () => {
const useFetchBenchmarks = useEvalStore((s) => s.useFetchBenchmarks);
const benchmarks = useEvalStore((s) => s.benchmarkList);
const { data, error, isLoading, mutate } = useFetchBenchmarks();
return (
<AsyncBoundary
data={data}
empty={<EmptyState />}
error={error}
isEmpty={data?.length === 0}
isLoading={isLoading}
onRetry={() => {
void mutate();
}}
>
<BenchmarkCards items={benchmarks} />
</AsyncBoundary>
);
};
The example's EmptyState and BenchmarkCards stand for the surface's existing
renderers. Follow these distinctions when adapting it:
NotFound / zero-value defaults. Do not put
an error branch after if (!map[id]) return <Skeleton />; failure may never fill the map.loadMoreError and show an inline Retry row.
Suspend observer-triggered retries while that error is unresolved.Move the API call into its service and the request into a store SWR hook. Reuse the
existing state shape and action organization unless they need to change for the task.
Replace the component's effect/local fetch state with that hook and selectors, then
connect error, retry, and pending feedback. Refresh the same cache key after writes.
Check initial failure, retry, settled-empty results, background failure, and switching ids against the surface being changed. For stale data, compare read/refresh keys and the store bucket they update; for stuck loading, inspect rejected requests as well as success callbacks. Do not add a second fetch or another loading flag to mask the cause.
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
실제 제품을 적절한 조작 환경에서 실행해 변경 사항을 검증하고, 사용자가 확인할 수 있는 증거를 모아 인수 보고서로 게시합니다. 기존 검증 계획을 확인하거나 점검 항목을 만들고, `lh` CLI로 결과를 게시할 수 있습니다.
LobeHub에 AI 모델 제공자를 추가하는 과정을 안내합니다. 런타임 연동, 모델 정보와 설정, 브랜드 표시, 문서화 및 인수 확인을 다루며, 기존 제공자에 모델만 추가하는 경우와 새 연동이 필요한 경우를 구분합니다.
서버 환경 변수로 사용자 설정의 기본값을 지정하도록 구현하는 절차를 안내합니다. 변수 검증, 서버 설정 구성, 사용자 저장소 반영부터 예시 환경 파일과 셀프 호스팅 문서 갱신까지 다룹니다.
에이전트 실행 단계, 도구 호출, 사람의 승인, 컨텍스트 압축, 하위 에이전트 호출에 훅을 등록하는 방법을 안내합니다. 이벤트를 관찰하고 로컬 도구 결과를 모의하거나 설정에 따라 웹훅으로 도구 호출을 허용 또는 거부할 수 있습니다.
에이전트의 이벤트 기반 백그라운드 작업을 설계하고 구현하도록 돕습니다. 소스, 시그널, 액션, 정책의 역할을 나누고 비동기 처리, 중복 실행 방지, 완료 기록과 관측 기능을 다룹니다.
실제 데스크톱 채팅 앱에서 봇 채널의 인수 테스트를 진행합니다. Discord, Slack, Telegram, WeChat, Lark, QQ에서 메시지를 보내고 응답을 기다린 뒤 결과 화면을 캡처합니다. iMessage는 BlueBubbles 브리지를 사용합니다.