style: remove em/en dashes repo-wide, add CLAUDE.md rule against them (#890)

Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.

Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.

Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.

Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-07-04 15:58:06 +02:00
committed by GitHub
co-authored by Claude Sonnet 5
parent a5154ee884
commit ec27228a8e
1588 changed files with 28388 additions and 10890 deletions
+9 -9
View File
@@ -2,7 +2,7 @@
* Webhook event-bus handler.
*
* Subscribes to every CoreEventType the v1 API surface emits and converts
* each emission into N rows in `webhook_deliveries` — one per active webhook
* each emission into N rows in `webhook_deliveries`: one per active webhook
* subscribed to (company_id, event_type). The dispatcher cron picks them
* up at next-minute boundary and POSTs to the receiver.
*
@@ -14,8 +14,8 @@
* runs inside Promise.allSettled in the bus (see lib/events/bus.ts), so
* a DB insert failure is logged but doesn't crash the emitter.
* - We capture `previous_attributes` only for events whose payload carries
* both a prior and current shape. Phase 6 PR-1 emits null for everything
* — adding the diff is a follow-up that requires touching each route's
* both a prior and current shape. Phase 6 PR-1 emits null for everything:
* adding the diff is a follow-up that requires touching each route's
* emit() call site to capture the prior row.
* - Service-role client because this code runs from the bus, outside any
* authenticated Supabase context.
@@ -35,7 +35,7 @@ const log = createLogger('webhooks/handler')
* MCP telemetry events and internal-only flows (event_log writes, etc.) are
* deliberately excluded.
*
* Adding a new event type to this set is a public-API change — bump
* Adding a new event type to this set is a public-API change: bump
* API_V1_VERSION + add to the changelog when you do.
*/
const PUBLIC_WEBHOOK_EVENTS = new Set<CoreEventType>([
@@ -69,7 +69,7 @@ let registered = false
/**
* Subscribe the webhook handler to every event in PUBLIC_WEBHOOK_EVENTS.
* Idempotent — safe to call from ensureInitialized() across hot reloads.
* Idempotent: safe to call from ensureInitialized() across hot reloads.
*/
export function registerWebhookHandler(): void {
if (registered) return
@@ -78,7 +78,7 @@ export function registerWebhookHandler(): void {
for (const eventType of PUBLIC_WEBHOOK_EVENTS) {
eventBus.on(eventType, async (payload) => {
// payload type depends on eventType but every variant carries
// companyId — the only field we structurally need here.
// companyId: the only field we structurally need here.
const companyId = (payload as { companyId?: string }).companyId
if (!companyId) {
// Surface as an error: every CoreEvent payload variant types
@@ -86,7 +86,7 @@ export function registerWebhookHandler(): void {
// bug that silently breaks webhook delivery for that event. Logging
// at error level ensures it shows up in monitoring rather than
// disappearing into routine warn-noise.
log.error('event missing companyId — webhook fanout skipped', new Error('missing companyId'), { eventType })
log.error('event missing companyId: webhook fanout skipped', new Error('missing companyId'), { eventType })
return
}
@@ -108,7 +108,7 @@ export function registerWebhookHandler(): void {
/**
* Drop fields from the in-process event payload that have no value to an
* external webhook receiver. Currently strips:
* - userId: an internal Supabase auth.users.id UUID — no value to the
* - userId: an internal Supabase auth.users.id UUID: no value to the
* receiver, identifies the gnubok-side actor not the resource. The
* companyId stays (it's the tenant scope, useful for multi-tenant
* receivers).
@@ -156,7 +156,7 @@ async function fanOutToWebhooks(args: {
if (!webhooks || webhooks.length === 0) return
// Synthesise a correlation id for the fanout batch. The event bus is
// async — by the time we reach here the originating route's request
// async: by the time we reach here the originating route's request
// context is gone, so we can't recover the live request_id. A fresh
// 'whfan_<uuid>' keeps the BFNAR 2013:2 kap 8 § behandlingshistorik
// requirement satisfied (the column is never NULL on a fresh insert)