Skip to main content

Policy and quiet hours

A policy is the behaviour record for one conversation. [policy] in everasync.toml is the default; a policy stored for a specific channel + conversation overrides it, and the /async slash command writes those overrides from inside the conversation itself.

If the lookup fails, the default is used and a warning is logged — a storage hiccup never changes behaviour silently.

The fields​

[policy]
enabled = true
sensitivity = "conservative"
nudge_delay_secs = 150
min_confidence = 0.7
unfurl_links = true

# [policy.quiet_hours]
# start = "22:00"
# end = "07:00"

enabled — master switch (default true)​

false stops the pipeline for that conversation immediately after the policy lookup: no classification, no connector calls, no nudge. Events are still recorded.

sensitivity — "conservative" (default) · "balanced" · "high"​

How much of the ambiguous middle is acted on, and whether an AI provider is consulted at all. Full treatment in sensitivity.

nudge_delay_secs — grace window (default 150)​

How long the author has to fix the message themselves before the private nudge fires. A thread reply or an edit from the author cancels it. Accepted range through /async delay is 0–3600 seconds.

Shorter is not better. The window is what converts "you were nudged" into "you fixed it before anything happened", and the cancel counter is a feature, not waste — see the private-nudge rule.

min_confidence — public-posting bar (default 0.7)​

The minimum connector confidence before a context card is posted publicly. This is a completely different number from the sensitivity thresholds: those gate the AI's opinion about the message, this gates a connector's certainty that it found the right artifact.

Roughly what connectors return:

SituationTypical confidence
Reference carried an exact key (EVER-123, PR #482)0.95
Author has exactly one open candidate0.80–0.85
Several candidates, best match by recency + keyword overlap≤ 0.75
Ever Gauzy, any matchcapped at 0.80 while the user map is hand-maintained

Raise it toward 0.9 if the wrong artifact ever gets posted; a bad public card is worse than no card. Lower it toward 0.6 only in a channel where everyone works on one repo.

With it on, URLs already in the message are handed to the connectors alongside the prose references. The platform's own preview gives you a title; a connector gives you "open · review requested · 2 approvals". Connectors claim the hosts they own and ignore the rest.

Turn it off in a channel where people paste a lot of links and do not want a reply on each:

/async unfurl off

quiet_hours — optional UTC window​

[policy.quiet_hours]
start = "22:00"
end = "07:00"

Inside the window, no nudge is scheduled. start > end wraps midnight, so the example above is the expected 22:00→07:00 night.

Two things worth knowing:

Quiet hours are UTC, and they suppress nudges only

The window is evaluated against UTC, not the workspace's timezone — a distributed team should pick the window that covers the hours nobody should be pinged in, not one team's local night.

Quiet hours do not suppress context cards. A card is useful whenever somebody reads the thread; a nudge at 3am is a notification nobody asked for.

The /async command​

The slash command edits the current conversation's policy and always replies privately — Slack renders slash-command responses ephemerally.

CommandEffect
/async or /async statusReport enabled, sensitivity, nudge delay, min confidence, link enrichment, and the AI provider + model in use
/async onEnable for this conversation
/async offDisable for this conversation
/async sensitivity high|balanced|conservativeSet sensitivity
/async delay <secs>Set the grace window (0–3600)
/async unfurl on|offToggle link enrichment
/async charterPrint the charter in force
/async digestCounters for the last 7 days

Anything unrecognised replies with the command list. /async status is the fastest way to confirm a whole install is live — it exercises HTTPS ingress, signature verification and the bot token in one round trip.

Example status reply:

Ever Async here is *ON* — sensitivity `Conservative`, nudge delay 150s,
min confidence 70%, link enrichment on.
AI: OpenRouter (+1 fallback) · `anthropic/claude-sonnet-5`

The REST equivalent​

The dashboard and any script use the same store:

# read — JSON null when nothing is stored (the client falls back to defaults)
curl -s http://localhost:8100/api/v1/policies/slack/C0123ABC

# write — a FULL policy object; 204 No Content on success
curl -X PUT http://localhost:8100/api/v1/policies/slack/C0123ABC \
-H 'content-type: application/json' \
-d '{
"enabled": true,
"sensitivity": "balanced",
"nudge_delay_secs": 120,
"min_confidence": 0.75,
"quiet_hours": null,
"unfurl_links": true
}'

PUT replaces the whole record — there is no partial update. Read, modify, write back.

warning
/api/v1 is unauthenticated by default

[auth] mode defaults to local_trusted — no credential is required and every caller is the local board principal — and /api/v1 carries permissive CORS. That is the right default for a private network and the wrong one for a public hostname: the two calls above are exactly how a stranger silences the bot in your channels.

Turn on the optional [auth] block before you publish it. Under mode = "authenticated" the read above needs viewer and the write needs operator (action policy.write). Ingress is different: it is protected by each platform's request signature, which is why it stays anonymous. → Security & authentication