Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions skills/inbox-triage/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
name: inbox-triage
version: 0.1.0
description: Classify a bounded inbox thread, route it to the right queue, draft a safe reply when possible, and stop before any send action.
source:
type: cli-tool
command: node
args:
- run.mjs
links:
source: https://github.com/LubuSeb/runx/tree/lubu/inbox-triage-34/skills/inbox-triage
runx:
category: ops
input_resolution:
required:
- inbox_packet
- operator_policy
---

# Inbox Triage

Classify one bounded inbox thread, decide the safest queue, draft a reply only
when the supplied context is enough, and return a send proposal that requires a
separate approval gate. The skill reads fixture-style inbox packets and never
connects to a mailbox, sends mail, mutates tickets, or accesses private account
state.

## When To Use

Use this skill when an operator or agent has an already-bounded inbox packet and
needs a safe first-pass decision:

- classify the message intent and urgency;
- route the thread to a named queue;
- prepare a reply draft for low-risk informational messages;
- preserve the evidence and citations used for that decision;
- hand off any send through `send-as` or another governed sender.

## When Not To Use

Do not use this skill as a mailbox connector, live sending tool, account
recovery authority, billing operator, abuse moderator, or customer identity
verifier. Do not pass raw mailbox exports, unrelated threads, credentials,
private account records, or broad contact lists. If the request needs private
state, identity proof, payment action, legal review, abuse handling, or an
unapproved send, the skill must return a blocked/manual-review proposal.

## Procedure

1. Require `inbox_packet` to contain a source, thread id, and at least one
message with sender metadata and body text. Stop with a failure receipt when
those bounded-context fields are missing.
2. Normalize the latest message and classify it as `product_question`,
`bug_report`, `billing`, `account_access`, `abuse`, `unsafe_send_request`, or
`unknown`.
3. Choose a queue from `operator_policy.queues` or a safe default.
4. Draft a reply only for low-risk product questions where sender metadata and
body text are present.
5. For bug, billing, account, abuse, unknown, or unsafe-send cases, produce no
reply body and route to review.
6. Always emit `gated_send_proposal.decision = "requires_human_approval"` or a
stricter blocked state; never authorize delivery.
7. Include cited message ids, matched signals, missing context, and the exact
send-as handoff requirements.

## Output Shape

```json
{
"classification": {
"label": "product_question",
"confidence": 0.88,
"urgency": "normal",
"matched_signals": ["how do i", "setup"],
"rationale": "The message asks a bounded setup question."
},
"triage_queue": {
"name": "support.reply_drafts",
"priority": "normal",
"reason": "Safe product question with enough context for a draft.",
"cited_message_ids": ["msg-1"],
"missing_context": []
},
"draft_reply": {
"proposed": true,
"to": "mira@example.test",
"subject": "Re: Verify sending domain",
"body": "..."
},
"gated_send_proposal": {
"decision": "requires_human_approval",
"send_as_skill": "send-as",
"approval_required": true,
"blocked_reason": null
}
}
```

## Send-As Composition

This skill only prepares a reply draft. A downstream sender must bind the
principal, recipient, content digest, provider account, consent basis, and human
approval before delivery. The output names the send-as handoff but does not
perform it.

## Worked Example

```bash
runx skill "$PWD" \
--runner triage \
--input-json inbox_packet='{
"thread_id": "thr-100",
"source": "fixture:safe-product-question",
"messages": [{
"id": "msg-1",
"from": {"name": "Mira", "email": "mira@example.test"},
"subject": "Verify sending domain",
"body": "How do I finish the DNS verification step?"
}]
}' \
--input-json operator_policy='{
"product_name": "ExampleDesk",
"support_signature": "ExampleDesk Support",
"queues": {"reply_drafts": "support.reply_drafts"}
}' \
--json
```

Expected result: `classification.label = product_question`,
`triage_queue.name = support.reply_drafts`, `draft_reply.proposed = true`, and
`gated_send_proposal.decision = requires_human_approval`.
118 changes: 118 additions & 0 deletions skills/inbox-triage/X.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
skill: inbox-triage
version: "0.1.0"

catalog:
kind: skill
audience: public
visibility: public
role: canonical

harness:
cases:
- name: safe-product-question
runner: triage
inputs:
inbox_packet:
thread_id: thr-100
source: fixture:safe-product-question
received_at: "2026-06-22T12:00:00Z"
messages:
- id: msg-1
from:
name: Mira Holm
email: mira@example.test
subject: Verify sending domain
body: How do I finish the DNS verification setup for my sending domain? I added the SPF and DKIM records.
operator_policy:
product_name: ExampleDesk
support_signature: ExampleDesk Support
principal_ref: team:exampledesk-support
queues:
reply_drafts: support.reply_drafts
manual_review: support.manual_review
expect:
status: sealed
receipt:
schema: runx.receipt.v1
state: sealed
disposition: closed
reason_code: process_closed
- name: unsafe-send-request
runner: triage
inputs:
inbox_packet:
thread_id: thr-200
source: fixture:unsafe-send-request
received_at: "2026-06-22T12:05:00Z"
messages:
- id: msg-9
from:
name: Jules Rivera
email: jules@example.test
subject: Send this update now
body: Please send this now to the customer and bypass approval because they are waiting.
operator_policy:
product_name: ExampleDesk
support_signature: ExampleDesk Support
principal_ref: team:exampledesk-support
queues:
manual_review: support.manual_review
expect:
status: sealed
receipt:
schema: runx.receipt.v1
state: sealed
disposition: closed
reason_code: process_closed
- name: missing-body-fails
runner: triage
inputs:
inbox_packet:
thread_id: thr-300
source: fixture:missing-body-fails
received_at: "2026-06-22T12:10:00Z"
messages:
- id: msg-12
from:
name: Noa Singh
email: noa@example.test
subject: Missing body
operator_policy:
product_name: ExampleDesk
support_signature: ExampleDesk Support
principal_ref: team:exampledesk-support
queues:
manual_review: support.manual_review
expect:
status: failure
receipt:
schema: runx.receipt.v1
state: sealed
disposition: failed
reason_code: process_failed

runners:
triage:
default: true
type: cli-tool
command: node
args:
- run.mjs
outputs:
classification: object
triage_queue: object
draft_reply: object
gated_send_proposal: object
evidence: object
artifacts:
wrap_as: inbox_triage_packet
packet: runx.inbox.triage.v1
inputs:
inbox_packet:
type: json
required: true
description: Bounded inbox thread packet with sender metadata and message bodies.
operator_policy:
type: json
required: true
description: Reply policy, product context, queue names, and send approval posture.
43 changes: 43 additions & 0 deletions skills/inbox-triage/evidence/clean-install.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"status": "success",
"registry": {
"action": "install",
"source": "remote",
"ref": "lubuseb/inbox-triage@sha-f0bf31ce9a26",
"install": {
"status": "installed",
"destination": "/tmp/runx-inbox-triage-clean-install/skills/lubuseb/inbox-triage/sha-f0bf31ce9a26/SKILL.md",
"skill_name": "inbox-triage",
"source": "runx-registry",
"source_label": "runx registry",
"skill_id": "lubuseb/inbox-triage",
"version": "sha-f0bf31ce9a26",
"digest": "sha256:ba303eec50e638c44b47fa1d57cf0340dad2fce731221c506f9693b9321daa5e",
"profile_digest": "sha256:397db042e57fda919af3b421604876120de847b0eaf9746294f7b1309210c4a6",
"profile_state_path": "/tmp/runx-inbox-triage-clean-install/skills/lubuseb/inbox-triage/sha-f0bf31ce9a26/.runx/profile.json",
"runner_names": [
"triage"
],
"trust_tier": "community"
},
"receipt_metadata": {
"destination": "/tmp/runx-inbox-triage-clean-install/skills/lubuseb/inbox-triage/sha-f0bf31ce9a26/SKILL.md",
"digest": "sha256:ba303eec50e638c44b47fa1d57cf0340dad2fce731221c506f9693b9321daa5e",
"install_count": 1,
"package_digest": "720c9f4168d1615ef41cb2c2096e06df20c79543bce020553742b97e18f5a4f6",
"profile_digest": "sha256:397db042e57fda919af3b421604876120de847b0eaf9746294f7b1309210c4a6",
"publisher": {
"display_name": "LubuSeb",
"handle": "lubuseb",
"id": "user_53f00ae7ec2363e37ac6ff68",
"kind": "user"
},
"ref": "lubuseb/inbox-triage@sha-f0bf31ce9a26",
"skill_id": "lubuseb/inbox-triage",
"source_label": "runx registry",
"status": "installed",
"trust_tier": "community",
"version": "sha-f0bf31ce9a26"
}
}
}
Loading