ec27228a8e
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>
68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
MCP_TOOL_CAPABILITY_MAP,
|
|
PAID_OPERATION_CAPABILITY_MAP,
|
|
PAID_CAPABILITIES,
|
|
CAPABILITY,
|
|
} from '../keys'
|
|
|
|
/**
|
|
* These maps are the contract that gates the paid MCP/agent path (dispatch +
|
|
* commit). Locking the exact entries is the guard against a future paid
|
|
* external-service tool silently bypassing the paywall: mirrors the
|
|
* TOOL_SCOPE_MAP assertions in the mcp-server tests.
|
|
*/
|
|
/**
|
|
* MCP tools that invoke a paid capability directly (no stage→commit round-trip),
|
|
* so they are gated at DISPATCH only and have no commit-time (operation-map)
|
|
* counterpart. gnubok_upload_document runs Bedrock OCR inline via
|
|
* extractInvoiceFields: it never stages a pending_operation.
|
|
*/
|
|
const DISPATCH_ONLY_MCP_TOOLS = new Set<string>(['gnubok_upload_document'])
|
|
|
|
describe('MCP_TOOL_CAPABILITY_MAP', () => {
|
|
it('gates exactly the paid MCP tools (3 external-service staging tools + the AI OCR tool)', () => {
|
|
expect(MCP_TOOL_CAPABILITY_MAP).toEqual({
|
|
gnubok_send_invoice: CAPABILITY.email_send,
|
|
gnubok_vat_declaration_submit: CAPABILITY.skatteverket,
|
|
gnubok_agi_submit: CAPABILITY.skatteverket,
|
|
// Dispatch-only AI tool: inline Bedrock OCR, no staged operation.
|
|
gnubok_upload_document: CAPABILITY.ai,
|
|
})
|
|
})
|
|
|
|
it('only maps tools to PAID capabilities', () => {
|
|
for (const key of Object.values(MCP_TOOL_CAPABILITY_MAP)) {
|
|
expect(PAID_CAPABILITIES).toContain(key)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('PAID_OPERATION_CAPABILITY_MAP', () => {
|
|
it('gates exactly the three paid pending-operation types', () => {
|
|
expect(PAID_OPERATION_CAPABILITY_MAP).toEqual({
|
|
send_invoice: CAPABILITY.email_send,
|
|
submit_vat_declaration: CAPABILITY.skatteverket,
|
|
submit_agi: CAPABILITY.skatteverket,
|
|
})
|
|
})
|
|
|
|
it('only maps operations to PAID capabilities', () => {
|
|
for (const key of Object.values(PAID_OPERATION_CAPABILITY_MAP)) {
|
|
expect(PAID_CAPABILITIES).toContain(key)
|
|
}
|
|
})
|
|
|
|
it('covers the same capabilities as the STAGING MCP tools (dispatch ↔ commit parity)', () => {
|
|
// Parity applies to staging tools only: an op that can be staged via MCP OR
|
|
// approved in the UI must be gated on both transports. Dispatch-only tools
|
|
// (inline AI OCR) have no commit counterpart and are excluded.
|
|
const stagingMcpCaps = new Set(
|
|
Object.entries(MCP_TOOL_CAPABILITY_MAP)
|
|
.filter(([tool]) => !DISPATCH_ONLY_MCP_TOOLS.has(tool))
|
|
.map(([, cap]) => cap),
|
|
)
|
|
expect(new Set(Object.values(PAID_OPERATION_CAPABILITY_MAP))).toEqual(stagingMcpCaps)
|
|
})
|
|
})
|