Corrective Action: Sanity Write Pattern Lost After Context Compaction

Corrective Action: Sanity Write Pattern Lost After Context Compaction

Date: March 11, 2026 Category: Context Loss / Post-Compaction Regression Impact: ~8 wasted tool calls (4 failed attempts) to accomplish a 1-call operation. Token waste, user frustration, and execution delay on what should have been a trivial edit. Resolution Time: ~3 minutes of thrashing before success


Incident

What Happened

During a single session, V published an article to Sanity CMS using publish-article.ts (which internally uses @sanity/client with the website .env token, running from the apps/website/ directory as an ES module). Minutes later, after context compaction occurred, V was asked to patch the same article (change "Brian Ginsberg" to "Steve Ginsberg"). Despite having just published to Sanity successfully, V failed 4 times in sequence:

  1. Used the monorepo root .env token (read-only) — got 403 Insufficient Permissions
  2. Wrote a temp script to /tmp/ — module @sanity/client not found (wrong directory)
  3. Ran from monorepo root — same module not found
  4. Copied script to apps/website/ as .js — failed because package.json has "type": "module" (ESM), and the script used require() (CJS)
  5. Renamed to .cjs — finally succeeded

Timeline

Time Event
~11:01 CT Article published to Sanity via publish-article.ts (success, first try)
~11:30 CT Context compaction occurred (prior conversation compressed to summary)
~11:45 CT User asked to change name in article
~11:48 CT First attempt: wrong token (root .env) — 403
~11:49 CT Second attempt: script in /tmp/ — module not found
~11:50 CT Third attempt: still in wrong CWD — module not found
~11:51 CT Fourth attempt: .js in ESM package — require() not defined
~11:52 CT Fifth attempt: .cjs extension — success

Root Cause

Context compaction destroyed operational knowledge that had been demonstrated minutes earlier.

The summary generated during compaction captured what was accomplished ("published article to Sanity") but not how it was accomplished. Specifically, these four critical details were lost:

  1. Token selection: The website .env has the write-capable SANITY_API_TOKEN. The monorepo root .env has a read-only token. The publish script uses dotenvx which auto-loads from apps/website/.env.
  2. Working directory: @sanity/client resolves from apps/website/node_modules/, not from the monorepo root or /tmp/.
  3. Module system: apps/website/package.json declares "type": "module". Any .js file in that directory tree is treated as ESM. CJS requires the .cjs extension.
  4. Existing tooling: The scripts/sanity/query.js tool handles reads. For writes, the writeClient in apps/website/src/lib/sanity/client.ts is the established pattern. An ad-hoc script was the wrong approach entirely.

Category: Context Loss / Post-Compaction Regression

This is a systemic issue, not a one-time mistake. Every context compaction discards operational "how-to" knowledge. The compaction summary format prioritizes what happened (good for continuing a conversation) but not how to do it again (needed when the same tool must be used again in the same session).

The deeper failure: V did not consult any reference material before attempting the operation. The Sanity write pattern is documented in:

  • memory/MEMORY.md → Build Notes → "Sanity write client requires SANITY_API_TOKEN env var"
  • Dewey index → 630 (Sanity CLI), 113 (Sanity client)
  • apps/website/src/lib/sanity/client.ts (the actual write client)

V treated this as a "I'll just write a quick script" problem instead of checking how the system already handles Sanity writes.


Fix Applied

Immediate Resolution

Renamed the patch script from .js to .cjs, ran from apps/website/ directory using the website .env token. Article patched successfully (4 spans modified, rev r3xPnhMhowrbQmj0TOotDJ).

What Should Have Happened

One command. No temp scripts. No trial and error:

cd apps/website && SANITY_API_TOKEN=$(grep SANITY_API_TOKEN .env | tr -d '\r' | cut -d= -f2) node -e '...'

Or better: use npx tsx (already available in the website) to run a quick ESM script that imports the existing writeClient from src/lib/sanity/client.ts.

Or best: a permanent scripts/sanity/patch.js tool that handles document mutations the way scripts/sanity/query.js handles reads.

Verification

Article at valuefirstteam.com/media/articles/ten-minutes-to-a-register confirmed to show "Steve Ginsberg" throughout.


Prevention Measures

1. MEMORY.md Critical Lesson

Add to Critical Lessons:

Sanity writes require the website .env token, the apps/website/ working directory, and .cjs extension (or tsx) for scripts. The root .env token is read-only. @sanity/client only resolves from apps/website/node_modules/. The package uses ESM ("type": "module"), so CJS scripts need .cjs extension. Before writing ANY ad-hoc Sanity mutation, check scripts/sanity/query.js for the established pattern and apps/website/src/lib/sanity/client.ts for the write client. Fixed Mar 11 after 4 failed attempts to patch an article that was published successfully minutes earlier in the same session.

2. Self-Correction Trigger

Add to vf-self-correction.md Infrastructure Triggers:

| Writing an ad-hoc script to mutate Sanity data without checking existing tooling | Reinventing the wheel. Check scripts/sanity/query.js (reads) and apps/website/src/lib/sanity/client.ts (writes) first. Run from apps/website/ with the website .env token. Never use the root .env for Sanity writes. |

3. Permanent Sanity Patch Tool

Create scripts/sanity/patch.js — a companion to query.js that handles document mutations. This eliminates the need for ad-hoc scripts entirely. Pattern: node scripts/sanity/patch.js <documentId> <jsonPatchOps>.

4. Post-Compaction Checklist

When resuming after context compaction, before attempting any tool/API operation that was performed earlier in the session: read the relevant Dewey index section and check for existing tooling. Do not rely on compaction summaries for operational how-to knowledge. They capture outcomes, not procedures.


Lessons

Context compaction summaries are conversation continuity tools, not operational playbooks. When a session crosses a compaction boundary and needs to repeat an operation, the correct response is to consult the system's documented patterns (Dewey index, memory files, existing scripts) rather than attempting to reconstruct the approach from memory. The 4-failure sequence here was not a knowledge gap — it was a process gap. V knew Sanity writes were possible (had just done one) but didn't pause to look up how the system does them.

The broader principle: when you know something works but don't remember how, look it up. Don't guess. The Dewey index, memory files, and codebase exist specifically so that operational knowledge survives context loss. Use them.


Related Incidents

Date Incident Pattern
Mar 4 NEVER use curl to query Sanity Same root: ad-hoc approach instead of using established scripts/sanity/query.js
Mar 8 CRLF token infrastructure Token selection confusion (which .env has which token)
Mar 4 esbuild em dash / block comment bug Module system confusion (CJS vs ESM in the website package)