Skip to content

Commit d298dc3

Browse files
mpstatonclaude
andcommitted
new(context-v, agent-skills): triage-inbox-w-suggestions — the co-pilot discipline for draining the corpus inbox
The triage layer the Corpus-Inbox spec deferred, started as an operator+agent skill so a cleared-context session can run it: scan pending inbox files, stamp triage_suggestion blocks, propose in confidence-banded batches, operator sweeps. Encodes the destination model (funder org / person / domain primary home; SurrealDB as the single canonical index with uuid minted at filing time — inbox items have NO row until filed; cheap reference .md copies with reference_of loop-safety frontmatter), the six task lanes, Train-Case-with-lowercase- connectors tagging, the run-manifest resume rhythm, and the open decisions the first co-pilot run must settle (mechanics fork, non-funder orgs, person content home, reference double-counting). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013u3i9BoKeZRndqToQ3M5Q5
1 parent 15c1e55 commit d298dc3

1 file changed

Lines changed: 199 additions & 0 deletions

File tree

  • context-v/agent-skills/triage-inbox-w-suggestions
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
name: triage-inbox-w-suggestions
3+
description: The operator+agent discipline for draining a client's corpus inbox (clients/<client>/corpus/inbox/, 162 pending in reach-edu as of 2026-07-25) — agent scans each pending file, stamps a triage_suggestion, and proposes destinations in confidence-banded batches; the operator sweeps. Use whenever the user says "triage the inbox", "work through the inbox", "drain the inbox", "/triage-inbox", or asks to file/sort pending inbox captures. Encodes the destination model (funder org folder, person, or domain — with SurrealDB as the single canonical index and cheap reference .md copies fanned into any additionally-relevant folder), the six task lanes (TRIAGE/EXTRACT/ENRICH/DEDUPE/FLAG/DISCARD), the tagging convention (YAML array, Train-Case with lowercase connector words), and the batch/resume rhythm. Co-pilot phase: agent proposes, operator disposes; nothing files without an operator sweep.
4+
---
5+
6+
# Triage Inbox with Suggestions
7+
8+
The inbox (per [[../../specs/Corpus-Inbox-Capture-and-Triage|Corpus-Inbox-Capture-and-Triage]])
9+
promised "capture first, triage later." Capture shipped 2026-06-09; *later* is
10+
now — reach-edu's inbox sits at **162 pending files** and the flat list broke
11+
down at 86 (see [[../../explorations/Inbox-Sort-by-Agent-Tasks|Inbox-Sort-by-Agent-Tasks]],
12+
whose taxonomy and confidence bands this skill operationalizes). The goal of a
13+
triage run: **every item leaves `inbox/` for a core folder** — an existing one,
14+
or a new one we create deliberately. Strive for a full drain; a residue of
15+
genuinely-uncertain items staying `pending` is acceptable, forcing them is not.
16+
17+
## Ground truth (verified against code + disk, 2026-07-25)
18+
19+
- **Inbox files** live at `clients/<client>/corpus/inbox/<date>_<slug>.md`
20+
real corpus markdown with the `captured_*` / `triaged_*` / `inbox_status`
21+
frontmatter blocks. Many have `tags: []` and empty `captured_note`.
22+
- **Inbox items have NO SurrealDB row.** `corpus.inbox.add` writes the file
23+
only. The single-index belief holds for *filed* content — `source.add`
24+
mints a `source_uuid` (+ per-corpus `source_usages` rows) for domain
25+
corpora; `organization.corpus.add` / `person.corpus.add` mint
26+
`content_items` rows for org/person corpora — but the canonical uuid for an
27+
inbox item is **minted at triage time, by filing through a capability**.
28+
Never handcraft uuids or DB rows; the capability path also carries the
29+
`client` tag every canonical write requires.
30+
- **Disk taxonomy** under `clients/<client>/corpus/`:
31+
- `funders/<org-slug>/` — corpora for organizations flagged as funders.
32+
(Canonically these are `organizations` in SurrealDB; *funder* is a role
33+
modifier, and not every collection-worthy org is one — a non-funder org
34+
destination means deciding where non-funder org corpora live: see Open
35+
decisions.)
36+
- `strategies/<slug>/sources/` — domain corpora; the folder name comes from
37+
the domain type (`strategy``strategies`, `topic``topics`,
38+
`thesis``theses`, `category``categories`,
39+
`market-segment``market-segments`). reach-edu's domains are strategies.
40+
- `inbox/` — the queue this skill drains.
41+
- `_discarded/` — created on first discard; plain visible folder, never
42+
dot-prefixed, never hard-delete.
43+
- `AGENTS.md` at the corpus root — read it at session start; it is the
44+
corpus's own operating guidance.
45+
46+
## The destination model
47+
48+
Each pending item gets **one primary home** plus optional reference copies:
49+
50+
1. **Primary home** — a funder org folder, a person, or a domain
51+
(thesis/strategy/topic/…). Filing goes **through the capability wire** so
52+
the canonical index row exists first: `source.add` (domains, resolves
53+
`(domain_type, domain_slug)` against the live `domain.list` — never
54+
fabricate a slug, per the [[../inbox-curation/SKILL|inbox-curation]]
55+
decision tree), `organization.corpus.add` (org/funder),
56+
`person.corpus.add` (person).
57+
2. **Reference copies** — once the canonical uuid exists, the markdown is
58+
cheap to replicate into *any other* folder where agents will later work,
59+
so deliverable-generation can stay inside one folder without hunting
60+
stragglers. Every reference copy MUST declare itself in frontmatter:
61+
62+
```yaml
63+
reference_of: "<canonical uuid (source_uuid or content_items id)>"
64+
canonical_path: "corpus/strategies/workforce-development/sources/2026-06-09_….md"
65+
reference_note: "replicated here because <one line>"
66+
```
67+
68+
The `reference_of` key is the loop-safety contract: anything scanning a
69+
folder dedupes by it, and coverage/count surfaces must learn to skip it
70+
(see Open decisions — double-count risk).
71+
3. **Sweeping / hard-to-classify content** gets an *abstract* home, not a
72+
forced org: propose a `topic` (or `category`) domain via `domain.create`
73+
— always `chat_propose`-grade (creating a corpus is a visible,
74+
workspace-wide decision), never silent — and file there. Examples from
75+
the live inbox: cross-funder sector reports, Work-Trend-Index-style
76+
industry PDFs, regulatory documents.
77+
78+
## The suggestion pass (the scanning half)
79+
80+
For each `inbox_status: pending` file, read the cheap signals — `title`,
81+
`exact_url` host, `published_at`, `captured_note`, first ~500 chars of body,
82+
`binary_asset` presence — and match against three rosters loaded once per
83+
run: the funder/org roster (slugs + known domains), the live `domain.list`,
84+
and the harvested tag roster. Stamp the result into the file as the
85+
`triage_suggestion:` block (schema per
86+
[[../../explorations/Inbox-Sort-by-Agent-Tasks|Inbox-Sort-by-Agent-Tasks]] —
87+
action, proposed destination, confidence 0–1, one-line rationale, signals
88+
used). The block is a **proposal**; the operator's sweep decides.
89+
90+
Confidence bands govern the sweep affordance:
91+
92+
| Band | Confidence | Sweep shape |
93+
|---|---|---|
94+
| Auto-routable | ≥ 0.90 | Presented as a batch list; one operator yes files the whole batch |
95+
| Suggested | 0.60–0.89 | Per-item confirm ("looks like X — yes / different / skip") |
96+
| Uncertain | < 0.60 | Operator decides unaided; agent stays quiet |
97+
98+
Co-pilot phase rule: **no auto-apply, ever, without an operator sweep** —
99+
even the ≥0.90 band files only after a batch yes. Downgrade this friction
100+
only after calibration data exists (operator override rate per band).
101+
102+
## Task lanes
103+
104+
Every file maps to exactly one lane (taxonomy from the exploration):
105+
106+
- **TRIAGE** — destination is clear or suggestible. The main lane.
107+
- **EXTRACT** — PDFs/binaries with empty bodies: run extraction before
108+
classification, or classify from title+URL alone with a confidence
109+
penalty and note `body_unextracted: true` in the suggestion.
110+
- **ENRICH** — stub captures needing more context; agent may fetch linked
111+
pages, human picks.
112+
- **DEDUPE** — `exact_url` (or binary sha256) already exists in
113+
`content_items` / `sources` or in a filed folder: propose merge-or-skip,
114+
never file a duplicate.
115+
- **FLAG** — high-signal, operator wants it surfaced but not filed yet;
116+
stays pending with the flag noted.
117+
- **DISCARD** — cookie walls, 404 bodies, consent boilerplate,
118+
`content_length_bytes` tiny: move to `corpus/_discarded/` (archive, no
119+
delete), `inbox_status: "discarded"`.
120+
121+
Drain order: auto-routable TRIAGE first, then suggested TRIAGE, EXTRACT,
122+
DEDUPE, DISCARD, then the uncertain residue. Easy buckets first; the human's
123+
attention goes to the hard tail.
124+
125+
## Mechanics of one filing
126+
127+
1. File through the capability → canonical uuid + client tag exist.
128+
2. Place the markdown in the primary home; populate the `triaged_*` block
129+
(`triaged_by: "operator-confirmed:triage-inbox-w-suggestions"` when the
130+
operator confirmed an agent proposal — provenance matters) and flip
131+
`inbox_status: "triaged"`. Keep the `captured_*` block untouched forever.
132+
3. Write any reference copies with their `reference_of` frontmatter.
133+
4. Tags (see below) go on at filing time, not before.
134+
5. Never clobber anything a human already filed — filing is additive;
135+
collisions become DEDUPE decisions.
136+
137+
> **Open mechanics fork (resolve in the first co-pilot run, then record the
138+
> ruling here):** `source.add` fetches the URL fresh and writes its own file
139+
> under `sources/` — but the inbox file already holds a fetched body (and
140+
> sometimes a binary). Either (a) file via capability and treat the *inbox
141+
> file* as the moved/reference artifact, or (b) let the capability write its
142+
> file and demote the inbox original to a reference copy. Pick once, apply
143+
> uniformly.
144+
145+
## Tagging convention
146+
147+
- YAML inline array of quoted strings: `tags: ["Workforce-Development", "State-of-the-Industry", "Future-of-Work"]`
148+
- Casing is title-like with dashes: each substantive word capitalized,
149+
**connector/minor words lowercase** ("of", "the", "on", "an", "and"),
150+
acronyms uppercase (`"HNWI"`, `"AI-in-K-12"`). Not ALLCAPS; not
151+
every-word Train-Case.
152+
- **Prefer an existing tag over minting a new one.** Harvest the roster at
153+
run start (grep `tags:` across the client's corpus + the domain sources'
154+
tags) and match against it; new tags need a reason a roster tag can't
155+
serve.
156+
157+
## Batch rhythm & resumability
158+
159+
- Work in batches of ~15–25 files; each batch = scan → stamp suggestions →
160+
operator sweep → file → log.
161+
- Keep a run manifest at `clients/<client>/corpus/inbox/_triage-runs/<date>_<n>.md`
162+
(plain visible folder): one line per file — decision, destination, uuid,
163+
band, who decided. This is what makes a cleared-context session resumable
164+
and the calibration data for loosening the bands later.
165+
- Idempotence: only `inbox_status: "pending"` files are in scope; a re-run
166+
after an interrupted session picks up exactly where the manifest stopped.
167+
168+
## Never
169+
170+
- Hard-delete anything (discard = archive to `_discarded/`).
171+
- Fabricate a domain slug, org slug, or uuid — resolve against live rosters.
172+
- File a canonical write without the `client` tag (the capability wire does
173+
this for you; another reason not to bypass it).
174+
- Dot-prefix a folder meant for human review.
175+
- Force the last uncertain items just to hit 100% — pending is an honest
176+
state.
177+
178+
## Open decisions (co-pilot phase resolves these; record rulings in place)
179+
180+
- [ ] The mechanics fork above (move-the-inbox-file vs capability-writes-
181+
fresh + inbox file becomes reference).
182+
- [ ] **Non-funder organizations**: orgs worth collecting on that aren't
183+
funders — sibling folder (`corpus/organizations/<slug>/`?) or the
184+
funders folder with a DB flag distinction only?
185+
- [ ] **Person-destined content**: `person.corpus.add` is DB-side; persons
186+
have no on-disk corpus folder today. Where does the markdown live?
187+
- [ ] **Reference-copy double-counting**: coverage lenses and
188+
`corpus.list_for_record` walk the filesystem; teach them to skip
189+
`reference_of:` files before fanning references widely.
190+
- [ ] When (if ever) the ≥0.90 band graduates from batch-confirm to
191+
auto-apply-with-undo.
192+
193+
## See also
194+
195+
- [[../../specs/Corpus-Inbox-Capture-and-Triage|Corpus-Inbox-Capture-and-Triage]] — capture spec; this skill is the triage layer it deferred.
196+
- [[../../explorations/Inbox-Sort-by-Agent-Tasks|Inbox-Sort-by-Agent-Tasks]] — taxonomy, `triage_suggestion:` schema, confidence bands, phased plan.
197+
- [[../inbox-curation/SKILL|inbox-curation]] — didi's live filing decision tree; this skill is its bulk/backlog sibling and shares its domain-resolution discipline.
198+
- [[../../plans/Download-PDFs-into-Corpus-Inbox|Download-PDFs-into-Corpus-Inbox]] — why binaries sit beside markdown; feeds the EXTRACT lane.
199+
- `clients/<client>/corpus/AGENTS.md` — the corpus's own standing guidance; read before every run.

0 commit comments

Comments
 (0)