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>
72 lines
4.0 KiB
Markdown
72 lines
4.0 KiB
Markdown
---
|
|
name: loop-vercel-errors
|
|
description: Loop that pulls recent production runtime errors from Vercel (via the Vercel MCP locally, or the Vercel API with a token), groups + dedupes them, files well-formed GitHub issues, and opens a fix PR only for clearly trivial/safe cases. Best run LOCALLY (the Vercel MCP is available there). Follows .claude/loops.md.
|
|
---
|
|
|
|
# loop-vercel-errors
|
|
|
|
**Goal:** every distinct, real production runtime error is tracked as a GitHub issue (deduped), and the
|
|
obviously-trivial ones have a proposed fix PR. **Never merge.** Read `.claude/loops.md` first.
|
|
|
|
Vercel project `erp-base` (`prj_zOvCFaOMXS166cUY5VYEGHKke00X`, team `team_WPj3QZgcSVRWZKcHJQB3wfv8`).
|
|
|
|
> **No error-aggregation service is wired up** (Sentry is NOT used: no `@sentry/*`, no config, despite
|
|
> leftover `SENTRY_*` names in `.env.local`/CLAUDE.md). The only source is **Vercel's own runtime logs /
|
|
> observability**, which have short retention: this loop sees a *recent window*, not full history.
|
|
> **Run it LOCALLY** so the Vercel MCP is available; the cloud routine is disabled (see step 1).
|
|
|
|
## 1. Fetch errors from Vercel
|
|
1. **Vercel MCP (primary, local runs):** `mcp__plugin_vercel_vercel__*`: authenticate if needed, then
|
|
list recent **production** deployments and pull runtime logs / observability / error events. This is
|
|
the clean path and is only present in a LOCAL session.
|
|
2. **Vercel API (if a token exists):** with a `VERCEL_TOKEN` (from vercel.com/account/tokens),
|
|
```bash
|
|
curl -s -H "Authorization: Bearer $VERCEL_TOKEN" \
|
|
"https://api.vercel.com/v6/deployments?projectId=prj_zOvCFaOMXS166cUY5VYEGHKke00X&teamId=team_WPj3QZgcSVRWZKcHJQB3wfv8&limit=5&target=production"
|
|
# then pull runtime logs for the latest prod deployment id
|
|
```
|
|
This is the only path that works in a **cloud** routine, and only if `VERCEL_TOKEN` is set as a
|
|
secret on the cloud env. `VERCEL_OIDC_TOKEN` in `.env.local` is short-lived and NOT a usable API token.
|
|
3. **Vercel CLI last resort:** `vercel logs <prod-deployment-url> --json` (v48 here; only a live tail).
|
|
|
|
If **no** source is reachable, STOP and report exactly which sources you tried and why each failed.
|
|
**Never invent errors.**
|
|
|
|
## 2. Group into distinct errors
|
|
Cluster raw log lines by **signature**: normalized message + top of stack + route. Drop noise (expected
|
|
4xx, aborted requests, health checks). For each cluster capture: signature, first/last seen, count,
|
|
sample stack, affected route/file.
|
|
|
|
## 3. Dedupe (mandatory, see loops.md)
|
|
Fingerprint = a stable hash of the normalized signature.
|
|
```bash
|
|
gh issue list --search "<fingerprint> in:body state:all" --json number,state
|
|
```
|
|
Open match → comment ("still occurring, N since <date>"). Closed + recurring → reopen. No match → file
|
|
new. **Cap 8 new issues/run**; log the rest.
|
|
|
|
## 4. File the issue
|
|
```
|
|
Title: [prod error] <short normalized message> (<route>)
|
|
Labels: loop:auto, loop:vercel, bug
|
|
Body:
|
|
**Signature:** … **Count / window:** … **First/last seen:** …
|
|
**Route/file:** app/…:line **Sample stack:** (fenced) **Vercel:** <deployment/log link if available>
|
|
**Likely cause:** <1-2 lines from reading the referenced code>
|
|
<!-- loop-fingerprint: <hash> -->
|
|
```
|
|
Point at the suspected `file:line` by reading the code the stack references.
|
|
|
|
## 5. Trivial fix only (propose-don't-merge), cap 2 PRs/run
|
|
Open a `loop/vercel-<hash>` fix PR **only** when the cause is unambiguous and low-risk (missing null
|
|
guard, unhandled `undefined`, bad env read with an obvious default, a narrow type fix). Anything
|
|
touching bookkeeping/money/migrations/auth → issue only, label `loop:needs-human`. Every fix passes the
|
|
**[`loop-verify`](../loop-verify/SKILL.md)** gate. PR body: `Closes #<issue>`.
|
|
|
|
## 6. Report
|
|
List: new issues filed, existing issues updated, fix PRs opened, which source worked, anything skipped.
|
|
```
|
|
|
|
> **Want proper error tracking?** Vercel runtime-log retention is short. For a real errors→tickets loop,
|
|
> wire an error sink (Sentry, Vercel Log Drains to a store, etc.) and update step 1 to read from it.
|