skip to content
Threshold gate + unsure band04 / 05

Could This Meeting Be An Email?

Finally, a calibrated answer to the oldest question in corporate life.

flow graph · graphOf(needs-a-meeting)
gate · needs-a-meetinggateDoes this need a meeting?emit · keepemitkeepemit · declineemitdeclineemit · counter-offeremitcounter-offerthenotherwiseunsure
  • calls jev
  • your code
  • leaf / emit
  • fork / join
packages/examples/src/meeting-email.ts
1/**
2 * Could This Meeting Be An Email?
3 * Pattern: threshold gate on a score, with an "unsure" band.
4 *
5 * A score question rates the invite on a 0–4 rubric. The gate passes at 2.5,
6 * and anything within 0.4 of the bar takes a third, more nuanced path instead
7 * of flipping a coin at the boundary.
8 */
9import { emit, gate, noul, score } from "jevchain";
10import type { Example } from "./types";
12export const meetingEmail = gate("needs-a-meeting", {
13 title: "Does this need a meeting?",
14 ask: score("How much does this meeting need people talking live, in real time?", [
15 "Could be a Slack message",
16 "Could be an email",
17 "Could be a doc with comments",
18 "A short call would help",
19 "Must be live: a real decision, a conflict, or brainstorming",
20 ]),
21 alsoAsk: { agenda: noul("Does the invite include a clear agenda or goal?") },
22 pass: { min: 2.5 },
23 then: emit("Keep it. Maybe make it 25 minutes. You deserve 5 minutes of silence.", { id: "keep" }),
24 otherwise: emit("Decline with a doc link and a smile. Reclaim your afternoon.", { id: "decline" }),
25 unsure: { margin: 0.4, then: emit("Counter-offer: 15 minutes, agenda first, cameras optional.", { id: "counter-offer" }) },
26});
28export const example: Example = {
29 slug: "meeting-email",
30 title: "Could This Meeting Be An Email?",
31 tagline: "Finally, a calibrated answer to the oldest question in corporate life.",
32 pattern: "Threshold gate + unsure band",
33 lesson:
34 "Gate on a score or probability, and give borderline cases their own path. A decision near the bar is a different decision, not a coin flip.",
35 chain: meetingEmail,
36 file: "meeting-email.ts",
37 inputs: [
38 {
39 label: "Weekly sync",
40 value: { title: "Weekly sync", description: "Going around the room with status updates.", attendees: 14, minutes: 60 },
41 },
42 {
43 label: "Incident retro",
44 value: {
45 title: "Checkout outage retro",
46 description: "Agenda: timeline (10m), root cause debate (20m), decide on the fix and owner (15m). Payments + infra must attend.",
47 attendees: 6,
48 minutes: 45,
49 },
50 },
51 {
52 label: "Quick question",
53 value: { title: "Quick question", description: "", attendees: 2, minutes: 30 },
54 },
55 ],
56};

api key

bring your own typesafe key, or ride the shared one (rate-limited, be nice).

shared key
checking…
your key
not set

your key stays in this browser (localStorage, jevchain.byok). it only travels to this site's /api/jev proxy in an x-typesafe-key header, which forwards it to typesafe and immediately forgets it. nothing is logged or stored server-side. requests on your own key get a much roomier rate limit.

keyboard shortcuts

fewer clicks, more chains. these work anywhere outside a text field.