Tool text that lied to agents:
- gnubok_create_voucher pointed at gnubok_reverse_entry, which does not
exist; the tool is gnubok_reverse_journal_entry. A scan of server.ts,
skills/, prompts/ and structured-errors.ts found no other phantom names.
- gnubok_reverse_journal_entry said reversal_date defaults to today; the
executor passes undefined and reverseEntry() uses the original entry
date (same as the dashboard). Description now states that. No behaviour
change.
- gnubok_get_vacation_balance promised an estimated semesterloneskuld in
SEK but returned none. The tool now returns estimated_liability_sek
using the same BFNAR 2016:10 day valuation as the year-close and the v1
vacation-balance route (dayValueSek exported from semesterberedning),
floored at zero for overdrawn balances. Descriptions trimmed so the
tools/list payload stays under the 60.7K-token ceiling (60,696 after).
- gnubok_create_invoice said the invoice number is assigned at approval;
it is assigned on send or mark-as-sent (ensureInvoiceNumber).
- gnubok_convert_invoice: "har redan makuleras" -> "har redan makulerats".
- lib/entitlements/keys.ts comment claimed bank_sync has no MCP tool while
the map right below gates gnubok_connect_bank on it.
Version: MCP serverInfo.version, the extension version and /api/health all
hardcoded '1.0.0', so clients could not tell deploys apart. They now share
currentAppVersion() (commit SHA prefix inlined at build), resolved once at
module load so the definitions layer stays deterministic, with '1.0.0' as
the self-hosted fallback so Docker healthchecks keep a value. serverInfo is
not part of tools/list, so the catalog payload is unaffected by this part.
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(supabase): stop server clients leaking a 30s refresh ticker per request
`autoRefreshToken` defaults to true in supabase-js, and off-browser
@supabase/auth-js starts the refresh ticker unconditionally:
// in non-browser environments the refresh token ticker runs always
this.startAutoRefresh()
That is a setInterval firing every 30 s. It calls unref(), so the process
still exits, tests pass, and Vercel never notices because the process is
torn down long before the tickers accumulate. But unref() does not make a
timer collectable: it stays registered in the event loop and remains a GC
root for its callback, which closes over the GoTrueClient, the
SupabaseClient, and the whole request scope around it.
A long-running self-hosted instance therefore leaks one timer plus one
entire request graph (socket, IncomingMessage, ServerResponse, headers,
route context: ~100 kB) per client constructed. One died of "JavaScript
heap out of memory" after 42 h, the last 24 of them completely idle. The
heap snapshot showed 445 retained request graphs and ~1050 Timeouts in
the 30 000 ms bucket, retained via `autoRefreshTicker`, and the rate
matched the traffic exactly: the Docker healthcheck polls /api/health
every 30 s and the webhook dispatch cron runs every minute, so
3 clients/min x 148 min = 444.
- new lib/supabase/service-client.ts: createServiceRoleClient() applies
SERVER_AUTH_OPTIONS, spread LAST so a caller passing its own auth block
cannot re-enable the ticker
- 22 call sites migrated; only booking-templates/sync/cron had ever
passed the options itself
- guard 9 in no-new-antipatterns.mjs fails CI on any new value import of
supabase-js's createClient outside the wrapper; type-only imports are
fine. Verified to fail on a deliberate regression and pass once fixed
- browser clients untouched: a signed-in tab genuinely needs the refresh,
and lib/supabase/client.ts is built on createBrowserClient anyway
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(checks): catch namespace imports in the leaky-supabase-client guard
The guard only matched named imports, so
import * as sb from '@supabase/supabase-js'
sb.createClient(url, key)
reached createClient through member access without ever naming it, and
passed. Verified against the real script before and after: the shape is
flagged now, and `import type * as sb` still passes.
Namespace value imports are treated as leaky outright rather than tracking
member access, which keeps the check a regex over source text with no new
dependency.
Review also suggested excluding *.test.tsx alongside *.test.ts. Skipped: the
repo has no .test.tsx files, and all four sibling checks in this file use
`.test.ts`. Diverging in one of them would read as an accident; if such files
appear, all four should change together.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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>
* feat(api): implement caching and logging in health check endpoint
- Added in-memory caching for health check responses to reduce load on Postgres.
- Introduced logging for error handling in health check.
- Updated response structure to exclude error details from public responses.
feat(api): enhance OAuth consent UI and scope handling
- Improved consent UI to reflect exact requested scopes and added better user guidance.
- Updated scope handling logic to ensure least-privilege access.
- Enhanced styling for better user experience and accessibility.
chore(docker): improve security and resource management in Docker setup
- Updated Docker Compose configuration to enforce read-only file systems and resource limits.
- Added health checks and logging options for better observability.
- Introduced optional Caddy reverse proxy for TLS termination.
fix(migrations): resolve ambiguity in create_company_with_owner function
- Dropped orphaned 3-arg overload of create_company_with_owner function.
- Recreated canonical 4-arg version with cash account seeding logic.
- Ensured proper permissions for function execution in Postgres.
* feat: enhance security checks for team membership in company creation