[object Object]

← back to Dw Pitch Followup

fix(notes): FileMaker note-save silently no-op'd — write rep-1 bare key + read-back verify

229cd17c1379f4098c2f7e10e20b050d1be55d20 · 2026-07-15 10:56:36 -0700 · Steve

Root cause: repeating-field rep 1 must be written as the BARE field name ("Internal notes"), not "Internal notes(1)" — the Data API accepts the (1) form with success code 0 but silently ignores it, so notes logged as saved but never persisted (verified: a real save was empty on read-back). Also updateRecord reports committed:true after any non-erroring PATCH without confirming the field changed. Fix: write bare key AND read the record back, throwing if the note is not actually present — never report a save we cannot see.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit 229cd17c1379f4098c2f7e10e20b050d1be55d20
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 15 10:56:36 2026 -0700

    fix(notes): FileMaker note-save silently no-op'd — write rep-1 bare key + read-back verify
    
    Root cause: repeating-field rep 1 must be written as the BARE field name ("Internal notes"), not "Internal notes(1)" — the Data API accepts the (1) form with success code 0 but silently ignores it, so notes logged as saved but never persisted (verified: a real save was empty on read-back). Also updateRecord reports committed:true after any non-erroring PATCH without confirming the field changed. Fix: write bare key AND read the record back, throwing if the note is not actually present — never report a save we cannot see.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 lib/fm-notes.js | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/lib/fm-notes.js b/lib/fm-notes.js
index 2402700..da30738 100644
--- a/lib/fm-notes.js
+++ b/lib/fm-notes.js
@@ -104,14 +104,22 @@ export async function addNote(account, note) {
   const prevRO = process.env.FM_READONLY; // defensive: never let a stray read-only flag block or linger
   delete process.env.FM_READONLY;
   try {
-    const res = await client.updateRecord(
+    // REP-1 WRITE FIX: a repeating field's rep 1 must be set via the BARE field name. FileMaker's
+    // Data API accepts "Internal notes(1)" with success code 0 but SILENTLY IGNORES it — the write
+    // no-ops while reporting committed:true (this caused notes to log-as-saved but never persist).
+    await client.updateRecord(
       'invoice', INVOICE_LAYOUT, cur.invoiceRecordId,
-      { 'Internal notes(1)': toFm(next) },
+      { 'Internal notes': toFm(next) },
       { dryRun: false },
     );
-    if (!res.committed) throw new Error(res.note || 'FileMaker did not commit the note');
+    // VERIFY: updateRecord's committed:true only means "PATCH didn't error", not "field changed".
+    // Read the record back and confirm the note is actually there — never report a save we can't see.
+    const check = await getNotes(account);
+    if (!check || !String(check.notes || '').includes(line)) {
+      throw new Error('FileMaker accepted the write but the note did not persist — NOT saved');
+    }
+    return { ...cur, notes: check.notes, added: line };
   } finally {
     if (prevRO !== undefined) process.env.FM_READONLY = prevRO;
   }
-  return { ...cur, notes: next, added: line };
 }

← 109d6a3 Make invoice # on the note chip + notes editor clickable — o  ·  back to Dw Pitch Followup  ·  chore: lint, refactor (dedup repaint per unique email), v0.4 dcb29c4 →