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(auth): SoD acknowledge on stage+approve keys + agent:write scope for memory tools
Segregation of duties on API keys is now warn + explicit acknowledgement
(not block): minting a key with any staging write scope AND
pending_operations:approve returns 409 API_KEY_SOD_CONFLICT unless the
caller re-POSTs with acknowledge_sod: true. The acknowledgement is recorded
(sod_acknowledged_at / sod_acknowledged_by) for an auditable risk acceptance
(ISO 27001:2022 A.5.3 / BFNAR 2013:2). The create UI surfaces an inline
warning and an explicit confirm dialog before submitting the ack — the
default "all scopes ticked" create routes through that path.
Also introduces the agent:write scope and maps the previously-UNMAPPED memory
tools gnubok_remember_fact / gnubok_forget_fact to it. Because unmapped tools
were callable by any key, the migration grandfathers agent:write onto every
existing non-revoked key with an explicit scope list so nothing regresses;
new keys must opt in. agent:write is deliberately excluded from the default
grants and is NOT a staging scope (no SoD conflict with approve).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(db): enforce both-or-neither on the SoD acknowledgement pair
Review finding (Greptile P2): sod_acknowledged_at/sod_acknowledged_by were
independently nullable, so a partial write could silently pass and undermine
the auditable risk acceptance (ISO 27001 A.5.3 / SOC 2 CC6.1). Adds a
paired-NULL CHECK constraint + pg-real coverage for both partial-write
directions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(auth)+feat(auth): compliance-review round — self-attestation documented, ack logged, SoD boundary assumption captured
- Migration header now states explicitly that the SoD acknowledgement is a
SELF-attestation by deliberate design (enskild firma has no second person;
the claude.ai approval flow needs stage+approve on one credential) — the
control objective is informed consent + audit record, not dual control.
- The acknowledge_sod=true path now emits a structured log.warn
(api_key.sod_acknowledged with key id/prefix, conflicting scope, scopes,
acknowledger, company) so the acceptance lands in the logging pipeline in
addition to the sod_acknowledged_* columns (ASVS V16.1.1).
- STAGING_SCOPES carries the documented system control (BFNAR 2013:2
systemdokumentation) for why agent:write is not a staging scope: memory
tools write advisory agent context and cannot stage räkenskapsinformation.
Dismissed as by-design/verified: hard-block and second-approver remediations
(user decision: warn + acknowledge); scope-update gap (the [id] route only
supports DELETE — scopes are immutable post-creation); session-auth concern
(withRouteContext is cookie+MFA only; API-key auth exists only on /api/v1
and MCP).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: re-trigger CI (Supabase Preview 502 infra hiccup)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>