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
289 changes: 289 additions & 0 deletions .claude/skills/triage-inbox/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
# Triage Inbox Skill

Surface the top 5 karrio engineering items for the day, ranked and tagged. Bridge between the **Notion Agent Inbox** (cross-domain capture surface) and the **Linear Karrio Maintenance project** (engineering source of truth). Approval-gated — never auto-acts.

## Three-Surface Architecture

```
NOTION Agent Inbox GH DISCUSSIONS LINEAR Karrio Maintenance
(personal capture — (community capture — (engineering execution —
any role) bug reports, ideas) agents read state here)

capture ─────────────promote──────────────► EBE-### issue
(user approves) │
branch (EBE-{n}-{slug})
PR titled "[EBE-{n}] ..."
merge → Linear auto-moves
to Done
```

- **Notion Inbox** = personal capture + reflection. Cross-domain (Eng/Chief/Logger/Research/Admin). Eng-tagged items promote to Linear.
- **GitHub Discussions** = community capture. Bug reports and feature ideas from external users. Promote to Linear when triaged.
- **Linear** = canonical engineering backlog. `EBE-###` IDs embed in branch names so parallel agents don't collide.
- **GitHub Issues** = **disabled** on `karrioapi/karrio`. Don't query `gh issue list`.
- **GitHub PRs** = code. Title includes `[EBE-###]` so Linear auto-moves the issue to In Review / Done on merge.

## When to Use

- Start of a karrio working day (morning triage)
- Before opening a new Conductor workspace ("what should I pick up?")
- Whenever the Notion Inbox grows beyond ~10 pending Eng-tagged items

## Modes

This skill has **two modes**. The user picks; default is `triage`.

| Mode | Purpose |
|---|---|
| `triage` (default) | Rank Linear backlog + surface top 5. Recommend workspaces. |
| `promote` | Walk through Notion Inbox `Pending` items tagged `Eng`, propose Linear issues, create on approval. |

## Inputs

| Source | Tool | Filter |
|---|---|---|
| Linear backlog | `mcp__claude_ai_Linear__list_issues` | `project: "Karrio Maintenance"`, `state: Backlog` or `Todo` |
| Notion Agent Inbox | `mcp__claude_ai_Notion__notion-search` on `collection://e8147767-2ae6-43df-a1f5-9d89c2312640` | `Status = Pending` AND (`Agent = Eng` OR `Agent` is empty) |
| GH Discussions | `gh api graphql` (see below) | recent unresolved discussions in `General`, `Ideas`, `Q&A` (skip `Announcements`, `Show and tell`, `Polls`) |
| In-repo TODOs (optional) | `Grep "TODO\|FIXME"` | only when explicitly asked |

GraphQL query for Discussions:

```
query {
repository(owner: "karrioapi", name: "karrio") {
discussions(first: 30, orderBy: {field: UPDATED_AT, direction: DESC},
categoryId: <general/ideas/q-a id>) {
nodes {
number title url updatedAt
answer { id } # null = unresolved
category { name }
labels(first: 5) { nodes { name } }
}
}
}
}
```

Categories worth triaging: `General` (most bug reports land here), `Ideas` (feature/connector requests), `Q&A` (often surfaces real bugs). Skip `Announcements`, `Show and tell`, `Polls`. Only promote discussions where `answer` is null (unresolved) and the title doesn't already correspond to an open Linear issue.

### Reference IDs

| Surface | ID |
|---|---|
| Linear team | `Eben Labs` (`EBE`) — `12c98fbb-796b-498b-9674-7e634219c964` |
| Linear project | `Karrio Maintenance` — `da42e101-382d-4c57-a90b-a12204ca5c31` |
| Notion Inbox | `collection://e8147767-2ae6-43df-a1f5-9d89c2312640` |

### Notion Inbox schema

| Field | Values |
|---|---|
| `Task` | title (required) |
| `Status` | `Pending` / `Approved` / `Done` / `Rejected` |
| `Risk Level` | `Low` / `Medium` / `High` |
| `Suggested Action` | free text |
| `Link` | URL |
| `Agent` | `Chief` / `Logger` / `Eng` / `Research` / `Admin` |

### Linear scope labels (created)

| Label | When to apply |
|---|---|
| `scope/connector` | `modules/connectors/<carrier>/` |
| `scope/server` | Django API, models, migrations, GraphQL, Huey jobs |
| `scope/frontend` | `apps/dashboard/`, `packages/` |
| `scope/release` | version bumps, changelog |
| `scope/docs` | README, AGENTS.md, in-repo docs |
| `scope/chore` | CI, deps, lint, infra |
| `needs-PRD` | requires `PRDs/<name>.md` before work starts (see `.claude/rules/prd-and-review.md`) |
| `Bug` / `Feature` / `Improvement` | workspace-level (pre-existing) |

---

## Mode: `triage` (default)

### 1. Pull Linear backlog

```
mcp__claude_ai_Linear__list_issues with
project: "Karrio Maintenance"
state: "Backlog" (also pull "Todo" — those are next-up)
limit: 50
```

If the backlog is empty, **switch to `promote` mode** automatically — the user has nothing to triage.

### 2. Rank

Sort by:
1. `priority = 1 (Urgent)` first — outages or release blockers
2. `Bug` label before `Feature`/`Improvement` (production over polish)
3. Smallest scope first (single `scope/connector` before cross-module work)
4. `state = Todo` before `state = Backlog` (already accepted into the queue)
5. Freshness (newer `updatedAt` wins ties)

Cap output at **5 items**. Life OS rule is "max 3 priorities per week" — surface 5 so the user picks 1-3.

### 3. Recommend workspace + role

For each surfaced item, suggest:

| Label combo | Suggested skill / Conductor workspace |
|---|---|
| `scope/connector` | `carrier-integration` skill, workspace `EBE-{n}-{carrier}-{short}` |
| `scope/server` + `Bug` | `debugging` skill, current workspace OK if scoped |
| `scope/server` + `needs-PRD` | `create-prd` skill first, then fresh workspace |
| `scope/frontend` | fresh workspace `EBE-{n}-{short}` (no dedicated skill yet — flagged gap) |
| `scope/release` | `release` skill, dedicated release workspace |
| `Bug` w/o scope | gstack `/investigate` |

Workspace naming convention: **`EBE-{issue-number}-{short-slug}`**. The branch becomes the same. Linear auto-links the PR when `[EBE-{n}]` appears in the PR title.

### 4. Output

```
## Triage — <date>

| # | Pri | Labels | Issue | Suggested action | Workspace |
|---|---|---|---|---|---|
| 1 | Urgent | scope/connector, Bug | EBE-12 SmartKargo fractional seconds | `debugging` skill | EBE-12-smartkargo-seconds |
| 2 | High | scope/server, needs-PRD | EBE-9 Tenant-scoped webhook retries | Write PRD first | EBE-9-webhook-retries |
| ... |
```

Below the table:
- **Skipped:** items that didn't make the top 5 with one-line reason each
- **Backlog health:** count by status, count by scope; flag if Backlog > 30
- **Notion Inbox status:** count of `Pending` Eng-tagged items not yet promoted

### 5. Approval gate

Wait for the user to pick items. Do **not**:
- Move Linear issues to `Todo` / `In Progress` automatically
- Spawn Conductor workspaces
- Modify branch state

Once the user picks issue `EBE-{n}`:
- `mcp__claude_ai_Linear__save_issue` with `id: "EBE-{n}", state: "Todo"` (or `In Progress` if they're starting now)
- Print the exact Conductor command for the workspace + the branch name

---

## Mode: `promote`

For pushing **Notion Inbox** items OR **GH Discussions** into Linear. Run when:
- Linear backlog is sparse and capture surfaces have unprocessed items
- User explicitly says "promote" / "drain the inbox" / "promote discussions"

### 1. Pull from capture surfaces

**Notion Inbox** — Eng-tagged Pending items:
```
mcp__claude_ai_Notion__notion-search with
query: ""
data_source_url: "collection://e8147767-2ae6-43df-a1f5-9d89c2312640"
```
Filter in-process to `Status = Pending` AND (`Agent = Eng` OR `Agent` empty).

**GH Discussions** — recent unresolved discussions:
```
gh api graphql -f query='<query above with recent discussions, answer:null>'
```
Filter to categories `General` / `Ideas` / `Q&A`. De-dupe against existing open Linear issues (compare titles).

The user usually picks ONE source per run. Default: ask which surface to drain.

### 2. For each item, draft a Linear issue

Build a draft per Inbox row:

| Linear field | Source |
|---|---|
| `team` | `Eben Labs` |
| `project` | `Karrio Maintenance` |
| `title` | Notion `Task` field |
| `description` | Notion `Suggested Action` + Notion row URL as "Source" link |
| `priority` | Map Notion `Risk Level`: High → 2 (High), Medium → 3 (Medium), Low → 4 (Low) |
| `labels` | Inferred scope label(s) from keyword matching, plus `Bug` / `Feature` / `Improvement` |
| `state` | `Backlog` |

#### Scope inference (from `Task` text)

| Tag | Trigger keywords |
|---|---|
| `scope/connector` | carrier name (UPS, FedEx, SmartKargo, ...), `mapper`, `proxy`, `provider`, `rate`, `tracking`, `shipment` |
| `scope/server` | `Django`, `migration`, `model`, `serializer`, `viewset`, `huey`, `graphql` |
| `scope/frontend` | `dashboard`, `next`, `react`, `apps/dashboard`, `packages/ui` |
| `scope/release` | `version`, `changelog`, `release`, `bump` |
| `scope/docs` | `README`, `docs/`, `AGENTS.md`, doc-only |
| `scope/chore` | dependency bump, lint config, CI tweak |
| `needs-PRD` | new feature / multi-module / schema change / breaking API — see `.claude/rules/prd-and-review.md` |

If no scope keyword matches, leave scope unset and flag it for the user.

### 3. Show drafts to user, batched

Print a table of all drafts. Do **not** create yet:

```
## Promote Inbox → Linear (drafts)

| # | Notion Task | Proposed Linear title | Labels | Priority |
|---|---|---|---|---|
| 1 | "Fix smartkargo seconds" | "Fix smartkargo fractional seconds parsing" | scope/connector, Bug | High |
| 2 | "Webhook retries" | "Tenant-scoped webhook retry queue" | scope/server, needs-PRD | Medium |
```

Ask the user which to create. Accept ranges ("1-3, 5") or `all`.

### 4. On approval — create issues + close Inbox rows

For each approved draft:
1. `mcp__claude_ai_Linear__save_issue` with the drafted fields → returns `EBE-{n}`
2. `mcp__claude_ai_Notion__notion-update-page` on the source Inbox row:
- `Status = Approved`
- `Link = <Linear issue URL>` (so the Notion row points to the canonical issue)

Print the resulting `EBE-{n}` IDs back to the user.

---

## Scheduling

```
/schedule create "Karrio morning triage" cron="0 8 * * 1-5" prompt="/triage-inbox"
```

The schedule produces the table only. Actual approval happens when the user reads it. Don't schedule `promote` — promotion should be deliberate.

## Anti-Patterns

- ❌ Creating Linear issues without showing the user the draft first
- ❌ Surfacing >5 items in `triage` mode — defeats the purpose
- ❌ Skipping `needs-PRD` detection — items going straight to code is the #1 way scope creeps
- ❌ Working off Inbox items directly without promoting first — splits the source of truth
- ❌ Auto-moving Linear issues to `In Progress` — that's a deliberate user action, marks the start of work
- ❌ Triaging items where `Agent = Admin` or `Research` — wrong lane, leave them alone
- ❌ Writing to the Notion Daily Log — that's the Logger role's job

## Outputs This Skill Does NOT Produce

Single-responsibility — keep these in their lanes:

- Daily Log entries → Logger role
- Decision Log entries → Chief of Staff
- Weekly Reviews → `/retro` flow
- Code changes → spawned specialist workspaces

## Reference

- Linear project: https://linear.app/eben-labs/project/karrio-maintenance-58f70aa22f0e
- Notion Inbox: `collection://e8147767-2ae6-43df-a1f5-9d89c2312640`
- Life OS doc: https://www.notion.so/3547c752109c80dc9938dd1bfc13e9f1
- PRD rule: `.claude/rules/prd-and-review.md`
- Branch convention: `EBE-{n}-{short-slug}` — keeps Linear auto-link working
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Skills in `.claude/skills/` provide step-by-step guides:
- `project-setup/` — Environment setup, running servers, schema generation
- `create-prd/` — Write PRDs with ASCII diagrams before non-trivial features
- `review-implementation/` — Fresh-context review checklist for quality gates
- `triage-inbox/` — Pull Notion Agent Inbox + GitHub backlog, rank, tag scope, surface top 5 (morning triage)

## Full Reference

Expand Down
Loading