Follow-up to 99bf533 from code review:
- The AffectedRows == 0 guard in Store{Invoice,Reminder}DocumentFileAsync
was dead: fds__setInvoiceFile / fds__setReminderFile run SET NOCOUNT ON,
so ExecuteNonQuery returns -1 whether or not the UPDATE ... WHERE [file]
IS NULL matched. Both procs already return the updated Id through their
OUTPUT clause, so read that result set (getSQLDatatable_async) instead:
empty rowset = no-op store, logged, and the blob upload / FileCreated
event are skipped rather than reporting unpersisted bytes as success.
- inv/rdoc's regenerate path built the download name from the raw
DocumentName; a user-typed title with '/' truncated FileInfo.Name.
SafeDocumentFileName strips editor markup and every invalid filename
char, and the path no longer goes through FileInfo at all.
- Reminder counterpart tests for the store-file failure branches.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Decisions
This folder holds Architecture Decision Records (ADRs) — short, immutable records of a specific technical choice, why it was made, and what it implies going forward.
What belongs here
A decision, not a how-to. If it answers "why do we do X this way, and what
else did we consider," it's a decision. If it explains "how subsystem X
currently works," that belongs in ../Concepts
instead (and a decision often triggers a concept doc to be created/updated).
File naming
NNNN-kebab-case-title.md, four-digit zero-padded, sequential across the
whole folder (0001-..., 0002-...). Never reuse or renumber.
Required YAML frontmatter
Every decision file starts with:
---
status: Accepted # Proposed | Accepted | Superseded
date: 2026-07-03 # date the decision was accepted
applyTo: # glob(s) — files/areas this decision governs
- "Fuchs/Notifications/**"
supersededBy: "" # filename of the decision that replaced this one, if any
---
Agents (Claude, Copilot, Codex) must scan the YAML frontmatter of every file
in this folder first (cheap — no need to read the body) and only read the
full body of decisions whose applyTo glob matches the files they're about
to touch, or whose subject is otherwise clearly relevant to the task. This
keeps decision-following cheap even as the folder grows.
Body template
# NNNN — Title
## Context
What problem/situation forced a choice.
## Decision
What was decided, stated plainly.
## Consequences
What this implies for future code — constraints, follow-ups, trade-offs
accepted knowingly.
## Alternatives considered
Options that were rejected and why (optional but preferred).
Rules
- Decisions are immutable once
Accepted. Do not edit the Decision/ Consequences of an existing file to reverse it. Instead, write a new decision, set itsapplyTo/subject accordingly, and set the old file'sstatus: Superseded+supersededBy: NNNN-new-file.md. - Follow existing decisions. Before implementing anything in an area
covered by an
Accepteddecision, read it and conform to it. If you believe a decision is wrong, raise it with the user rather than silently deviating. - Capture new decisions as they happen. Whenever the user (or the code
you're writing) settles a non-obvious architectural or cross-cutting
choice — not a routine implementation detail — add a decision here in the
same change, and create/update the matching concept doc in
../Concepts.