Sync the #ai-platform-internal-qa Slack channel (demo.thakicloud.net QA reports) to a local corpus with full threads, filter out completion-tagged (완료/확인_완료) items, and dedup-check a new QA finding before posting it in the team's expert template. Use when "QA 이슈 올리기 전 중복 체크", "내부 QA 슬랙 싱크", "demo qa 중복 확인", "이 버그 이미 올라왔나", "internal-qa 채널", "check if this QA issue is a duplicate", "post QA finding". Do NOT use for GitHub issue dedup (use github-issue-dedupe), monitor/alert spam throttling (use slack-alert-dedup-cooldown), or general Slack posting (use scripts/slack_post_message.py).
Instalação
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Sync the #ai-platform-internal-qa Slack channel (demo.thakicloud.net QA reports) to a local corpus with full threads, filter out completion-tagged (완료/확인_완료) items, and dedup-check a new QA finding before posting it in the team's expert template. Use when "QA 이슈 올리기 전 중복 체크", "내부 QA 슬랙 싱크", "demo qa 중복 확인", "이 버그 이미 올라왔나", "internal-qa 채널", "check if this QA issue is a duplicate", "post QA finding". Do NOT use for GitHub issue dedup (use github-issue-dedupe), monitor/alert spam throttling (use slack-alert-dedup-cooldown), or general Slack posting (use scripts/slack_post_message.py).
QA reporters post demo-app bugs to #ai-platform-internal-qa (C0ABMR6CH42) in a
fixed template (환경: / 이슈: / 담당자:). Before posting a new finding you
must know it isn't already reported. This skill syncs the channel + threads
locally, then dedup-gates new posts.
Format is code-owned (parsing, status enum, dedup scoring, mrkdwn render) per
[[sonnet-format-determinism]] and [[thin-harness-fat-skills]]; the model only
judges borderline dup matches. Slack access is repo-local SLACK_USER_TOKEN
(Path B, [[mcp-graceful-degradation]]) so it runs headless. Posting is a gated
external action (Safety Gates): explicit approval required.
게시 단위 = 관찰된 현상 1개 (근본 원인 1개가 아니다)
리포트 단위는 "관찰한 증상(현상)"이지 "근본 원인"이 아니다. QA는 현상을 보고 올리고
(예: "엔드포인트가 안 뜨고 계속 있다"), 그 시점엔 근본 원인이 대개 미상이다. 하나의 현상 =
하나의 포스트 = 하나의 스레드. 리포터에게 근본 원인별 사전 분해를 강요하지 않는다.
한 현상을 RCA하다 1차 원인 + 2차(부차)·구조적 발견이 같이 나오면, 같은 스레드에
1차 원인 / 2차(부차) 발견으로 명확히 라벨해 함께 둔다. 이건 여전히 이슈 1개다.
별도 포스트로 쪼개는 건 진짜 다른 현상(다른 증상·무관한 재현경로)일 때만이다.
같은 증상의 다른 근본 원인은 쪼개는 기준이 아니다.
부차 발견이 독립 트래킹(별도 완료/확인_완료)이 필요하면, 그건 담당자가 후속
티켓/스레드로 분리하고 원 스레드에 링크한다 — 리포터의 포맷 부담이 아니라 담당자의 triage다.
2026-07-08 경위: 한 현상(35B 엔드포인트가 안 뜸)의 RCA에서 1차(S3 1초 저속-타임아웃) +
2차(resume watcher가 CrashLoop을 fast-fail로 못 내림)가 같이 나왔다. 이 둘을 한 스레드에
담은 건 "한 현상"으로선 맞다. 담당자가 "1이슈:1스레드"를 요청했고 직전 커밋이 이를
"근본원인별로 N개로 쪼개라"로 박았으나 — 그건 과교정이다. 리포트 단위는 현상이고,
부차 이슈는 리포터가 쪼개는 게 아니라 담당자가 후속으로 분리·해결하는 게 맞다. 되돌린다.
(담당자 측 valid point = 완전히 무관한 두 버그가 한 스레드면 per-thread 상태태그가 깨진다 →
그래서 "같은 현상이면 1차/2차 라벨로 한 스레드, 무관한 현상이면 별 스레드"로 절충.)
Status model (empirical, from the channel's pinned 공지)
Dedup checks against all synced issues including done — reposting a
fixed-and-closed issue is still redundant. "작업 중" view excludes done.
Workflow
Sync (do this first, and re-run before any dedup check to stay fresh):
.venv/bin/python .claude/skills/qa-issue-dedup-poster/scripts/qa_slack.py \
sync --since 2026-06-29 --json
# weekly/trend context: add later --since; corpus = state/corpus.jsonl (gitignored)
Prints total / active counts + status breakdown. Fetches every parent + full
thread (resolution replies included).
Dedup-check a candidate (retrieval is code; you make the final call):
.venv/bin/python .claude/skills/qa-issue-dedup-poster/scripts/dedup.py \
--issue "metis hub > model registry 필터 검색 옵션 누락" --json
Score blends token overlap + feature-path (A > B > C) overlap.
score ≥0.5 = likely_dup, 0.3–0.5 = review, <0.3 = unlikely. On a
review verdict, read the matched issue + its thread and decide semantically
(same root cause vs superficially similar).
Duplicate → thread-append, not drop. A dup is not discarded — the new
detail is posted as a reply in the existing issue's thread (recurrence /
deeper RCA / new evidence) so one thread accumulates the full story instead of
the channel fragmenting into near-duplicates:
likely_dup (score ≥0.5) → post.pyauto-appends to the matched thread.
review/unlikely but you judged it the SAME issue (semantic match the
score missed — e.g. a closed issue that regressed) → pass the matched
issue's ts via --reply-to <ts> to append there.
genuinely new → normal top-level post (no --reply-to, not a dup).
want a NEW top-level despite a dup → --force.
Each corpus/dedup match carries a ts (the parent message) — that's the
--reply-to value.
Humanize the body text first (required), THEN enforce 존댓말 (required).
Before rendering, run the issue / location / actual / root-cause prose through
the humanizer skill so the post reads like a person wrote it — natural
sentences, no AI-slop, varied endings ([[report-writing-humanize]]).
⚠️ humanizer outputs 평서체 (plain -다 endings that read as 반말) — it does
NOT do 경어/register. This is a team channel, so after humanizer you MUST
chain korean-tone-reviewer to convert the field text to 존댓말 (합니다체
-습니다; 명령형 개조식 재현 경로는 그대로 허용). Skipping the tone step ships a
반말 post (2026-07-14 사고: 휴머나이저만 돌려 반말로 게시 → 담당자 지적). The
scaffold (labels, bullets, links) stays code-owned; only the field text is
humanized + tone-reviewed. QA must be able to (a) locate the bug and (b)
understand the cause without re-triage, so always fill --location
(화면/계층/URL/코드 위치) and --root-cause (확인된 로그·재현 근거; 추정이면
[추정]). Confirmed-vs-hypothesis must be honest ([[news-freshness-and-sourcing]]).
Render + post (dry-run first; --post is gated). A likely_dupauto-appends to the matched thread; add --reply-to <ts> to append a
lower-scored match you judged identical, or --force to post fresh:
.venv/bin/python .claude/skills/qa-issue-dedup-poster/scripts/post.py \
--app "Metis Serve" --title "<한눈에 파악되는 짧은 제목>" \
--env prod --issue "<humanized one-line issue>" \
--location "<앱>화면 > 위치 · 계층(예: 백엔드 chat.go:157 → KEDA interceptor)" \
--steps "step1; step2; step3" --expected "<expected>" --actual "<actual>" \
--root-cause "<확인된 원인 or [추정]>" \
--domain 백엔드 --domain 클러스터큐 --ref "https://…|화면 설계" \
--screenshot /tmp/repro.png # dry-run# → review draft + dedup, then re-run with --post to send
담당자는 내용의 도메인으로 자동 태그 (--domain, 한/영 별칭 가능): 인프라→박윤재,
기획→김해림, 프론트→신찬우·이민기, 백엔드→조태양, ml→유두열, 클러스터큐(kueue/keda)→야키요.
여러 도메인이면 모두 태그되고, 매핑에 없는 도메인은 조용히 무시하고 경고만 낸다(임의 배정
금지 — [[sra-incorporation-guard]]). 특정인을 직접 넣으려면 --assignee <id> (둘은 합쳐짐).
매핑은 references/assignees.json 한 곳에서 관리 — 담당자/도메인 변경은 그 파일만 고친다.
Rendered layout (code-owned scaffold): a bold at-a-glance 제목🐛 *[{app}] {title}* on top so readers know the app + situation instantly,
then each section with its label bolded (*환경:*, *이슈:*, *위치:*,
*재현 경로:*, *기대/실제 동작:*, *원인(추정):*, *참고:*, *담당자:*) and a
blank line between sections for fast scanning. Always pass --app (which
app) and --title (short headline); if --title is omitted it's derived from
the issue.
긴 필드는 한 문장으로 잇지 말고 테마 불릿으로 (가독성, 2026-07-14 지적). 길거나
구조적인 --actual/--root-cause(예: 1차/2차/별개/검증 원인)는 문장을 이어붙이지 말고
\n으로 • 테마 불릿을 만들어 넘긴다(불릿 안 미니라벨 *1차 원인(확정):* 권장). render는
본문에 \n이 있으면 라벨을 제 줄에 두고 그 아래에 본문을 붙인다(한 줄 필드는 인라인 유지) —
"문장마다 빈 줄"이 아니라 "논리 덩어리마다 불릿"이 정답. steps는 ; 구분이 자동 번호줄이 된다.
Renders the team's expert template (mrkdwn, [[slack-post-formatting]]) and
posts as the user via scripts/slack_post_message.py. Get user approval on
the draft before --post.
Attach a screenshot to the thread (do this whenever a screen is involved).
Pass --screenshot <path> (repeatable) — after the parent posts, post.py
uploads each image to that message's thread via scripts/slack_upload_file.py
(thread_ts, SLACK_USER_TOKEN). Prefer a browser capture of the actual
error screen (demo.thakicloud.net): use agent-browser / carbonyl-browser
when a browser + demo session is available. If the app screen can't be reached
headlessly (no browser binary / no auth), fall back to a capture of the
reproduction evidence (CLI repro, backend error log) and say so — never
fabricate a UI shot. The screenshot rides the thread so the visual sits with
the issue.
Notes / gotchas
Never post without an explicit finding from the user + approval on the draft.
This is a real team channel; the skill only automates the dedup gate + format,
not the decision to post.
Post reads human AND 존댓말, not templated. Body text goes through humanizer
first, then korean-tone-reviewer to force 존댓말 (합니다체) — humanizer alone
ships 반말 (2026-07-14 사고). --location/--root-cause are filled so QA locates +
understands the bug in one read. Screen bugs get a --screenshot attached to the thread (browser capture of
the error screen preferred; CLI/log repro as honest fallback).
The 이슈: line is the dedup key; the > feature path dominates scoring —
keep new issues in the same App > 화면 > 위치 shape for best matching.
게시 단위 = 관찰된 현상 1개(위 절). 한 현상의 RCA에서 나온 1차/2차 원인은 같은
스레드에 라벨해 둔다(근본원인별로 쪼개지 않는다). 진짜 다른 현상만 별 포스트. 부차 이슈의
독립 트래킹이 필요하면 담당자가 후속 티켓으로 분리 — 리포터가 사전 분해할 일 아니다.
assignees are captured by Slack user-id; look up display names via
slack_read_user_profile if you need to name them.
Re-sync is cheap and idempotent (corpus is rebuilt each run). Always sync
before a dedup check so newly-posted issues are in scope.
Corpus holds internal QA content (member ids) → state/ cache is gitignored;
only skill code is committed ([[token-diet-hygiene]]).