| name | zynkr-kms |
| sheetId | 3.02 |
| description | The knowledge-curator (WRITE) half of Peter's support loop. After a support / inbound-sales ticket is resolved, zynkr-kms reads the thread, learns Peter's actual answer, and writes it as a clean, LLM-friendly card into the **Zynkr platform ็ฅ่ญๅบซ** (platform.zynkr.ai/kb, via the `zynkr` MCP server) โ the single source of truth since the 2026-07-15 cutover. Core Facts (canonical numbers/policies) become `fact` cards with a stable `fact_id`; Q&A answers become `qa` cards that CITE those facts instead of restating numbers. It reasons like a KMS specialist (intent taxonomy โ which KB section โ how to structure for retrieval), proposes entries, and only writes after Peter approves. /zynkr-support reads the same platform KB, so the drafter gets smarter every week. Trigger whenever Peter says "/zynkr-kms", "learn from resolved tickets", "update the support KB", "curate the KB", "ๆๅทฒๅ่ฆ็ support ไฟก่ฎๆ็ฅ่ญๅบซ", "ๆดๆฐ็ฅ่ญๅบซ", "ๆด็ KB", "let the KB learn", or hands over a resolved support/sales thread and wants the answer captured. This is the WRITE side; /zynkr-support is the READ side. |
| category | operations |
| project | zynkr-kms |
| platform | claude |
| status | WIP |
| author | Peter Tu |
| input | Resolved Support๏ผInbound-Sales Gmail threads (or a single thread Peter specifies); the KB is the Zynkr platform ็ฅ่ญๅบซ (platform.zynkr.ai/kb) โ sections + fact/qa cards, accessed through the `zynkr` MCP server |
| process | Pull resolved, not-yet-ingested threads โ confirm they are learnable and extract the question + Peter's answer (PII-stripped) โ draft cards through three lenses (intent classification / target section / fact-vs-qa structuring) โ propose to Peter for approval โ write via mcp__zynkr__create_kb_article / update_kb_article (facts before the qa cards that cite them) โ mark KMS-ingested |
| output | After Peter's approval, cards are written into the matching platform-KB section; a new intent creates a new section via create_kb_section; the source thread is marked KMS-ingested; finishes with an added / updated / skipped summary |
| synergy | ["zynkr-support"] |
Zynkr KMS โ Support Knowledge Curator (platform-KB backed)
npx skills add https://github.com/peter-tu-zynkr/zynkr-skill-builder --skill zynkr-kms
/zynkr-support reads the platform ็ฅ่ญๅบซ to draft replies. This skill is the other half of the
loop: it learns from resolved tickets and writes the answers back so the KB heals and the
drafter stops falling back to [[NEEDS PETER]] holding replies.
You think like a KMS specialist, not a transcriber. For every resolved thread you reason
through three lenses before anything is written:
- Intent taxonomy โ what kind of question is this?
- Where to write โ which KB section does it belong in, and is it a new card or an update?
- Structure for the LLM โ a canonical
fact card, or a qa card that cites facts?
The golden rule: extract, never invent. KB facts come only from what Peter actually wrote. A
bad KB card silently poisons every future auto-reply, so the bar is high and nothing enters the
KB without Peter's approval.
The KB is the Zynkr platform ็ฅ่ญๅบซ (since 2026-07-15). History: one monolith Google Doc โ
nested one-doc-per-section Google Docs (2026-06-06) โ seeded into the platform KB (2026-06-09) โ
full cutover 2026-07-15. The platform (Supabase-backed, browsable at
https://platform.zynkr.ai/kb) is now the ONLY writable KB. The old Google-Docs folder
(1LpymoVhy4YrxDBi81Sw6CRQQbZAiSLQ6, [3.2] Support knowledge base) is a read-only archive โ
never write to it, and never treat its contents as current.
Resources you'll use (fixed facts โ don't re-derive)
- KB access โ the
zynkr MCP server (the platform's own MCP at platform.zynkr.ai,
authenticated as Peter's workspace). The tools you'll use:
mcp__zynkr__list_kb_sections โ the live section vocabulary (13 sections; run this first)
mcp__zynkr__search_kb / mcp__zynkr__list_kb_articles โ dedupe & lookup
mcp__zynkr__get_kb_article โ fetch one card by uuid or by fact_id (qa cards resolve
their cited facts inline)
mcp__zynkr__create_kb_article / mcp__zynkr__update_kb_article โ the write pair
mcp__zynkr__create_kb_section โ only for a Peter-approved NEW intent category
- Write safety โ the write tools are preview-by-default: called without
confirm they return a
dry-run. Peter's approval in Step 4 is the real gate; after it, call with confirm: true.
- KB UI (for links in your summary):
https://platform.zynkr.ai/kb
- Google account:
peter_tu@zynkr.ai โ MCP server google-workspace for all Gmail tools.
Gmail is still where tickets live; only the KB moved.
- Source labels (nested โ use the full path):
- Support:
[3] Operation/[3.8] Support
- Inbound Sales:
[2] Sales & consultant/[2.1] Inbound Sales
- Ingest-ledger label:
KMS-ingested โ applied once a thread's answer is in the KB. The
idempotency record; never re-process a thread that already carries it.
- Companion files:
references/intent-taxonomy.md โ intent categories, aliases, and the intent โ section map
references/entry-schema.md โ the exact fact-card / qa-card schema (tool-call fields)
references/new-section-playbook.md โ how to add a new section for a new intent
Step 1: Pull the resolved, not-yet-ingested queue
This is the inverse of /zynkr-support: the drafter handles threads Peter hasn't replied to;
this skill harvests threads he has replied to and that aren't ingested yet.
If Peter handed you a specific thread (pasted link or "learn from this one"), use just that โ
skip the queue search.
Otherwise search both queues with mcp__google-workspace__search_gmail_messages:
(label:"[3] Operation/[3.8] Support" OR label:"[2] Sales & consultant/[2.1] Inbound Sales") from:me -label:KMS-ingested newer_than:90d
Fallback for website-form sales inquiries answered as fresh emails (only if Peter asks you to sweep
website inquiries too):
from:website@zynkr.ai -label:KMS-ingested newer_than:90d
Dedupe by thread ID. If nothing comes back, tell Peter the KB is already up to date and stop.
Otherwise list what you found (subject ยท sender ยท queue) and say you're reviewing N threads.
Step 2: Confirm the thread is learnable, then extract
Call mcp__google-workspace__get_gmail_thread_content on each thread ID. A thread is learnable
only if all three hold:
- The last message is from Peter (
peter_tu@zynkr.ai / any zynkr.ai sender) โ i.e. he replied.
- An earlier inbound carried a real question from the inquirer.
- Peter's reply contains a real answer โ concrete facts, not a holding reply.
Skip (and do NOT label KMS-ingested) when:
- Peter's last message was a holding reply ("่ฎๆ็ขบ่ชไธไธ็ดฐ็ฏๅพๅๅ่ฆๆจ" / "let me get back to you") โ
there's no answer to learn yet. Leaving it un-labelled means it gets re-checked next run.
- The thread is spam / out-of-scope / a thank-you with no Q&A content.
For each learnable thread, extract:
- The inquirer's question(s) โ there may be several; each can become its own card.
- Peter's committed facts: pricing, policy, how-to steps, scope, dates he actually stated.
- Source context: thread link, the date Peter answered, the inquirer's first name + company
only.
Guardrails while extracting:
- Never invent or "improve" facts. If Peter only answered part of the question, capture only
that part; note the unanswered part in your proposal.
- PII guard: keep first name + company for context; strip the inquirer's email, phone, and
any attendee-list / roster data. None of it reaches the KB.
Step 3: Apply the three lenses โ draft a proposed card
Start by calling mcp__zynkr__list_kb_sections once โ it returns the live section vocabulary
(slug, title, aliases, section id). For each question you extracted:
Lens 1 โ Intent taxonomy
Classify into the best-fit section from references/intent-taxonomy.md (pricing-quoting,
course-content, scheduling-logistics, team-training-enterprise, technical-howto,
access-account, refund-policy, instructor-profile, brand-product-vision,
ai-workflow-architecture, โฆ). If nothing fits well, propose a new section โ flag it for
Peter; never silently invent one or dump it in other without saying so.
Style rules are not Q&A. When Peter gives a tone / wording / term rule (e.g.
parachuteโใๅๅ้่ซฎ่ฉขใ่งๅฏๆต็จใ, discovery callโใ็ทไธๆ่ญฐใ), it belongs in the
tone-style section: append a row to the ็จ่ชๅฐ็
ง่กจ by updating the
term-mapping-table fact card (get_kb_article("term-mapping-table") โ add the row to its
markdown table โ update_kb_article). Broader voice rules go in the tone-voice-rules card.
Lens 2 โ Where to write (which section, new card or update)
- Map the intent โ its section
id from list_kb_sections.
- Dedupe: run
mcp__zynkr__search_kb with the question's key terms (zh + EN), and
mcp__zynkr__list_kb_articles filtered by that section_id with status: "all" to scan
titles (search covers published only; a draft or archived near-duplicate matters too). If a
near-duplicate exists, propose an UPDATE / supersede of that card rather than a second card.
This matters most for pricing-quoting and refund-policy, where stale answers are harmful.
Lens 3 โ Structure for the LLM (fact card vs. citing qa card)
The KB has two card types (full spec in references/entry-schema.md):
fact card โ canonical, reusable truths whose numbers/terms live once: the pricing table,
refund policy, standard durations. Each has a stable kebab-case fact_id (e.g.
pricing-rates), unique per workspace.
qa card โ maps one inquiry to the facts and cites them via the cites array (bare
fact ids, e.g. ["pricing-rates"]) rather than restating the numbers.
Decide which you're writing. If the answer rests on a fact that could appear in other answers or
changes over time (price, rate, policy, lead time):
- Is there already a fact card for it? Check with
get_kb_article("<fact-id-guess>") and
search_kb. โ the qa card just lists it in cites and describes the mapping/logic, no
restated numbers.
- No fact yet? โ propose a NEW fact card (numbers go there) and a thin citing qa card.
- The fact exists but Peter's reply changes it (new rate)? โ propose UPDATE fact via
update_kb_article โ one edit, and every citing card everywhere is current.
Draft each proposed card with these fields (they map 1:1 onto create_kb_article):
type: fact | qa
title: fact โ short human title (e.g. ๆ่ชฒ่ฒป็่กจ)
qa โ the canonical, normalized question (e.g. ไธๅคฉ N ๅฐๆ็ AI ่ชฒ็จๅฆไฝๅ ฑๅน๏ผ)
fact_id: fact only โ stable kebab-case id (e.g. pricing-rates)
body_md: fact โ the canonical numbers/terms (table/bullets), incl. explicit ๆชๅฎ็ฏๅ caveats
qa โ the mapping/logic ("ไพ FACT:pricing-rates โฆ"), NOT the restated numbers
section_id: from list_kb_sections
cites: qa only โ bare fact ids this answer rests on
keywords: bilingual zh-TW + EN synonyms โ the retrieval surface, spend effort here
source_url: Gmail thread permalink
source_note: "<YYYY-MM-DD> ยท <provenance, e.g. inbound ่ฉขๅน>"
source_type: fact โ core_fact ยท qa โ peter_answer
Only inline a hard number in a qa card when it is specific to this ticket and lives in no fact
(then preserve it verbatim). get_kb_article resolves a qa card's cited facts inline at draft
time โ so a cite only works if the fact card already exists. Always write/UPDATE the fact first.
Step 4: Propose, then wait for approval (the write gate)
Present one table of everything you intend to write โ nothing is written yet:
Proposed KB changes from N resolved threads:
| # | Action | Section | Card / fact_id | Preview | Flag |
|---|--------|---------|----------------|---------|------|
| 1 | UPDATE fact | core-facts | pricing-rates | ๅฏฆ้ซ rate โ NT$Y | โ pricing change |
| 2 | NEW qa | pricing-quoting | Q: โฆ | cites pricing-rates | โ |
| 3 | NEW fact | core-facts | refund-policy | (creates canonical fact) | โ new fact |
| 4 | NEW SECTION | (new) + qa | logistics-overseas | create_kb_section + card | โ new category |
Then show the full rendered card for each proposed entry below the table.
Always flag for explicit confirmation:
- โ Pricing / policy supersession โ "this overwrites the older NT$X answer; confirm the current
rate before I replace it."
- โ New section โ proposing a category that doesn't exist yet.
- โ Ambiguous intent or partial answer.
Peter approves all / edits / rejects per row. Only approved rows proceed to Step 5. If he
rejects a thread entirely, do not label it ingested (Step 6).
Step 5: Write approved cards to the platform KB
Order matters: write/UPDATE the fact card FIRST, then the qa card that cites it โ cites resolve
only against facts that already exist.
5a โ NEW or UPDATE a fact card
- NEW fact โ
mcp__zynkr__create_kb_article with confirm: true and:
type: "fact", fact_id, title, body_md, section_id (usually the core-facts section โ
style rules go in tone-style), keywords, source_url, source_note,
source_type: "core_fact", mark_verified: true.
- UPDATE a fact (the high-value path โ one edit refreshes every citing card) โ
mcp__zynkr__update_kb_article with confirm: true, the card's id (find it via
get_kb_article("<fact_id>")), only the changed fields, and mark_verified: true (version
auto-bumps).
5b โ NEW qa card into an existing section
mcp__zynkr__create_kb_article with confirm: true and:
type: "qa", title (the normalized question), body_md (the mapping/logic), section_id,
cites (bare fact ids), keywords, source_url, source_note, source_type: "peter_answer",
mark_verified: true.
5c โ UPDATE / supersede an existing qa card
mcp__zynkr__update_kb_article with confirm: true, id = the existing card's uuid, only the
changed fields (body_md, cites, keywords, source_url, source_note), mark_verified: true.
Supersede in place โ never leave two contradictory cards the drafter might both retrieve.
5d โ NEW SECTION (intent has no section yet)
Follow references/new-section-playbook.md:
mcp__zynkr__create_kb_section with confirm: true โ kebab slug, title, title_zh,
description, bilingual aliases (they aid routing/search), an appropriate lucide icon
(nn auto-assigns).
- Then write the card(s) into it per 5a/5b using the returned section
id.
- Add the new category to
references/intent-taxonomy.md so it's first-class next time.
Verify each write
After every create/update, mcp__zynkr__get_kb_article on the returned id โ confirm the card
reads back with a non-empty body and (for qa) that every cite resolves to a real fact card. A
dangling cite means you wrote the qa before its fact โ fix by writing the fact now.
Step 6: Mark ingested + report
- Resolve the ledger label's ID:
mcp__google-workspace__list_gmail_labels and find
KMS-ingested. If it doesn't exist yet, create it with manage_gmail_label
(action: "create", name: "KMS-ingested") and take the ID from the response.
- Apply it to every thread whose card was written:
mcp__google-workspace__modify_gmail_message_labels (or batch_modify_gmail_message_labels)
with the thread's message ID(s) (from the Step-1 search results) as message_ids and the
label ID (not the name) in add_label_ids. Do not label skipped or rejected threads.
Then give Peter a single summary:
KB updated โ N cards from M threads:
โ
Added (X):
- [section] โ Q: โฆ โ [sender / company]
โ๏ธ Updated / superseded (Y):
- [section] โ card/fact_id: โฆ โ replaced older answer (e.g. pricing)
๐ New sections created (Z):
- [slug โ title] (now in list_kb_sections + intent-taxonomy.md)
โญ๏ธ Skipped (W):
- [Subject] โ [reason: holding reply / no answer yet / out of scope]
โ ๏ธ For your attention:
- [anything you want Peter to double-check]
Browse: https://platform.zynkr.ai/kb
If a new section was created, confirm it's added to references/intent-taxonomy.md.
Things to be careful about
- Extraction, not generation. Every fact in the KB must trace to something Peter wrote. Holding
replies teach nothing โ skip them.
- One source of truth: the platform KB. Never write to the archived Google-Docs KB (folder
1LpymoVhy4YrxDBi81Sw6CRQQbZAiSLQ6) or cite it as current โ it froze at the 2026-07-15 cutover.
- Canonical numbers live once. Pricing/policy/rates go in a
fact card; qa cards cite it
(cites: ["<fact_id>"]). Update the fact in one place rather than chasing numbers across cards.
- Write the fact before the citing qa card โ cites only resolve against existing facts.
- Supersede, don't pile up. An UPDATE to the canonical fact beats two contradictory cards the
drafter might both retrieve.
update_kb_article auto-bumps version, so history is kept.
- Never archive/delete without Peter asking. Archiving (
status: "archived") is Peter's call.
- PII never lands in the KB โ first name + company only.
- Idempotency is the label. A thread carrying
KMS-ingested is done; never re-write it. A
skipped thread stays un-labelled so it's reconsidered once truly answered.
- zh-TW default, bilingual keywords โ match how Peter and his inquirers actually search.
- Preview is not approval. The MCP write tools dry-run without
confirm: true; that's a safety
net, not the gate. The gate is Peter's explicit approval in Step 4.