faf-mcp 4.1.0 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <!-- faf: faf-mcp | TypeScript | mcp | The Interop MCP for Context — the Cursor / IDE Edition. Persistent context for Cursor, VS Code, and every MCP-compatible IDE. IANA-registered application/vnd.faf+yaml. Start with "Use FAF". -->
2
- <!-- faf: doc=changelog | latest=v4.1.0 | canonical=project.faf | family=FAF -->
2
+ <!-- faf: doc=changelog | latest=v4.1.2 | canonical=project.faf | family=FAF -->
3
3
 
4
4
  # Changelog
5
5
 
@@ -8,6 +8,23 @@ All notable changes to faf-mcp will be documented in this file.
8
8
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
9
9
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
10
10
 
11
+ ## [4.1.2] - 2026-11-05 — The Instructions Edition
12
+
13
+ **faf_write can no longer overwrite the birth certificate or your context files, and faf_dna never issues a second one.**
14
+
15
+ ### Fixed
16
+ - **`faf_write` refuses `.faf-dna`** in every spelling and route (case, `..`, absolute paths, symlinks, symlinked folders, `.faf-dna/x`), with or without `overwrite: true` ([#110](https://github.com/Wolfe-Jam/faf-mcp/pull/110)).
17
+ - **Existing `project.faf`, `*.faf`, `CLAUDE.md` and `AGENTS.md` need `overwrite: true`.** New files write as before. Writes stay inside the project folder, never in `.git/`, checked on the resolved real path, and land atomically with file permissions kept.
18
+ - **`faf_dna` issues one birth certificate, once.** Concurrent calls create exactly one. If `.faf-dna` is missing but git history shows it existed (any branch, the reflog, a stash, a linked worktree, a `--shared` clone's source), it refuses and prints the restore steps. If history can't be checked, it refuses rather than guess ([#110](https://github.com/Wolfe-Jam/faf-mcp/pull/110), [#111](https://github.com/Wolfe-Jam/faf-mcp/pull/111)).
19
+ - **Restore steps** list the latest version first: `git log --all --reflog --diff-filter=AM --oneline -- .faf-dna`, then `git checkout <sha> -- .faf-dna`.
20
+
21
+ ## [4.1.1] - 2026-10-28 — The Instructions Edition
22
+
23
+ **One rule for project questions: read project.faf first.**
24
+
25
+ ### Changed
26
+ - **Server instructions: a rule for project questions.** Line 2 now reads "For questions about the project (stack, goal, commands), read project.faf first and answer from it. faf_score shows what is missing; faf_doctor says why." It replaces "Start with faf_doctor or faf_score…", which models read as a hint: in a 3-run pilot on a repo where project.faf and package.json disagree, Claude Code and Grok Build read package.json first 3 of 3 with the old line, and project.faf first 3 of 3 (Claude) and 2 of 3 (Grok) with the new one. 712 characters.
27
+
11
28
  ## [4.1.0] - 2026-10-27 — The Instructions Edition
12
29
 
13
30
  **faf-mcp now tells the model how its tools fit together: server instructions in initialize (642 characters), plus faf-cli 8.5.0, which knows a skills repo when it sees one.** 413 tests pass, 0 fail.
package/CLAUDE.md CHANGED
@@ -25,12 +25,12 @@ The Interop MCP for Context — the Cursor / IDE Edition. Persistent context for
25
25
  - **What:** The Cursor / IDE Edition — persistent context for Cursor, VS Code, and every MCP-compatible IDE — 29 MCP tools (15 essential shown by default), interop with AGENTS.md, .cursorrules, GEMINI.md
26
26
  - **Why:** Eliminate the 20-minute AI context tax — give AI instant project understanding in 30 seconds
27
27
  - **Where:** npm · MCP Registry · any MCP-compatible IDE — people get it how they wish
28
- - **When:** Production/Stable — v4.1.0 The Instructions Edition, WJTTC certified
28
+ - **When:** Production/Stable — v4.1.2 The Instructions Edition, WJTTC certified
29
29
  - **How:** npx faf-mcp or npm install -g faf-mcp, then add to your MCP config
30
30
 
31
31
  ---
32
32
 
33
- *STATUS: SYNC ACTIVE — 2026-10-27T21:01:01.358Z*
33
+ *STATUS: SYNC ACTIVE — 2026-11-05T14:10:38.504Z*
34
34
  <!-- faf:end -->
35
35
 
36
36
  ## Working here
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  **.FAF Context** is the MCP server for the IDE side of FAF. One `project.faf` in your repo, and every AI tool's context file is authored from it — AGENTS.md, .cursorrules, GEMINI.md, CLAUDE.md — and scored, so you know exactly where to focus. It runs locally over stdio on the same faf-cli the terminal uses. The FAF ecosystem it belongs to has comfortably passed 100k downloads across npm and PyPI ([live count](https://faf.one/downloads)).
21
21
 
22
- ⭐ Bookmarks it for you, helps other devs find it too.
22
+ ⭐ [Star it on GitHub](https://github.com/Wolfe-Jam/faf-mcp): bookmarks it for you, helps other devs find it too.
23
23
 
24
24
  [![CI](https://github.com/Wolfe-Jam/faf-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/Wolfe-Jam/faf-mcp/actions/workflows/ci.yml)
25
25
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -28,7 +28,17 @@
28
28
 
29
29
  ---
30
30
 
31
- ## What's New in 4.1.0 — The Instructions Edition
31
+ ## What's New in 4.1.2 — The Instructions Edition
32
+
33
+ **faf_write can no longer overwrite the birth certificate or your context files, and faf_dna never issues a second one.** `.faf-dna` is refused in every spelling and route, existing context files need `overwrite: true`, and `faf_dna` refuses rather than reissue when git shows a certificate existed.
34
+
35
+ ## 4.1.1
36
+
37
+ **One rule for project questions: read project.faf first.**
38
+
39
+ - **Server instructions, line 2.** "For questions about the project (stack, goal, commands), read project.faf first and answer from it. faf_score shows what is missing; faf_doctor says why." It replaces "start with `faf_doctor` or `faf_score`", which agents read as a hint. In a 3-run pilot on a repo where project.faf and package.json disagree, Claude Code read project.faf first 3 of 3 and Grok Build 2 of 3 with the new line, against 0 of 3 for both with the old one. 712 characters.
40
+
41
+ ## 4.1.0 — The Instructions Edition
32
42
 
33
43
  **faf-mcp now tells the model how its tools fit together: server instructions in initialize (642 characters), plus faf-cli 8.5.0, which knows a skills repo when it sees one.**
34
44
 
@@ -225,7 +235,7 @@ The 15 Core tools, shown by default:
225
235
  | `faf_gemini` | Import GEMINI.md into `project.faf`, or write it from `project.faf` |
226
236
  | `faf_git` | Author a `project.faf` from a public GitHub repo URL |
227
237
 
228
- **+14 more with `FAF_TOOLS=all`:** `faf_status` · `faf_what` · `faf_guide` · `faf_debug` · `faf_clear` · `faf_list` · `faf_read` (read a file within the allowed roots: cwd, the OS temp dir, or `FAF_ALLOWED_ROOTS`) · `faf_write` (write a file within the same roots) · `faf_readme` · `faf_human_add` · `faf_quick` · `faf_formats` · `faf_dna` · `faf_conductor`
238
+ **+14 more with `FAF_TOOLS=all`:** `faf_status` · `faf_what` · `faf_guide` · `faf_debug` · `faf_clear` · `faf_list` · `faf_read` (read a file within the allowed roots: cwd, the OS temp dir, or `FAF_ALLOWED_ROOTS`) · `faf_write` (write a file within cwd or `FAF_ALLOWED_ROOTS`; never `.faf-dna` or `.git/`; an existing `*.faf`, `CLAUDE.md` or `AGENTS.md` needs `overwrite: true`) · `faf_readme` · `faf_human_add` · `faf_quick` · `faf_formats` · `faf_dna` · `faf_conductor`
229
239
 
230
240
  **Built on faf-cli.** Every tool composes the bundled [faf-cli](https://www.npmjs.com/package/faf-cli) in-process — the same scorer, the same renderers, the same block injector the CLI uses. Nothing shells out to a `faf` on your PATH.
231
241
 
@@ -26,6 +26,12 @@ export declare const fafWriteTool: Tool;
26
26
  * Handle faf_read tool execution
27
27
  */
28
28
  export declare function handleFafRead(args: any): Promise<CallToolResult>;
29
+ /**
30
+ * Context files faf_write will not replace unless the caller passes
31
+ * `overwrite: true`: any `*.faf` (project.faf, .faf), CLAUDE.md and AGENTS.md.
32
+ * Case-insensitive, so a case-insensitive filesystem cannot slip past.
33
+ */
34
+ export declare function isProtectedContextFile(p: string): boolean;
29
35
  /**
30
36
  * Handle faf_write tool execution
31
37
  */
@@ -37,9 +37,12 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.fileHandlers = exports.fafWriteTool = exports.fafReadTool = exports.PathValidator = void 0;
39
39
  exports.handleFafRead = handleFafRead;
40
+ exports.isProtectedContextFile = isProtectedContextFile;
40
41
  exports.handleFafWrite = handleFafWrite;
41
42
  const fs = __importStar(require("fs/promises"));
42
43
  const path = __importStar(require("path"));
44
+ const fsSync = __importStar(require("fs"));
45
+ const crypto = __importStar(require("crypto"));
43
46
  const safe_path_1 = require("../utils/safe-path");
44
47
  /**
45
48
  * Security validator for file paths
@@ -103,7 +106,7 @@ exports.fafReadTool = {
103
106
  */
104
107
  exports.fafWriteTool = {
105
108
  name: 'faf_write',
106
- description: 'Write content to any file on the local filesystem',
109
+ description: 'Write a file within the project root (cwd, or FAF_ALLOWED_ROOTS when set). Paths outside it, inside .git/, and .faf-dna (the birth certificate) are always refused. An existing *.faf, CLAUDE.md or AGENTS.md is not replaced unless overwrite is true. Writes via temp file + rename.',
107
110
  inputSchema: {
108
111
  type: 'object',
109
112
  properties: {
@@ -111,6 +114,10 @@ exports.fafWriteTool = {
111
114
  type: 'string',
112
115
  description: 'Absolute or relative file path to write'
113
116
  },
117
+ overwrite: {
118
+ type: 'boolean',
119
+ description: 'Set true to replace an existing *.faf, CLAUDE.md or AGENTS.md. Without it, faf_write refuses and leaves the file unchanged. Never applies to .faf-dna.'
120
+ },
114
121
  content: {
115
122
  type: 'string',
116
123
  description: 'Content to write to the file'
@@ -182,18 +189,107 @@ async function handleFafRead(args) {
182
189
  };
183
190
  }
184
191
  }
192
+ /**
193
+ * Context files faf_write will not replace unless the caller passes
194
+ * `overwrite: true`: any `*.faf` (project.faf, .faf), CLAUDE.md and AGENTS.md.
195
+ * Case-insensitive, so a case-insensitive filesystem cannot slip past.
196
+ */
197
+ function isProtectedContextFile(p) {
198
+ const base = path.basename(p).toLowerCase();
199
+ return base.endsWith('.faf') || base === 'claude.md' || base === 'agents.md';
200
+ }
201
+ /**
202
+ * Write `content` to `target` via a temp file in the same directory, then
203
+ * rename (replace) or link (no-clobber) it into place. A reader never sees a
204
+ * half-written file. With `noClobber`, an existing target is never replaced,
205
+ * even if it appears between the caller's check and this write: link() fails
206
+ * with EEXIST atomically.
207
+ */
208
+ async function atomicWrite(target, content, noClobber) {
209
+ const dir = path.dirname(target);
210
+ const tmp = path.join(dir, `.${path.basename(target)}.faf-tmp-${process.pid}-${crypto.randomBytes(4).toString('hex')}`);
211
+ await fs.writeFile(tmp, content, { encoding: 'utf8', flag: 'wx' });
212
+ try {
213
+ if (!noClobber) {
214
+ // Keep the replaced file's permission bits (0755 stays 0755). The temp
215
+ // file was created with the default mode, so copy the old one across.
216
+ await keepMode(target, tmp);
217
+ await fs.rename(tmp, target);
218
+ return;
219
+ }
220
+ try {
221
+ await fs.link(tmp, target);
222
+ }
223
+ catch (err) {
224
+ if (errCode(err) === 'EEXIST')
225
+ throw err;
226
+ // Filesystem without hard links: exclusive copy is still no-clobber.
227
+ await fs.copyFile(tmp, target, fsSync.constants.COPYFILE_EXCL);
228
+ }
229
+ }
230
+ finally {
231
+ await fs.rm(tmp, { force: true });
232
+ }
233
+ }
234
+ /** Copy `from`'s permission bits onto `to`. No-op when `from` does not exist. */
235
+ async function keepMode(from, to) {
236
+ let mode;
237
+ try {
238
+ mode = (await fs.stat(from)).mode & 0o7777;
239
+ }
240
+ catch (err) {
241
+ if (errCode(err) === 'ENOENT')
242
+ return;
243
+ throw err;
244
+ }
245
+ await fs.chmod(to, mode);
246
+ }
247
+ function errCode(err) {
248
+ return typeof err === 'object' && err !== null && 'code' in err ? String(err.code) : undefined;
249
+ }
250
+ function existsNoFollow(p) {
251
+ try {
252
+ fsSync.lstatSync(p);
253
+ return true;
254
+ }
255
+ catch {
256
+ return false;
257
+ }
258
+ }
259
+ function refuseExisting(filePath) {
260
+ return {
261
+ content: [{
262
+ type: 'text',
263
+ text: `❌ Refused: ${filePath} already exists and is a protected context file ` +
264
+ `(*.faf, CLAUDE.md, AGENTS.md). faf_write did not change it.\n` +
265
+ `To replace it, call faf_write again with overwrite: true. ` +
266
+ `To update project.faf or CLAUDE.md without losing hand-written content, ` +
267
+ `use the dedicated faf tools instead.`
268
+ }],
269
+ isError: true
270
+ };
271
+ }
185
272
  /**
186
273
  * Handle faf_write tool execution
187
274
  */
188
275
  async function handleFafWrite(args) {
189
276
  const startTime = Date.now();
190
277
  try {
191
- const { path: rawPath, content } = args;
192
- // Confine to the project root(s) before any write — closes arbitrary file
193
- // write outside the project (e.g. overwriting ~/.bashrc).
278
+ const input = (args ?? {});
279
+ const rawPath = input.path;
280
+ const content = input.content;
281
+ const overwrite = input.overwrite === true;
282
+ if (typeof content !== 'string') {
283
+ return {
284
+ content: [{ type: 'text', text: '❌ content must be a string' }],
285
+ isError: true
286
+ };
287
+ }
288
+ // Confine to the project root before any write: cwd only (or an explicit
289
+ // FAF_ALLOWED_ROOTS), never .git/, never .faf-dna, real path resolved.
194
290
  let filePath;
195
291
  try {
196
- filePath = (0, safe_path_1.confineFileOp)(rawPath);
292
+ filePath = (0, safe_path_1.confineWriteOp)(rawPath);
197
293
  }
198
294
  catch (err) {
199
295
  if (err instanceof safe_path_1.PathConfinementError) {
@@ -215,24 +311,36 @@ async function handleFafWrite(args) {
215
311
  isError: true
216
312
  };
217
313
  }
314
+ const protectedFile = isProtectedContextFile(filePath) || isProtectedContextFile(String(rawPath));
315
+ const noClobber = protectedFile && !overwrite;
316
+ if (noClobber && existsNoFollow(filePath)) {
317
+ return refuseExisting(filePath);
318
+ }
218
319
  // Create directory if needed
219
320
  const dir = path.dirname(filePath);
220
321
  await fs.mkdir(dir, { recursive: true });
221
322
  // Write file with timeout
222
- await Promise.race([
223
- fs.writeFile(filePath, content, 'utf8'),
224
- new Promise((_, reject) => setTimeout(() => reject(new Error('Write timeout (30s)')), 30000))
225
- ]);
323
+ try {
324
+ await Promise.race([
325
+ atomicWrite(filePath, content, noClobber),
326
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Write timeout (30s)')), 30000).unref?.())
327
+ ]);
328
+ }
329
+ catch (err) {
330
+ if (noClobber && errCode(err) === 'EEXIST')
331
+ return refuseExisting(filePath);
332
+ throw err;
333
+ }
226
334
  const duration = Date.now() - startTime;
227
335
  return {
228
336
  content: [{
229
337
  type: 'text',
230
- text: `✅ Successfully wrote ${contentSize} bytes to ${path.resolve(filePath)}`
338
+ text: `✅ Successfully wrote ${contentSize} bytes to ${filePath}`
231
339
  }],
232
340
  metadata: {
233
341
  duration_ms: duration,
234
342
  bytes_written: contentSize,
235
- file_path: path.resolve(filePath),
343
+ file_path: filePath,
236
344
  message: `✅ Write completed in ${duration}ms`
237
345
  }
238
346
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fileHandler.js","sourceRoot":"","sources":["../../../src/handlers/fileHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fileHandler.js","sourceRoot":"","sources":["../../../src/handlers/fileHandler.ts"],"names":[],"mappings":"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"}
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FafToolHandler = void 0;
37
37
  const fileHandler_1 = require("./fileHandler");
38
38
  const fs = __importStar(require("fs"));
39
+ const git_history_1 = require("../utils/git-history");
39
40
  const os = __importStar(require("os"));
40
41
  const pathModule = __importStar(require("path"));
41
42
  const faf_file_finder_js_1 = require("../utils/faf-file-finder.js");
@@ -295,7 +296,7 @@ class FafToolHandler {
295
296
  },
296
297
  {
297
298
  name: 'faf_write',
298
- description: 'Write a file within the allowed roots (cwd, the OS temp dir, or FAF_ALLOWED_ROOTS). Paths outside them are refused.',
299
+ description: 'Write a file within the project root (cwd, or FAF_ALLOWED_ROOTS when set). Paths outside it, inside .git/, and .faf-dna (the birth certificate) are always refused. An existing *.faf, CLAUDE.md or AGENTS.md is not replaced unless overwrite is true. Writes via temp file + rename.',
299
300
  annotations: {
300
301
  title: 'Write Project File',
301
302
  readOnlyHint: false,
@@ -309,6 +310,10 @@ class FafToolHandler {
309
310
  type: 'string',
310
311
  description: 'Absolute or relative file path to write'
311
312
  },
313
+ overwrite: {
314
+ type: 'boolean',
315
+ description: 'Set true to replace an existing *.faf, CLAUDE.md or AGENTS.md. Without it, faf_write refuses and leaves the file unchanged. Never applies to .faf-dna.'
316
+ },
312
317
  content: {
313
318
  type: 'string',
314
319
  description: 'Content to write to the file'
@@ -2053,6 +2058,34 @@ Call these MCP tools:
2053
2058
  }]
2054
2059
  };
2055
2060
  }
2061
+ // A .faf-dna that was ever committed is a birth that already happened.
2062
+ // Reissuing would replace the certificate; restore it from git instead.
2063
+ const history = (0, git_history_1.gitEverTracked)(dnaPath);
2064
+ if (history.status === 'found') {
2065
+ const committed = history.where;
2066
+ return {
2067
+ content: [{
2068
+ type: 'text',
2069
+ text: `🧬 FAF DNA Journey\n\n❌ .faf-dna is missing, but git shows it existed (${committed}).\nThe birth certificate is issued once and is not reissued.\n\n💡 Restore it from git instead:\n git log --all --reflog --diff-filter=AM --oneline -- .faf-dna (the first line is the latest version, reflog included)\n git checkout <sha> -- .faf-dna (restore that version)`
2070
+ }],
2071
+ isError: true
2072
+ };
2073
+ }
2074
+ // Fail closed: if git history could not be read in full, a deleted
2075
+ // certificate cannot be ruled out, so none is issued.
2076
+ if (history.status === 'unknown') {
2077
+ return {
2078
+ content: [{
2079
+ type: 'text',
2080
+ text: `🧬 FAF DNA Journey\n\n❌ .faf-dna is missing and git history could not be checked (${history.reason}).\nThe birth certificate is issued once and is not reissued, so faf_dna did not create one.\n\n💡 If .faf-dna existed before, restore it from git:\n git log --all --reflog --diff-filter=AM --oneline -- .faf-dna (the first line is the latest version, reflog included)\n git checkout <sha> -- .faf-dna (restore that version)\n💡 If this really is a brand-new project, create the first one with faf-cli:\n faf init`
2081
+ }],
2082
+ isError: true
2083
+ };
2084
+ }
2085
+ // Outside git there is no history to check: say so in the reply.
2086
+ const noHistoryNote = history.status === 'not-a-repo'
2087
+ ? `\n\n⚠️ Not a git repository: no history could be checked, so a lost .faf-dna cannot be told apart from a new project. If this project had a .faf-dna before, replace this one with the restored original.`
2088
+ : '';
2056
2089
  // .faf exists but no DNA - create initial DNA. Real scorer — same
2057
2090
  // one faf_score reads — never a local pseudo-score (v3.0 fix).
2058
2091
  const { readFafRaw, scoreFafYaml } = await faf_cli_bridge_js_1.fafCli;
@@ -2075,11 +2108,28 @@ Call these MCP tools:
2075
2108
  ],
2076
2109
  format: 'faf-dna-v1'
2077
2110
  };
2078
- fs.writeFileSync(dnaPath, JSON.stringify(dna, null, 2));
2111
+ // Exclusive create (O_EXCL): the birth certificate is written once.
2112
+ // If another call created it in the meantime, that one stands and is
2113
+ // the one reported — never a second certificate.
2114
+ try {
2115
+ fs.writeFileSync(dnaPath, JSON.stringify(dna, null, 2), { flag: 'wx' });
2116
+ }
2117
+ catch (err) {
2118
+ if (err?.code !== 'EEXIST')
2119
+ throw err;
2120
+ const existing = JSON.parse(fs.readFileSync(dnaPath, 'utf-8'));
2121
+ const bc = existing.birthCertificate ?? {};
2122
+ return {
2123
+ content: [{
2124
+ type: 'text',
2125
+ text: `🧬 FAF DNA Journey\n\n🎫 Birth Certificate already exists — not reissued.\n\n📊 Birth DNA: ${bc.birthDNA ?? 0}%\n📅 Born: ${String(bc.born ?? '').split('T')[0]}\n🎫 Certificate: ${bc.certificate ?? 'unknown'}`
2126
+ }]
2127
+ };
2128
+ }
2079
2129
  return {
2080
2130
  content: [{
2081
2131
  type: 'text',
2082
- text: `🧬 FAF DNA Journey\n\n🐣 Birth Certificate Created!\n\n📊 Birth DNA: ${currentScore}%\n📅 Born: ${new Date().toISOString().split('T')[0]}\n🎫 Certificate: ${dna.birthCertificate.certificate}\n\n💡 Your journey begins here! Run faf_auto or faf_go to grow.`
2132
+ text: `🧬 FAF DNA Journey\n\n🐣 Birth Certificate Created!\n\n📊 Birth DNA: ${currentScore}%\n📅 Born: ${new Date().toISOString().split('T')[0]}\n🎫 Certificate: ${dna.birthCertificate.certificate}\n\n💡 Your journey begins here! Run faf_auto or faf_go to grow.${noHistoryNote}`
2083
2133
  }]
2084
2134
  };
2085
2135
  }
@@ -2102,7 +2152,12 @@ Call these MCP tools:
2102
2152
  const now = new Date().toISOString();
2103
2153
  dna.current = { ...(dna.current ?? {}), score: live, lastSync: now };
2104
2154
  milestones.push({ type: 'sync', score: live, date: now, version: dna.current.version ?? 'v1.0.0' });
2105
- fs.writeFileSync(dnaPath, JSON.stringify(dna, null, 2));
2155
+ // birthCertificate is carried over unchanged; temp + rename so a
2156
+ // crash mid-write cannot truncate the certificate.
2157
+ const tmpDna = `${dnaPath}.tmp-${process.pid}-${Date.now()}`;
2158
+ fs.writeFileSync(tmpDna, JSON.stringify(dna, null, 2), { flag: 'wx' });
2159
+ fs.chmodSync(tmpDna, fs.statSync(dnaPath).mode & 0o7777); // keep the certificate's mode
2160
+ fs.renameSync(tmpDna, dnaPath);
2106
2161
  currentScore = live;
2107
2162
  }
2108
2163
  }