NOTE: This is the canonical provider-independent contract. Both provider wrappers must load it in full.
- Resolve supporting assets and relative script paths against
.claude/skills/user-gateway/; the canonical prose lives here while implementation assets remain with the compatibility launcher. - A named skill call means the host's skill-dispatch primitive. An Agent, Explore agent, workflow, or sub-agent means an isolated task/action invocation with fresh context and the requested capability tier. Provider wrappers map these roles to their native APIs.
- Model tier names in inherited procedures describe capability roles. Resolve them through
config/model-tier-map.json; an unavailable required capability returnsrequired_tool_missingrather than weakening a gate. - Never expose hidden chain-of-thought. Preserve only decisions, evidence, commands, structured artifacts, and operator-facing rationale required by this contract.
A THIN converter for the pre-work moment. The operator has a head full of half-formed observations about a topic — some broken things, some worries, some wishes, an undecided question — and wants them made concrete without anything getting lost or invented. This skill composes three existing contracts and owns NO routing table and NO ledger grammar of its own:
- Routing —
<repo>/_shared/skill-pipeline.md(the routing web) is the ONE owner of the rails, their entry conditions, the tiebreak rule, the re-route edges, and the re-route contract. The gateway CONSULTS it per fragment and cites it in output; per the web's own intro, no skill hardcodes its own routing table — reproducing the web's rail/entry-condition table in this file or in gateway output (verbatim or paraphrased) is a DEFECT. - Ledger —
<repo>/_shared/intake-engine.mdowns the ledger path formula + topic-slug rule (§1 — which itself reuses the shakedown-engine §1 slug rule by cross-reference), the row grammar +F<N>ids + status vocabulary + one-line disposition rule (§2), the zero-open check + canonical/goalstring (§3), and idempotent seeding (§4). Cite it; never restate it. - Listening —
../user-draft/core.mdStep 2 + Principles own the discipline: find the gaps, ask 3-5 targeted questions in at most ONE round, never block.
Pinned posture: CONVERT, DON'T PROPOSE. Every ledger row and every seed traces to a
fragment the operator voiced (or to their answer to a clarifying question). The gateway
never invents work the operator didn't voice — generating improvement ideas is
/goblin-suggest's job, and the boundary is hard: an
unprompted suggestion in gateway output ("while we're in there…", "you should also…") is
a DEFECT, not a bonus.
Use when the operator opens the intake valve: "here's everything on my mind about X", "this project doesn't feel right", "take my rough observations and make them concrete", or an unstructured vent about a topic.
Do NOT use:
- A single, already-clear fragment → go straight to its rail (one broken thing →
/user-debug; one capability wish →/plan-feature; …). The gateway earns its overhead only when fragments are plural or tangled. - Wanting IDEAS ("what should I improve?") →
/goblin-suggest. The gateway converts what was said; it does not generate what wasn't. - Refining ONE prompt or goal →
/user-draft.
Inputs: a topic and the free-text vent. Compute the topic slug per intake-engine §1 (its cross-reference chain to shakedown-engine §1 owns the rule — never restate it). No topic given → derive one from the vent's dominant subject and say so in the opening line. No vent given → ask for it. That single ask is the skill's missing input, not a block — the never-block rule (Step 1) governs refine questions, not the vent itself.
Per user-draft Step 2: split the vent into its distinct fragments (echo the split so the operator can correct it), find the gaps that would change a route or a seed, then ask 3-5 targeted questions in ONE round max — zero when the vent is already clear (say so). Never block: when answers don't come, or only some do, proceed with what's there and mark each assumption inline in the affected row/seed. No second round, ever.
Only ask questions that change the outcome: a repro-vs-distrust split, a constraint a seed needs, which document a cruft-smell is about. Never ask for more work items — see the pinned posture.
Seed the ledger per intake-engine (path + slug: §1; load-before-derive idempotence: §4 —
an existing ledger for this topic is LOADED and appended, new rows continuing the F<N>
sequence). Every fragment from Step 1 becomes exactly one row (§2 grammar; one-line
disposition).
Choose each row's route by CONSULTING the routing web — read its rails and entry
conditions, apply its tiebreak rule. Cite it in the output; do not reproduce its table.
Rows flip open → routed (or parked) as Step 3 dispatches them; no row stays
open at close — a fragment the gateway cannot safely route parks with the routing
question stated (that is an operator-only call), never a "will route later" leftover.
Each routed row gets a seed the operator can paste unchanged. Seed shapes by rail (this is the gateway's OUTPUT contract — which rail a fragment belongs on stays the web's call):
- bug → one complete
/user-debug --symptom '<one-line symptom>'line (plus the repro when one was voiced). - plan → the FULL
/plan-featureseed paragraph in the gateway output (problem + voiced constraints, scope strictly as voiced); the ledger disposition keeps only the one-line form (intake-engine §2 owns that rule — the ledger never holds paragraphs). - investigate → external/multi-source: a deep-research charter dispatched via
Workflow({name: "deep-research-pinned"})— never the built-in name (CLAUDE.md's dispatch rule, echoed by the web); codebase-local: a scoped Explore-agent framing (what to sweep, what a finding looks like). - verify → the applicable invocation among the verify rail's four modes —
/review-uat//user-uat//user-shakedown//user-walkthrough— chosen per the web's verify-rail detail section. - trim → a
/plan-triminvocation naming the plan document. (Cruft-smell that is NOT about a plan document is not trim's — the web's trim note routes it investigate-first.) - do → the two-step pair matching the do rail's real mechanics:
1.a/goblin-suggest --small <project>line (persists the atom — the voiced task text guides which atom to pick, or confirms it appears), then2.a/goblin-do <matching atom id or fuzzy text>line./goblin-doresolves persisted atoms only — a bare/goblin-doon a freshly-voiced task fails to resolve. - draft → a
/user-draft <the operator's rough thoughts>line. - decide → NOT a seed: the row parks with the open question stated verbatim in its disposition. Never answer it, never pick a side, never convert it into work — parked rows wait for the operator (intake-engine §2/§3 own parked semantics; deciding is the one thing the gateway must never do for them).
Three elements, always:
- Ledger path — absolute, per the intake-engine §1 formula.
- The canonical
/goalline for this topic slug — the string is defined ONCE in intake-engine §3; emit it exactly, never author a variant. (Arming it is the operator's choice.) - QUICK COPY — every routed row's seed, one paste-able line each (the plan rail's
line invokes
/plan-featurewith a compact form, the full paragraph staying above; the do rail contributes its two-step pair as two lines). Parked rows are questions, not seeds — they appear above with their open question, never in QUICK COPY.
Rows may later re-route: a rail skill that discovers wrong-rail work mid-run follows the web's re-route contract and writes back to this ledger per intake-engine §5. The gateway plays no part in that — it ends at the closing block.
The evals/ suite targets THIS gateway contract (created 2026-07-13, plan Step 12 /
#313): 10 assertions across 3 categories in evals/evals.json (passing threshold
8/10), 2 scenarios in evals/test_scenarios.json (one multi-rail vent, one clear
three-fragment vent), and a golden corpus of 2 goods + 10 single-defect bads under
evals/golden/ (manifest.json maps each bad to the one assertion it trips). Any edit
that changes an output contract here (listening pass, ledger seeding, seed shapes,
closing block) must update the affected assertions and goldens in the same diff, keeping
this footer's numbers equal to evals.json's. The rails + entry conditions + tiebreak +
re-route contract stay owned by skill-pipeline.md, and the ledger grammar + statuses +
zero-open check + canonical /goal stay owned by intake-engine.md — when either
changes, re-check the citation-consistency assertions here instead of duplicating the
contract.