From 2b5ba8a50e4f8b0dc0f5e40219e602889dd2e841 Mon Sep 17 00:00:00 2001
From: th-kim0823
Date: Thu, 14 May 2026 13:12:08 +0900
Subject: [PATCH] =?UTF-8?q?fix(sync):=20manifest.exported=5Fat=20=EC=A0=9C?=
=?UTF-8?q?=EA=B1=B0=20=E2=80=94=20no-op=20push=20=ED=9A=8C=ED=94=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
dogfood: 노트 변경이 0건이어도 자동 sync 가 매번 commit + push 를 생성.
원인은 manifest.json 의 exported_at timestamp 가 매 export 마다 갱신되어
git diff 가 항상 1줄 발생.
해결: composeManifest 의 exportedAt 입력 제거 + 출력 JSON 에서 필드 삭제.
이 필드는 ImportService 가 read 하지 않고 UI 표시도 없는 cosmetic 정보였음.
이제 노트 변경 있을 때만 commit/push 가 일어난다.
회귀 테스트: 같은 input 으로 두 번 호출 시 stable 출력 invariant 추가.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
src/main/services/ExportService.ts | 1 -
src/main/services/exportFormat.ts | 5 +++--
tests/unit/exportFormat.test.ts | 12 +++++++++---
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/src/main/services/ExportService.ts b/src/main/services/ExportService.ts
index de08d27..01cb527 100644
--- a/src/main/services/ExportService.ts
+++ b/src/main/services/ExportService.ts
@@ -136,7 +136,6 @@ export class ExportService {
totalBytes += Buffer.byteLength(indexJsonl, 'utf8');
const manifest = composeManifest({
- exportedAt: this.now().toISOString(),
noteCount: notes.length,
mediaCount
});
diff --git a/src/main/services/exportFormat.ts b/src/main/services/exportFormat.ts
index e0eb5cd..86f31d0 100644
--- a/src/main/services/exportFormat.ts
+++ b/src/main/services/exportFormat.ts
@@ -253,14 +253,15 @@ export function composeIndexJsonl(
// ---------------------------------------------------------------------------
export function composeManifest(input: {
- exportedAt: string;
noteCount: number;
mediaCount: number;
}): string {
+ // exported_at 필드 의도적 제외 — note 변경 없이도 git sync 가 매 호출마다
+ // timestamp 갱신 1줄 commit 을 만들어 history 노이즈와 불필요한 push 유발.
+ // import path 는 inkling_export_version 만 read 하므로 안전.
return JSON.stringify(
{
inkling_export_version: 1,
- exported_at: input.exportedAt,
note_count: input.noteCount,
media_count: input.mediaCount
},
diff --git a/tests/unit/exportFormat.test.ts b/tests/unit/exportFormat.test.ts
index ed7f611..bce1d67 100644
--- a/tests/unit/exportFormat.test.ts
+++ b/tests/unit/exportFormat.test.ts
@@ -230,16 +230,22 @@ describe('composeIndexJsonl', () => {
});
describe('composeManifest', () => {
- it('emits pretty JSON with required fields', () => {
+ it('emits pretty JSON with required fields (timestamp-free)', () => {
const m = composeManifest({
- exportedAt: '2026-04-26T00:00:00.000Z',
noteCount: 42,
mediaCount: 17
});
const obj = JSON.parse(m);
expect(obj.inkling_export_version).toBe(1);
- expect(obj.exported_at).toBe('2026-04-26T00:00:00.000Z');
expect(obj.note_count).toBe(42);
expect(obj.media_count).toBe(17);
+ // exported_at 필드 제거 — sync git history noise 방지.
+ expect(obj.exported_at).toBeUndefined();
+ });
+
+ it('두 번 호출 결과 stable (sync no-op invariant — 같은 input 이면 git diff 0)', () => {
+ const a = composeManifest({ noteCount: 5, mediaCount: 2 });
+ const b = composeManifest({ noteCount: 5, mediaCount: 2 });
+ expect(a).toBe(b);
});
});