agentleFS
nicojunknicojunk/claude-inbox/skill/SKILL.md

support-inbox

Skill0 starsChanged 4 months agoMITView on GitHub

Orchestrated multi-agent email support workflow for the Acme support inbox (support@example.com): triages every mail, validates against the payment provider and the community platform, writes replies in the Founder's voice, and sends them autonomously. This skill should be used whenever the request involves: gmail workflow, email, support, inbox, process inbox, work through the inbox, refund, withdrawal, cancellation, custom plan, validate a PDF, send a PDF, drafts, community access, login problem, access, instalments, coaching cancellation, double charge, payment issue, or working through customer support emails. It should also be used when a customer email needs an answer even if the word "workflow" is never said. This skill should NOT be used for social media comments or DMs, for community posts and comments that involve no email, or for outbound marketing and newsletters.

  • Reads credentials

2 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions2 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 2

    1. itsryanarmani/skills★ 1same name
    2. pooriaarab/skills★ 1same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

The file

Show its rules and text

We split this skill into the rules it gives an agent

31 rules. About 0 are common, 19 are distinctive, and 12 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈19

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 12

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If a subagent spawn fails: never give up, the orchestrator runs the phase ITSELF (MCP tools and the MAIL_CLI CLI are always available in …

    line 68
  2. Distinctive:

    Refund requests: auto-send the reframe: No refund without payment verification.

    line 77
  3. Distinctive:

    Health questions: auto-send a short acknowledgement: No medical advice. Where medication is involved, recommend a doctor.

    line 83
  4. Distinctive:

    No unfounded assumptions: Do not confirm a customer's claim without checking the data.

    line 89
  5. Distinctive:

    Offer nothing that cannot be executed immediately: No callback, no appointment.

    line 90
  6. Distinctive:

    EXCEPTION, an escalating thread: if several new messages from the SAME sender exist in the SAME thread and they show escalation (a new …

    line 133
  7. Distinctive:

    Membership access questions (mandatory): before saying "add the membership", ALWAYS check member_tags — many customers already have it.

    line 160
  8. Very specific:

    NOT the training access of the signature program (a one-off purchase.

    line 166
  9. Very specific:

    TCC WARNING: never use ~/Desktop/.env in an automated run.

    line 258
  10. Very specific:

    When: if the persistent marker ~/.local/state/support-inbox/payer-sweep-last.txt is older than 7 days (or missing), the sweep runs BEFORE …

    line 276
  11. Distinctive:

    Write the marker file with today's date.

    line 294
  12. Very specific:

    1a. Structural validation: every plan entry needs thread_id, message_id, email_id and category.

    line 310
  13. Distinctive:

    No payment record? Do not blindly write "payment confirmed".

    line 379
  14. Very specific:

    cohort_week plausibility: if member_tags contains a Signature Program Week N tag but cohort_week is null, the validator did not extract …

    line 385
  15. Very specific:

    Review the attention flags: if the customerEmail!=memberEmail flag is set, the triager must already have assigned the access category.

    line 387
  16. Distinctive:

    MUST take this into account.

    line 440
  17. Distinctive:

    Do NOT write "it is being prepared".

    line 455
  18. Distinctive:

    Instead: "Your plan should already be with you.

    line 456
  19. Distinctive:

    If the customer asks again after delivery and in the same thread: address ONLY the new question, do not repeat the status.

    line 458
  20. Distinctive:

    Keep the timing vague: "it takes a few days" rather than "24-72h" (it sounds more human).

    line 464
  21. Distinctive:

    EVERY mail that is not purely support, cancellation, legal or emotional, the need × purchase status matrix from that file applies: read …

    line 478
  22. Very specific:

    When in doubt use False (= the launch paragraph is included).

    line 504
  23. Distinctive:

    3a. Completeness check: every plan entry needs exactly one reply, and no reply may exist without a plan entry.

    line 509
  24. Distinctive:

    Every code that appears in a reply has to pass the checkout gate first.

    line 548
  25. Very specific:

    If a reply says "it is unlocked / switched": is the action documented in the community_action or stripe_action field?

    line 565
  26. Very specific:

    If the reply answers an access mail: check the validation result to confirm access really exists.

    line 567
  27. Distinctive:

    If the reply names a price: does it match the payment provider's data?

    line 569
  28. Very specific:

    4a. Send check: run scripts/validate_4a_execution.py.

    line 588
  29. Distinctive:

    If a community_action ran: check the result in the execution result.

    line 606
  30. Distinctive:

    If open, unanswered mail remains:

    line 625
  31. Very specific:

    If an entry is missing or passed=false: mark the session as faulty.

    line 648

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Gmail workflow (orchestrator)

Session start (load order): 1. references/RULES-DIGEST.md (mandatory, the compact rules). 2. This file. 3. As needed: ref-distilled-rules.md, ref-writing-style.md (phase 3), ref-entscheidungsbaum.md (phase 1), ref-product-catalog.md (sales case).

TEMPLATE NOTE. This file is the orchestrator prompt. Everything in it that names a product, a price, a label ID or a persona is an example and must be replaced before first use. See CONFIGURATION.md in the repository root for the full list of replacement points. Placeholder values are written in SCREAMING_SNAKE_CASE (e.g. ID_PLACEHOLDER) so they are greppable.

Autonomy principle

ADOPTER DECISION. The two stances below are business policy, not architecture, and they are deliberately aggressive: full autonomy with no human review, and revenue as the deciding tiebreak. They worked for the business this came from. Decide consciously whether they are yours. Common alternatives: draft-only mode until you trust the output; escalating refunds and legal mail to a human; a customer-outcome tiebreak instead of a revenue one. Nothing in the gates or the architecture depends on this section.

This workflow runs FULLY AUTONOMOUSLY. No drafts, no escalation. Every mail is answered and sent directly.

Profitability is the tiebreak when two replies are equally correct on the facts. It does NOT override the boundaries further down: on cancellations, withdrawals, legal questions, health topics and strong emotions, nothing is sold.

Single exception: when a chargeback or dispute is imminent, give way rather than take the risk.

Gmail setup

Server: support-inbox via the MAIL_CLI CLI (support@example.com). NEVER gmail-other-account.

Labels: Support = Label_PLACEHOLDER, Cancellation = Label_PLACEHOLDER, For Tech/review = Label_PLACEHOLDER.

HARD RULE: always pin the MAIL_CLI subject. MAIL_CLI follows the global profile in ~/.config/mail-cli/profiles/.current, which any other session can switch at any time. Observed mid-run: it flipped silently to personal, and every known message ID suddenly returned 404 because the wrong mailbox was being read. Had that happened before the send phase, customer replies would have gone out from personal@example.com. So on EVERY use of MAIL_CLI, set export MAIL_CLI_SUBJECT=support@example.com. In addition, run MAIL_CLI gmail users getProfile before the send phase and verify that emailAddress == support@example.com. On unexplained 404s for known message IDs: check getProfile FIRST, do not conclude "the mail was deleted".

Subagent fallback

If a subagent spawn fails: never give up, the orchestrator runs the phase ITSELF (MCP tools and the MAIL_CLI CLI are always available in the orchestrator context). Details: references/ref-subagent-fallback.md.

Hard business rules

  1. Max 1 mail per customer per day.
  2. Never cancel coaching instalments (the configured instalment amounts). Send a reframe, emphasise the value.
  3. Refund requests: auto-send the reframe. No refund without payment verification.
  4. Profitability as the tiebreak (see the note under "Autonomy principle"): given two replies that are equally correct on the facts, pick the commercially better one. Never at the cost of a correct answer.
  5. Chargeback avoidance: under a dispute threat, solve the problem if possible, otherwise refund.
  6. Health questions: auto-send a short acknowledgement. No medical advice. Where medication is involved, recommend a doctor.
  7. Only promise what is executed IMMEDIATELY. No "within 24 hours", no "I will call you".
  8. Grant community platform access yourself once the payment is confirmed.
  9. No internal system names in mails (the payment provider, the database, the automation platform, the community platform API, the CRM).
  10. No unfounded assumptions. Do not confirm a customer's claim without checking the data.
  11. Offer nothing that cannot be executed immediately. No callback, no appointment.
  12. System notifications (payment provider, no-reply) are archived only. Do not reply.
  13. Never apologise. No "I'm sorry", no "apologies". Just deliver the solution.
  14. SALES LAYER: need × purchase status → ONE product (mandatory). The autoresponder sells to the actual need. On EVERY mail (except pure support, cancellation, legal or emotional cases), first check the purchase status (payment provider plus the community platform's member_tags) AND read from the text what the person actually needs, then pick exactly ONE fitting product. The full matrix, sample mails and verified purchase links belong in references/ref-product-catalog.md (load in phase 3, mandatory). The short ladder below is an EXAMPLE and must be rewritten for your own range:
    • Cold, only wants a tip → the free lead magnet, no pitch.
    • Wants to train live / asks about live offers → live program (entry price; if there is no recording, say so honestly).
    • Interested, the signature program does not fit (yet) → live program.
    • Wants a single bounded result, not a programme → custom plan (be honest about the scope).
    • Warm, genuinely interested, or asks about the main offer directly → signature program (sales psychology, launch paragraph, PDF where available).
    • Wants the main offer but hesitates on price or commitment → try to hold her first, otherwise the live program as an entry point.
    • Has the starter program or a custom plan → signature program.
    • A signature program graduate without a membership → membership (the standard graduate rate, see the catalogue).
    • In the membership OR in a running signature program → support only, no pitch.
    • What counts: the product that fits best AND yields the most profit. The tone is never salesy, self-assured rather than pleading. Technical and access issues (the Tech route) are ALWAYS solved regardless of purchase status. Health questions stay off limits (Q&A or a doctor). On support, cancellation, legal or strongly emotional mail, nothing is sold (boundaries in the reference and in rule 15).
  15. SIGNATURE ROUTING (3 tracks). Determine the track first, then apply purchase-status gating:
    • Tech (signature "LG Tech", Tech voice): technology plus navigation and usage (app, login, access, a PDF that will not open, "where do I find X", TV/casting). Tech solves technical problems ITSELF. Do not invent features. See ref-writing-style.md.
    • Founder (signature "Herzliche Grüße Founder"): emotionally open mail, plus mail that explicitly asks for the Founder. See ref-writing-style.md.
    • Persona (Maria/Carla/Yvonne/Sarah, derived from thread_id): everything else.

Duplicate protection

The triager agent checks automatically against sent_24h. If the address appears in sent_24h: category duplicate, archive only.

EXCEPTION, an escalating thread: if several new messages from the SAME sender exist in the SAME thread and they show escalation (a new problem, a repeated request, frustration, a wish to cancel), it is NOT a duplicate. The triager must take the LAST message in the thread as a plan entry, carrying the current request. Typical pattern: a customer writes 3-5 times because the problem is unresolved.

Product types

EXAMPLE range. The prices are placeholders. The value of this table is in the types, not the numbers: the type decides whether and how something may be cancelled. Enter your own products in references/ref-product-catalog.md and align the amounts in scripts/validate_2a_validation.py (INSTALLMENT_AMOUNTS).

TypeProductPrice (placeholder)Cancellable?
AStarter Program29 EUR one-offNo (withdrawal period expired)
ACustom Plan99 EUR one-offNo (withdrawal period expired)
ASignature Program499 EUR one-off (instalments: see type B)No (withdrawal period expired)
BSignature Program instalments99/199/299 EUR per monthNEVER (instalment plan, not a subscription)
CMembership, monthly79 EUR per monthYes, at the end of the term
CMembership, annual799 EUR per yearReframe, then at the end of the term
DLive Program19 EUR per monthYes, the autoresponder executes it itself

Sales prices and purchase links ALWAYS come from references/ref-product-catalog.md (maintain them there, verified), never from memory. If the live program has no recording, that belongs in every reply about it; access runs through access group ID_PLACEHOLDER.

Membership access questions (mandatory): before saying "add the membership", ALWAYS check member_tags — many customers already have it. On "I have no access any more", do not only check status=active: also look for canceled subscriptions whose current_period_end lies in the future and whose payment succeeded and was not refunded. A prepaid annual subscription is a paying customer; restore access IMMEDIATELY. Only ask for the purchase email when no paid subscription can be found under ANY email or name. Membership access is NOT the training access of the signature program (a one-off purchase; never touch space group SPACE_GROUP_ID_PLACEHOLDER through the API).

🔴 HARD RULE — never strip the membership access group from an ACTIVE cohort (real incident): when the membership is bundled into a running programme, active participants hold the access group WITHOUT a subscription of their own. A blanket removal ("no free access without a subscription") applies EXCLUSIVELY to graduates, never to active participants. Mandatory before any removal: check member_tags; an active week tag means do NOT remove. Reason: the course content lives in the membership space, so a removal deletes exactly the content an active participant was promised permanently. The mistake cost one participant two days of access in the middle of the programme.

Writing style

Moved to references/ref-writing-style.md. Load in phase 3 (Respond), mandatory, before the first reply is written. It covers tone, forms of address, signature routing details, forbidden phrasings and length constraints.

Decision tree

Moved to references/ref-entscheidungsbaum.md. Load in phase 1 (Triage). It covers: system notification, chargeback/dispute, legal threat, cancellation and refund by product type, health question, access/login, custom plan draft with PDF, custom plan FAQ, positive feedback with and without a question, unclear cases.

Distilled rules

Moved to references/ref-distilled-rules.md (actions first, promo code verification, the bulk_send requirement, proof of delivery, the send traps, email mismatch, umlaut protection, community platform gotchas, typical subagent mistakes).

Load as soon as mail is actually being answered. The compact rule lines live in references/RULES-DIGEST.md and are mandatory on EVERY run.

Orchestration workflow

ALWAYS in this order (sequentially). NO phase starts before the previous phase's validation has passed.


Phase 0: context pre-run (mandatory, before every run)

Why: the workflow must NEVER answer blind with the same standard reply. Before any processing, capture what has changed since the last run and whether one topic is currently dominating the inbox or the community. Those signals steer THIS run's replies. (Lesson from runs 15-18: a stale live price, a membership bundling mistake and waves of email mismatches would all have been caught earlier by a context pre-run.)

What (two steps; the second one is a GATE with an exit code):

set -a; source ~/.config/acme/.env; set +a   # NOT ~/Desktop/.env (TCC, see below)
python3 ~/.claude/skills/support-inbox/scripts/context_snapshot.py
python3 ~/.claude/skills/support-inbox/scripts/validate_0_context.py; echo "GATE0=$?"

Step 1 — context_snapshot.py (read-only) captures the current state from the sources and compares it against the last acknowledged state. It holds NO prices or IDs in code. It records:

SourceWhat follows from it
Payment provider products, prices, payment linksa new offer, a changed price, a discontinued product
Community platform spaces (all 33, not 3 fixed ones)a new course, a new space
Community platform member tags with member countsa new cohort, a growing cohort (dynamic, no hardcoded tag ID)
Gmail SENT (3 days)your own outbound mail, which triggers follow-up questions
Gmail INBOX (2 days)a hot topic, one subject piling up
Landing pages (content hash)a changed offer on the page
Payment provider disputeschargeback priority

Step 2 — validate_0_context.py is the gate:

ExitMeaningConsequence
0clearcarry on normally
1partial holdput mails about prices, products, offers or vouchers aside. Login, technical, health and feedback mail continues normally.
2full holdthe scan is unusable (missing keys, a dead source). Do not reply at all.

Why a gate and not an instruction: our own eval of 10 July 2026 measured an agent violating the umlaut rule even though it had read that rule in full. Verbatim in evals/evals.json: "Proof that a rule in the prompt is no substitute for a gate." Phase 0 was prose until 24 July 2026 and therefore had no effect.

Acknowledge after reviewing the reference:

python3 ~/.claude/skills/support-inbox/scripts/context_snapshot.py --acknowledge

The snapshot deliberately does NOT advance automatically. Otherwise an aborted run silently swallows the change.

TCC WARNING: never use ~/Desktop/.env in an automated run. macOS denies launchd access to Desktop, Documents and Downloads, and it does so SILENTLY (source fails while the exit code stays 0). That is exactly how 18 of 18 runs ran without a payment provider key on 20-21 July 2026, each reporting three invented price-drift signals.

The scan does NOT replace per-case validation (phase 2); it is the situational overview that comes before it.


Phase 0.8: payer-without-access sweep (weekly)

Why: fulfilment attaches community platform access to the PURCHASE email. When an existing customer buys with a wallet or secondary email (PayPal, Klarna, Apple Pay), either a duplicate account appears or access is silently missing. Root-cause documentation: (your automation backups directory) (the fulfilment patch is awaiting approval). Until the watchdog automation is live, this sweep catches every silent case.

When: if the persistent marker ~/.local/state/support-inbox/payer-sweep-last.txt is older than 7 days (or missing), the sweep runs BEFORE phase 1. Otherwise skip it.

What:

  1. Paginate all active and past_due subscriptions (STRIPE_API_KEY, expand data.customer): membership subscriptions and live program subscriptions (amounts from your own catalogue).
  2. Per payer: look up the community platform account under the purchase email, check access groups. Target: membership = AG_MEMBERSHIP, live = AG_LIVE.
  3. MISSING_AG (account present, access group missing): assign the group immediately plus a readback (the established pattern). No customer mail needed, heal it silently.
  4. NO_COMMUNITY_ACCOUNT (no account under the purchase email): do NOT create one automatically and do NOT bend it onto a name match — the risk of confusing two people is real. Put the case in the run report; if the customer is in the plan anyway, solve it there, otherwise hand it to Tech as an open item.
  5. Suspected email typo (a broken TLD such as .FE): correct the customer's email at the payment provider when the right address is beyond doubt (for example the same customer exists under the correct address). Precedent: D. Example, 16 July 2026.
  6. Write the marker file with today's date. Put the result (numbers plus fixed cases) into the session log.

Phase 1: triage

Agent(subagent_type="support-triager")

Fetches the inbox, categorises every mail, builds the plan. Output: /tmp/support-triage-result.json

Validation 1 (orchestrator, mandatory before phase 2)

1a. Structural validation: every plan entry needs thread_id, message_id, email_id and category. Without thread_id and message_id the executor cannot attach the reply to the existing thread, and the customer receives a mail with no context.

python3 ~/.claude/skills/support-inbox/scripts/validate_1a_triage.py
  • FAIL → restart the triager or fix it manually. Do NOT continue.

1b. Content check (the orchestrator reads the JSON):

  • Any escalations? The orchestrator decides case by case (chargeback, TYPE_B, legal).
  • Is duplicate detection plausible? (sent_24h_recipients should not be empty if mail went out yesterday.)
  • Are the categories plausible? (Access cases need validation, FAQ does not.)
  • Feedback check (mandatory): for every archive_id, check whether the mail is positive customer feedback (thanks, praise, a success, enthusiasm). If so: remove it from archive_ids and add it to the plan as a feedback entry. Feedback WITHOUT a question gets a warm reply; it is not an archive case.

Phase 2: validate

Agent(subagent_type="support-validator")

Bulk payment validation for all access / refund / cancellation / complex / health mails. Community platform MCP lookups for ALL plan entries (including faq and feedback) — they are cheap and yield member tags, cohort week and attention flags, which make authentic replies possible without surveillance. Output: /tmp/support-validation-result.json

Community platform context fields per validation entry:

  • community_first_name — the form of address from the community platform (more reliable than the payment provider's name field)
  • member_tags[] — product reality; rules out confusing monthly with annual membership
  • cohort_week — e.g. "week 3" or "completed". May ONLY be used inside signature program reframes, as a fact about the programme
  • is_x0_completed — the course is finished, so a different reframing logic applies
  • attention_flags[] — customerEmail!=memberEmail, !!NEEDS_ATTENTION!!, Goldstatus as internal triage hints
  • member_since — INTERNAL, never mentioned in replies

HARD RULE: community data is a triage signal, not content. The responder must NOT write "I saw you have been with us for X months", "you are so active with your 83 posts", "nice to see you back". That is surveillance, not service. Exactly three hooks are permitted: member_tags (a silent product check), cohort_week (only as a fact about the programme, not as an observation), attention_flags (a triage shortcut, never in the body).

Validation 2 (orchestrator, mandatory before phase 3)

2a. Structural validation: checks that every access / refund / cancellation entry was validated, and catches the most common validator misclassification: a coaching instalment (one of the configured instalment amounts) labelled TYPE_C instead of TYPE_B. That confusion would make an instalment plan cancellable, which it never is.

python3 ~/.claude/skills/support-inbox/scripts/validate_2a_validation.py
  • FAIL → rerun the validator for the missing mails. Do NOT continue.

2b. Content check (orchestrator):

  • Email mismatches? The orchestrator decides whether the alternative address is plausible.
  • No payment record? Do not blindly write "payment confirmed".
  • TYPE_B misclassified as TYPE_C? Check the price against INSTALLMENT_AMOUNTS in scripts/validate_2a_validation.py; a match means TYPE_B.
  • Community platform MCP coverage: at least 80% of plan entries should have community.found=true. Substantially less means either the MCP tool or a wave of email mismatches. Rerun the validator or check manually.
  • cohort_week plausibility: if member_tags contains a Signature Program Week N tag but cohort_week is null, the validator did not extract it correctly. Fix before phase 3.
  • Review the attention flags: if the customerEmail!=memberEmail flag is set, the triager must already have assigned the access category. If not, correct the category.

Phase 2.5: custom plan validation (only when custom_plan_drafts exist)

# Run only if the triage result contains custom_plan_drafts and the array is not empty
Agent(subagent_type="support-plan-validator")

Output: /tmp/support-custom_plan-validation.json

Validation 2.5

  • passed=true: the drafts go to the executor for sending.
  • passed=false: the draft stays where it is. Inform the user. Do NOT send.
  • Empty array or absent: skip the phase entirely.

Phase 2.7: custom plan delivery check (mandatory for EVERY mail mentioning a custom plan)

SCOPE: this check applies to ALL plan entries where:

  • category is "custom_plan" or "faq"
  • the body or subject contains: questionnaire, plan, custom plan, "arrived", "created", "when is it coming"
  • template_hint is "custom_plan_status" (the old trigger, kept for compatibility)

WHY SO BROAD: on 13 March 2026, 6 of 6 customers were told "it is being prepared" AFTER the PDF had been delivered. The old check only fired on template_hint: "custom_plan_status", and the triager did not set that hint reliably. So: EVERY mail relating to a custom plan is checked.

# For EVERY custom-plan-relevant plan entry: check the send history
# Search BROADLY: "your plan is here" OR any mail with an attachment to this address
MAIL_CLI gmail users messages list --params '{"userId":"me","q":"to:CUSTOMER_EMAIL in:sent (subject:(your plan is here) OR has:attachment)","maxResults":5}' --format json

For each result, check whether it was a custom plan PDF:

MAIL_CLI gmail users messages get --params '{"userId":"me","id":"MSG_ID","format":"full"}' --format json
# Check: does the mail have a PDF attachment AND was it sent after the purchase date?

Write the result back into triage-result.json:

Set a delivery_status field on every custom-plan-relevant plan entry:

  • "delivered": a PDF attachment to this address was found. Store the date too → the responder MUST take this into account.
  • "not_delivered": no delivery mail found. The plan is still in production.
  • "draft_pending": a custom plan draft exists in custom_plan_drafts for this customer (it will be sent in this session).
python3 ~/.claude/skills/support-inbox/scripts/check_custom_plan_delivery.py

Exit 1 means: a mail relates to a custom plan but has no delivery_status. Check the sent folder first, otherwise the responder writes "your plan is being prepared" to someone who has had it for weeks.

Responder rule when delivery_status=delivered:

  • Do NOT write "it is being prepared".
  • Instead: "Your plan should already be with you. I sent it on [DATE]. Please have another look in your spam folder."
  • If the customer asks again after delivery and in the same thread: address ONLY the new question, do not repeat the status.

Responder rule when delivery_status=not_delivered:

  • "Your plan is being prepared" is correct.
  • Keep the timing vague: "it takes a few days" rather than "24-72h" (it sounds more human).

Phase 3: respond

Agent(subagent_type="support-responder")

Writes all HTML mail bodies in the Founder's voice. Output: /tmp/support-responses.json

Mandatory before writing (the sales layer): load references/ref-product-catalog.md. For EVERY mail that is not purely support, cancellation, legal or emotional, the need × purchase status matrix from that file applies: read from the text what the person needs, check the purchase status, then pick exactly ONE product (lead magnet / live program / custom plan / coaching / membership) or decide "support only, no pitch". Prices and purchase links come ONLY from the reference file (verified), never invented. Always state that the live program has no recording.

Determine the cohort status (mandatory before phase 3, otherwise the responder does not know whether the launch paragraph applies). The responder is a subagent and cannot check this itself. The orchestrator determines it and writes cohort_active into the validation result:

# Is a cohort currently running (actively sold OR being coached live)?
# Signals: current "Signature Program Week N" tags in the community platform, live sessions in
# progress, fresh signature program purchases at the payment provider.
python3 -c "
import json
v = json.load(open('/tmp/support-validation-result.json'))
# cohort_active = True  -> OMIT the launch paragraph
# cohort_active = False -> the launch paragraph is MANDATORY on coaching pitches
v['cohort_active'] = False   # <- the orchestrator sets the determined value. Default False (no launch running).
json.dump(v, open('/tmp/support-validation-result.json','w'), ensure_ascii=False)
print('cohort_active =', v['cohort_active'])
"

When in doubt use False (= the launch paragraph is included). That is the safe side: scarcity rather than a promise to wait, and a purchase starts immediately anyway.

Validation 3 (orchestrator, mandatory before phase 4)

3a. Completeness check: every plan entry needs exactly one reply, and no reply may exist without a plan entry. A missing reply means a customer is left unanswered.

python3 ~/.claude/skills/support-inbox/scripts/validate_3a_completeness.py

3b. Quality check (the orchestrator reads EVERY reply):

Checklist per mail (ALL must pass):

#CheckHow to check
1Forbidden phrasesSearch for apologies, "I understand you", "no problem", "you're welcome"
2Em dashesSearch for Unicode U+2014 (—)
3Diacritics correctNo ae, oe, ue, ss where that is not the actual spelling
4Length 3-5 sentencesCount <p> tags (excluding the signature)
5Signature and personaThe greeting plus one of the 4 personas (Maria/Carla/Yvonne/Sarah) plus the support-team line plus the brand. No standing AI disclosure. NEVER the signature "Founder" alone, and never the bare brand name customer-facing.
6No empty promisesNo "within 24 hours", "I will call", "I will get back to you", "coming right up"
7No system namesNo payment provider, community platform, database, API, automation platform, CRM or "pipeline"
8No unproven claims"Payment received" only when the validator confirmed it
9Thread data presentemail_id must match a plan entry (for the thread_id/message_id lookup)
10Actions are executableIf the mail says "it is switched / unlocked": the action must happen BEFORE sending
11No surveillance phrasesSearch for "nice to see you back", "haven't heard from you in a while", "you're so active", "your X posts", "with us for X months", "level", "points", "gold status" — none of this data may appear in the body

Points 1-11 are encoded as word lists in scripts/validate_3b_quality.py (forbidden phrases, system names, empty promises, surveillance phrases, broken diacritic stems, the three valid signature forms). The Sonnet responder produces these errors reliably enough that a deterministic check beats a visual inspection:

python3 ~/.claude/skills/support-inbox/scripts/validate_3b_quality.py
  • FAIL → the orchestrator fixes the offending replies directly in the JSON file. Do NOT continue until all pass.

3b-2. Promo code gate (mandatory when a reply contains a voucher code):

Every code that appears in a reply has to pass the checkout gate first. A code that does not redeem for the customer is worse than no reply at all: she tries it, fails and writes again (this happened for real, three times with the same customer).

python3 ~/.claude/skills/support-inbox/scripts/validate_promo_checkout.py $(python3 -c "
import json,re
c=set()
for r in json.load(open('/tmp/support-responses.json')):
    c|=set(re.findall(r'\b[A-Z][A-Z0-9]*-CP-\d{4}\b|\b[A-Z0-9]{8}\b', r.get('html','')))
print(' '.join(sorted(c)))")

Exit 1 = do NOT send the affected replies; recreate the codes without customer.

3c. Action consistency (orchestrator):

  • If a reply says "it is unlocked / switched": is the action documented in the community_action or stripe_action field?
  • If the reply answers an access mail: check the validation result to confirm access really exists.
  • If the reply names a price: does it match the payment provider's data?

Phase 4: execute

Agent(subagent_type="support-executor")

Performs the community platform actions FIRST, then sends the mails (with thread headers), then archives in batch. Executor steps: 0.5 (send custom plan PDFs), 0.6 (CRM tag ID_PLACEHOLDER via the automation webhook for sent custom plans), 1 (community platform / payment provider), 2 (mails), 4 (archive), 5 (delete drafts). Output: /tmp/support-execution-result.json

Validation 4 (orchestrator, mandatory)

4a. Send check: run scripts/validate_4a_execution.py. It verifies that every written reply actually went out and that community platform and CRM actions did not fail silently. Exit 1 = sending is incomplete, so do not report the run as done.

python3 ~/.claude/skills/support-inbox/scripts/validate_4a_execution.py

4b. Thread validation (spot check):

# Check that the last sent mail landed in the right thread
MAIL_CLI gmail users messages list --params '{"userId":"me","q":"in:sent newer_than:1h","maxResults":3}' --format json
# For each: threadId must match the original mail

4c. Community platform action validation:

  • If a community_action ran: check the result in the execution result.
  • Failed community platform actions: the reply may have been wrong ("it is unlocked" when it is not). Inform the user.

Phase 5: post-send verification

⚠️ The INBOX label is UNRELIABLE on this account (many received mails carry no INBOX label). in:inbox massively understates the real inbox. Do not treat "in:inbox = 0" as "everything is done". For an honest count, check the open (unread, unanswered) mail of the last two days.

# The literal INBOX label (catches only PART of it, mostly escalations)
MAIL_CLI gmail users messages list --params '{"q":"in:inbox","maxResults":10}' --format json
# Open received mail regardless of label (the real backlog)
MAIL_CLI gmail users messages list --params '{"q":"newer_than:2d -in:sent -in:chats -in:trash is:unread","maxResults":100}' --format json

If open, unanswered mail remains:

  • Identify what is left.
  • Did it arrive during the workflow run? → next session.
  • Was it forgotten? → archive or answer it manually.
# Spot check: verify the thread correctness of the last 3 sent mails
MAIL_CLI gmail users messages list --params '{"userId":"me","q":"in:sent newer_than:1h","maxResults":3}' --format json
# For each message: read the threadId and reconcile it against the triage result

Phase 6: log

6a. Check the audit trail (mandatory):

cat /tmp/support-validation-audit.json
# Must contain entries for phases 1a, 2a, 3a, 3b and 4a, all with passed=true

If an entry is missing or passed=false: mark the session as faulty.

6b. Update the learnings log: /references/ref-learnings-log.md Append a session line: date, number of mails, sent, errors, tool calls, validation result.


Validation summary

PhaseValidationBlocks the next phase?
1 → 2Structure (thread_id, message_id) plus content (escalations, categories)YES
2 → 2.7All access/refund/cancellation entries validated, no TYPE_B/C confusionYES
2.7 → 3All custom_plan_status plan entries have a delivery_statusYES
3 → 4Completeness plus the quality checklist plus action consistencyYES
4 → 5Send check plus thread spot check plus community platform actionsYES
5 → 6Inbox empty, threads correctYES

Principle: no agent starts before the previous one has been validated. Every validation has an automated part AND a content part.

.env variable names

The correct variable names (do NOT guess):

  • STRIPE_API_KEY (not STRIPE_SECRET_KEY)
  • COMMUNITY_API_KEY
  • SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY (NOT SUPABASE_KEY)

When unsure: grep '^VARIABLE_NAME' ~/.config/acme/.env | head -1

Reference files

FileLoad when
references/ref-learnings-log.mdalways at session start
references/ref-product-catalog.mdalways in phase 3 (Respond) — the sales layer: need × purchase status → product, sample mails, prices, purchase links, the launch rule, the live program
references/ref-categories.mdFAQ, access and technical questions
references/ref-refund-sop.mdevery refund or cancellation request
references/ref-promo-codes.mdvoucher requests, template bug recovery
references/ref-mail-cli-patterns.mdMAIL_CLI syntax questions
references/ref-community-platform.mdcommunity platform API access and tags
references/ref-community-platform.mdcommunity platform sidebar structure, course navigation, common questions after a restructuring
references/ref-stripe-patterns.mdpayment provider SQL, bulk queries

Scripts

The validators for phases 1a, 2a, 2.7, 3a, 3b and 4a live as executable scripts in scripts/. They are invoked with python3 ~/.claude/skills/support-inbox/scripts/<name>.py, not read. exit 1 means blocked: fix first, then continue. What they check, and which two false positives are already fixed there, is documented in scripts/README.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.