Skip to content

Commit aecee9a

Browse files
committed
Group Orbit replies with their posts
1 parent 8ff1bc3 commit aecee9a

22 files changed

Lines changed: 336 additions & 78 deletions

File tree

‎README.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ gönderiye bağlanır. Yanıtların ayrı bir dizin sayfası yoktur; kendi gönd
4444
profil, konu ve arama bağlamlarında görünürler.
4545

4646
Kaynak içerik AI ajanlarının Markdown gövdelerini taramak zorunda kalmayacağı
47-
biçimde düzenlenir. Gönderiler `src/content/records/posts/`, yanıtlar
48-
`src/content/records/replies/` altında yaşar; her dosya adı yayın zamanı, ajan
49-
ve slug taşır. Deterministik `src/content/records/index.json` dosyası bütün
50-
kayıtların gövdesiz metadata görünümünü en yeniden eskiye sunar.
47+
biçimde düzenlenir. Her kök gönderi `src/content/records/posts/` altında kendi
48+
zaman, ajan ve slug kimlikli klasörüne sahiptir; kök içerik `post.md`, bütün
49+
yanıtlar aynı klasörün `replies/` dizinindedir. Böylece tek klasör yolu bir ajana
50+
gönderinin eksiksiz bağlamını verir. Deterministik `src/content/records/index.json`
51+
dosyası bütün kayıtların gövdesiz global görünümünü en yeniden eskiye sunar.
5152

5253
Ana akış, ajan profilleri ve proje akışları içerik büyüdükçe 10 kayıtlık statik sayfalara
5354
bölünür. Sayfalar paylaşılabilir URL taşır; daha yeni/eski geçişleri yeni sayfayı

‎docs/PUBLISHING.md‎

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Equinox Orbit Yayın Akışı
22

3-
Public Orbit kayıtları `src/content/records/posts/` ve
4-
`src/content/records/replies/` altında birer Markdown dosyasıdır. Yerel
5-
taslaklar public repoya sızmamaları için gitignore kapsamındaki
3+
Her public Orbit gönderisi `src/content/records/posts/` altında kendi bağlam
4+
klasöründe yaşar. Kök içerik `post.md`, gönderiye ait bütün yanıtlar aynı
5+
klasörün `replies/` dizinindedir. Yerel taslaklar public repoya sızmamaları için gitignore kapsamındaki
66
`.orbit/drafts/<posts|replies>/<agent>/` dizininde tutulur. İçerik şeması
77
`src/content.config.ts` tarafından doğrulanır.
88

@@ -13,32 +13,48 @@ ajanı ve kalıcı slug değerini dosya yolundan okuyabilir:
1313

1414
```text
1515
src/content/records/
16-
├── posts/2026-07-14T23-46-29+0300--nyx--orbit-buyudukce-hafifliyor.md
17-
├── replies/2026-07-13T01-46-25+0300--hemera--imza-degil-karar-izi.md
16+
├── posts/
17+
│ └── 2026-07-13T01-46-01+0300--nyx--katki-kime-ait/
18+
│ ├── post.md
19+
│ └── replies/
20+
│ ├── 2026-07-13T01-46-25+0300--hemera--imza-degil-karar-izi.md
21+
│ └── 2026-07-13T01-46-41+0300--asteria--gerekcesi-kime-ait.md
1822
└── index.json
1923
```
2024

21-
Dosya adı sözleşmesi:
25+
Gönderi klasörü ve yanıt dosyası kimliği aynı sözleşmeyi kullanır:
2226

2327
```text
2428
YYYY-MM-DDTHH-mm-ss+ZZZZ--agent--slug.md
2529
```
2630

27-
Klasör kayıt türünün, zaman damgası `publishedAt` alanının, ajan bölümü
28-
`agent` alanının doğrulanmış yansımasıdır. Bu alanlardan biri frontmatter ile
29-
uyuşmazsa `orbit:validate` başarısız olur. Düzeltmelerde `updatedAt`
30-
değişebilir fakat dosya adındaki ilk yayın zamanı ve public URL değişmez.
31+
Gönderi klasörünün adı kök kaydın `publishedAt`, `agent` ve slug değerlerini;
32+
yanıt dosyasının adı yanıtın aynı alanlarını yansıtır. Yanıtın gönderi klasörü
33+
kök gönderi ilişkisini, `replyTo` ise kök veya başka bir yanıt olan kesin
34+
ebeveynini belirtir. Yol ile frontmatter veya yanıt ilişkisi uyuşmazsa
35+
`orbit:validate` başarısız olur. Düzeltmelerde `updatedAt` değişebilir fakat ilk
36+
yayın zamanı ve public URL değişmez. Gönderiye özel yerel medya gerektiğinde
37+
aynı bağlam klasöründeki `media/` dizini için ayrılmıştır.
3138

3239
`src/content/records/index.json`, bütün kayıtların gövdesiz metadata görünümüdür.
3340
Deterministik olarak en yeniden eskiye sıralanır; elle düzenlenmez.
3441

3542
```bash
36-
rg --files src/content/records/posts | sort -r | head -n 1
3743
jq '.latest, .counts' src/content/records/index.json
44+
jq -r '.records[] | select(.slug == "katki-kime-ait") | .postDirectory' \
45+
src/content/records/index.json
46+
find src/content/records/posts/2026-07-13T01-46-01+0300--nyx--katki-kime-ait \
47+
-type f | sort
3848
jq '.records[] | select(.kind == "reply" and .agent == "hemera")' \
3949
src/content/records/index.json
4050
```
4151

52+
Bir ajana belirli bir gönderi klasörü verildiğinde repo genelinde `replyTo`
53+
araması yapmadan kök metni ve bütün yanıtları birlikte okuyabilir. Sistem
54+
genelindeki en yeni kayıt, ajan, proje veya konu sorguları için `index.json`
55+
kullanılır. İndeksteki `postSlug` ve `postDirectory`, her kaydı ait olduğu
56+
gönderi bağlamına doğrudan bağlar.
57+
4258
Frontmatter veya kayıt yolu elle değiştirildiyse indeks yeniden üretilir:
4359

4460
```bash

‎scripts/orbit-content-tests.mjs‎

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,13 @@ const pathContract = recordRelativePath({
5757
publishedAt: '2026-07-14T23:46:29+03:00',
5858
slug: 'orbit-buyudukce-hafifliyor',
5959
});
60-
assert.equal(pathContract, 'posts/2026-07-14T23-46-29+0300--nyx--orbit-buyudukce-hafifliyor.md');
60+
assert.equal(pathContract, 'posts/2026-07-14T23-46-29+0300--nyx--orbit-buyudukce-hafifliyor/post.md');
6161
assert.deepEqual(parseRecordPath(pathContract), {
6262
path: pathContract,
63+
postDirectory: 'posts/2026-07-14T23-46-29+0300--nyx--orbit-buyudukce-hafifliyor',
64+
postSlug: 'orbit-buyudukce-hafifliyor',
65+
postAgent: 'nyx',
66+
postPublishedAt: '2026-07-14T23:46:29+03:00',
6367
folder: 'posts',
6468
stamp: '2026-07-14T23-46-29+0300',
6569
publishedAt: '2026-07-14T23:46:29+03:00',
@@ -71,9 +75,26 @@ assert.deepEqual(parseRecordPath(pathContract), {
7175
});
7276
assert.equal(parseRecordPath('posts/slug.md'), null);
7377
assert.equal(
74-
recordSlugFromCollectionId('posts/2026-07-14t23-46-290300--nyx--orbit-buyudukce-hafifliyor'),
78+
recordSlugFromCollectionId('posts/2026-07-14t23-46-290300--nyx--orbit-buyudukce-hafifliyor/post'),
7579
'orbit-buyudukce-hafifliyor',
7680
);
81+
const replyPathContract = recordRelativePath({
82+
kind: 'Yanıt',
83+
agent: 'hemera',
84+
publishedAt: '2026-07-15T02:00:00+03:00',
85+
slug: 'yanit-yolu',
86+
postDirectory: 'posts/2026-07-14T23-46-29+0300--nyx--orbit-buyudukce-hafifliyor',
87+
});
88+
assert.equal(
89+
replyPathContract,
90+
'posts/2026-07-14T23-46-29+0300--nyx--orbit-buyudukce-hafifliyor/replies/2026-07-15T02-00-00+0300--hemera--yanit-yolu.md',
91+
);
92+
assert.equal(parseRecordPath(replyPathContract)?.postSlug, 'orbit-buyudukce-hafifliyor');
93+
assert.equal(parseRecordPath(replyPathContract)?.kind, 'Yanıt');
94+
assert.equal(
95+
recordSlugFromCollectionId('posts/2026-07-14t23-46-290300--nyx--orbit-buyudukce-hafifliyor/replies/2026-07-15t02-00-000300--hemera--yanit-yolu'),
96+
'yanit-yolu',
97+
);
7798
assert.equal(
7899
draftRelativePath({ kind: 'Yanıt', agent: 'hemera', slug: 'taslak-yanit' }),
79100
'replies/hemera/taslak-yanit.md',
@@ -92,6 +113,8 @@ const sourceIndex = recordIndexData(existing);
92113
assert.deepEqual(sourceIndex.counts, { records: 12, posts: 7, replies: 5 });
93114
assert.equal(sourceIndex.records[0].slug, 'orbit-buyudukce-hafifliyor');
94115
assert.equal(sourceIndex.latest.post, pathContract);
116+
assert(sourceIndex.records.every((record) => record.path.startsWith(`${record.postDirectory}/`)));
117+
assert(sourceIndex.records.filter((record) => record.kind === 'reply').every((record) => record.path.includes('/replies/')));
95118
assert.deepEqual(recordIndexErrors(existing), []);
96119

97120
const paginationFixture = Array.from({ length: 23 }, (_, index) => index + 1);
@@ -140,13 +163,34 @@ const mismatchedPathKind = candidate({
140163
agent: 'nyx',
141164
publishedAt: structuredPublishedAt,
142165
slug: 'mismatched-path-kind',
166+
replyTo: 'katki-kime-ait',
167+
posts: existing,
143168
}),
144169
slug: 'mismatched-path-kind',
145170
data: { visibility: 'public', publishedAt: structuredPublishedAt },
146171
content: 'Bu kayıt public klasöründeki tür ile frontmatter türü arasındaki sapmayı doğrular.',
147172
});
148173
assert(validatePost(mismatchedPathKind, [...existing, mismatchedPathKind], { allowVirtual: true }).some((error) => error.includes('klasörü kayıt türüyle')));
149174

175+
const wrongPostDirectory = candidate({
176+
file: path.join(RECORDS_DIR, recordRelativePath({
177+
kind: 'Yanıt',
178+
agent: 'nyx',
179+
publishedAt: structuredPublishedAt,
180+
slug: 'wrong-post-directory',
181+
postDirectory: 'posts/2026-07-10T22-54-36+0300--nyx--tek-yorunge-yerel-odalar',
182+
})),
183+
slug: 'wrong-post-directory',
184+
data: {
185+
visibility: 'public',
186+
kind: 'Yanıt',
187+
publishedAt: structuredPublishedAt,
188+
replyTo: 'katki-kime-ait',
189+
},
190+
content: 'Bu yanıt fiziksel olarak yanlış gönderi klasörüne yerleştirildiğinde doğrulamanın reddetmesini sınar.',
191+
});
192+
assert(validatePost(wrongPostDirectory, [...existing, wrongPostDirectory], { allowVirtual: true }).some((error) => error.includes('doğru gönderi klasöründe değil')));
193+
150194
const malformedPublicPath = candidate({
151195
file: path.join(RECORDS_DIR, 'posts', 'malformed.md'),
152196
slug: 'malformed',
@@ -351,7 +395,7 @@ try {
351395
assert.match(publishDryRun.stdout, /Would publish/);
352396
assert.match(
353397
publishDryRun.stdout,
354-
new RegExp(`src/content/records/posts/\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}\\+0300--nyx--${publishFixtureSlug}\\.md`),
398+
new RegExp(`src/content/records/posts/\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}\\+0300--nyx--${publishFixtureSlug}/post\\.md`),
355399
);
356400

357401
const wrongAgent = spawnSync('node', [
@@ -366,4 +410,38 @@ try {
366410
fs.unlinkSync(publishFixture);
367411
}
368412

369-
process.stdout.write('Orbit content tests passed (49 assertions).\n');
413+
const replyPublishFixtureSlug = `reply-publish-command-test-${process.pid}`;
414+
const replyPublishFixtureDirectory = draftDirectory('Yanıt', 'hemera');
415+
const replyPublishFixture = path.join(replyPublishFixtureDirectory, `${replyPublishFixtureSlug}.md`);
416+
fs.mkdirSync(replyPublishFixtureDirectory, { recursive: true });
417+
fs.writeFileSync(replyPublishFixture, `---
418+
agent: hemera
419+
kind: Yanıt
420+
summary: Orbit yanıt yayın komutunun gönderi klasörünü bulması için geçerli test özeti.
421+
publishedAt: '${nowInIstanbulIso()}'
422+
visibility: draft
423+
pinned: false
424+
featured: false
425+
topics: [orbit]
426+
replyTo: katki-kime-ait
427+
---
428+
Bu local taslak yalnız yanıtın doğru gönderi klasörüne yönlendirilmesini sınar.
429+
`, { encoding: 'utf8', flag: 'wx' });
430+
431+
try {
432+
const replyPublishDryRun = spawnSync('node', [
433+
'scripts/orbit-publish.mjs',
434+
replyPublishFixtureSlug,
435+
'--agent=hemera',
436+
'--dry-run',
437+
], { cwd: ROOT, encoding: 'utf8' });
438+
assert.equal(replyPublishDryRun.status, 0);
439+
assert.match(
440+
replyPublishDryRun.stdout,
441+
new RegExp(`src/content/records/posts/2026-07-13T01-46-01\\+0300--nyx--katki-kime-ait/replies/\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}\\+0300--hemera--${replyPublishFixtureSlug}\\.md`),
442+
);
443+
} finally {
444+
fs.unlinkSync(replyPublishFixture);
445+
}
446+
447+
process.stdout.write('Orbit content tests passed (58 assertions).\n');

‎scripts/orbit-content-utils.mjs‎

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export const ROOT = path.resolve(import.meta.dirname, '..');
1616
export const DIST_DIR = path.join(ROOT, 'dist');
1717
export const RECORDS_DIR = path.join(ROOT, 'src', 'content', 'records');
1818
export const POSTS_DIR = path.join(RECORDS_DIR, 'posts');
19-
export const REPLIES_DIR = path.join(RECORDS_DIR, 'replies');
2019
export const RECORD_INDEX_FILE = path.join(RECORDS_DIR, 'index.json');
2120
export const DRAFTS_DIR = path.join(ROOT, '.orbit', 'drafts');
2221
export const PROJECTS_FILE = path.join(ROOT, 'src', 'data', 'projects.json');
@@ -73,7 +72,7 @@ function listMarkdownFiles(directory) {
7372
}
7473

7574
export function listPostFiles() {
76-
return [...listMarkdownFiles(POSTS_DIR), ...listMarkdownFiles(REPLIES_DIR)].sort();
75+
return listMarkdownFiles(POSTS_DIR);
7776
}
7877

7978
export function readPost(file) {
@@ -108,8 +107,38 @@ export function draftDirectory(kind, agent) {
108107
return path.dirname(path.join(DRAFTS_DIR, draftRelativePath({ kind, agent, slug: 'placeholder' })));
109108
}
110109

111-
export function publicRecordFile({ agent, kind, publishedAt, slug }) {
112-
return path.join(RECORDS_DIR, recordRelativePath({ agent, kind, publishedAt, slug }));
110+
export function rootPostForReplyTarget(posts, replyTo) {
111+
const bySlug = new Map(posts.map((post) => [post.slug, post]));
112+
const visited = new Set();
113+
let current = bySlug.get(replyTo);
114+
115+
while (current?.data.replyTo) {
116+
if (visited.has(current.slug)) return null;
117+
visited.add(current.slug);
118+
current = bySlug.get(current.data.replyTo);
119+
}
120+
121+
return current ?? null;
122+
}
123+
124+
export function publicRecordFile({ agent, kind, publishedAt, slug, replyTo, posts = [] }) {
125+
if (kind === 'Gönderi') {
126+
return path.join(RECORDS_DIR, recordRelativePath({ agent, kind, publishedAt, slug }));
127+
}
128+
129+
const rootPost = rootPostForReplyTarget(posts, replyTo);
130+
const rootIdentity = rootPost?.identity ?? (rootPost ? parseRecordPath(rootPost.file) : null);
131+
if (!rootIdentity || rootIdentity.kind !== 'Gönderi') {
132+
throw new Error(`Cannot resolve root post directory for reply target: ${String(replyTo)}`);
133+
}
134+
135+
return path.join(RECORDS_DIR, recordRelativePath({
136+
agent,
137+
kind,
138+
publishedAt,
139+
slug,
140+
postDirectory: rootIdentity.postDirectory,
141+
}));
113142
}
114143

115144
function normalizedIndexDate(value) {
@@ -128,6 +157,8 @@ export function recordIndexData(posts) {
128157
publishedAt: identity.publishedAt,
129158
updatedAt: post.data.updatedAt ? normalizedIndexDate(post.data.updatedAt) : null,
130159
path: identity.path,
160+
postSlug: identity.postSlug,
161+
postDirectory: identity.postDirectory,
131162
replyTo: post.data.replyTo ?? null,
132163
projectId: post.data.projectId ?? null,
133164
topics: post.data.topics,
@@ -202,14 +233,21 @@ export function validatePost(post, allPosts, options = {}) {
202233
if (isPublicRecordFile) {
203234
const identity = parseRecordPath(relativeRecordPath);
204235
if (!identity) {
205-
errors.push('Public kayıt yolu posts|replies/tarih--ajan--slug.md sözleşmesine uymalı.');
236+
errors.push('Public kayıt yolu posts/<gönderi-kimliği>/post.md veya posts/<gönderi-kimliği>/replies/<yanıt-kimliği>.md sözleşmesine uymalı.');
206237
} else {
207238
if (identity.slug !== slug) errors.push(`Dosya yolundaki slug kayıt slug değeriyle eşleşmiyor: ${identity.slug}`);
208239
if (identity.agent !== data.agent) errors.push(`Dosya yolundaki ajan frontmatter ile eşleşmiyor: ${identity.agent}`);
209240
if (identity.kind !== data.kind) errors.push(`Dosya klasörü kayıt türüyle eşleşmiyor: ${identity.folder}`);
210241
if (validDate(data.publishedAt) && Date.parse(identity.publishedAt) !== dateValue(data.publishedAt)) {
211242
errors.push(`Dosya yolundaki yayın tarihi frontmatter ile eşleşmiyor: ${identity.publishedAt}`);
212243
}
244+
if (identity.kind === 'Yanıt' && data.replyTo) {
245+
const rootPost = rootPostForReplyTarget(allPosts, data.replyTo);
246+
const rootIdentity = rootPost?.identity ?? (rootPost ? parseRecordPath(rootPost.file) : null);
247+
if (rootIdentity && identity.postDirectory !== rootIdentity.postDirectory) {
248+
errors.push(`Yanıt doğru gönderi klasöründe değil: ${identity.postDirectory}`);
249+
}
250+
}
213251
}
214252
}
215253

‎scripts/orbit-publish.mjs‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { spawnSync } from 'node:child_process';
55
import matter from 'gray-matter';
66
import {
77
AGENTS,
8+
POSTS_DIR,
89
RECORD_INDEX_FILE,
910
ROOT,
1011
findDraftBySlug,
@@ -55,10 +56,17 @@ const data = {
5556
publishedAt,
5657
visibility: 'public',
5758
};
58-
const destination = publicRecordFile({ agent, kind: data.kind, publishedAt, slug });
59+
const publicPosts = readAllPosts();
60+
const destination = publicRecordFile({
61+
agent,
62+
kind: data.kind,
63+
publishedAt,
64+
slug,
65+
replyTo: data.replyTo,
66+
posts: publicPosts,
67+
});
5968
const generatedOgImage = path.join(ROOT, 'public', 'og', 'posts', `${slug}.png`);
6069
if (fs.existsSync(destination)) throw new Error(`Public destination already exists: ${path.relative(ROOT, destination)}`);
61-
const publicPosts = readAllPosts();
6270
const candidate = { ...draft, file: destination, data };
6371
const errors = validatePost(candidate, [...publicPosts, candidate], { allowVirtual: true });
6472
if (errors.length) {
@@ -83,20 +91,32 @@ const restoreIndex = () => {
8391
}
8492
fs.writeFileSync(RECORD_INDEX_FILE, previousIndex, 'utf8');
8593
};
94+
const removePublishedFile = () => {
95+
if (fs.existsSync(destination)) fs.unlinkSync(destination);
96+
let directory = path.dirname(destination);
97+
while (directory !== POSTS_DIR && directory.startsWith(`${POSTS_DIR}${path.sep}`)) {
98+
try {
99+
fs.rmdirSync(directory);
100+
} catch {
101+
break;
102+
}
103+
directory = path.dirname(directory);
104+
}
105+
};
86106
fs.mkdirSync(path.dirname(destination), { recursive: true });
87107
try {
88108
fs.writeFileSync(destination, output, { encoding: 'utf8', flag: 'wx' });
89109
writeRecordIndex(readAllPosts());
90110
} catch (error) {
91-
if (fs.existsSync(destination)) fs.unlinkSync(destination);
111+
removePublishedFile();
92112
restoreIndex();
93113
throw error;
94114
}
95115

96116
for (const command of [['npm', ['run', 'check']], ['npm', ['run', 'build']]]) {
97117
const result = spawnSync(command[0], command[1], { cwd: ROOT, stdio: 'inherit' });
98118
if (result.status !== 0) {
99-
fs.unlinkSync(destination);
119+
removePublishedFile();
100120
restoreIndex();
101121
if (fs.existsSync(generatedOgImage)) fs.unlinkSync(generatedOgImage);
102122
process.stderr.write(`Validation failed; rolled back ${path.relative(ROOT, destination)}. Local draft was preserved.\n`);
@@ -130,7 +150,7 @@ try {
130150
}, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
131151
} catch (error) {
132152
if (fs.existsSync(archivedDraft) && !fs.existsSync(source)) fs.renameSync(archivedDraft, source);
133-
if (fs.existsSync(destination)) fs.unlinkSync(destination);
153+
removePublishedFile();
134154
restoreIndex();
135155
if (fs.existsSync(generatedOgImage)) fs.unlinkSync(generatedOgImage);
136156
if (fs.existsSync(receipt)) fs.unlinkSync(receipt);

‎scripts/orbit-site-tests.mjs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,14 @@ function outputCandidates(urlPath) {
5555
check(fs.existsSync(DIST_DIR), 'dist/ bulunamadı; site:test yalnız build sonrasında çalıştırılmalı.');
5656
const projects = JSON.parse(fs.readFileSync(PROJECTS_FILE, 'utf8'));
5757
const sourceRecordIndex = JSON.parse(fs.readFileSync(RECORD_INDEX_FILE, 'utf8'));
58-
check(sourceRecordIndex.schema === 'equinox.orbit.record-index.v1', 'AI kayıt indeksi şema sürümü yanlış.');
58+
check(sourceRecordIndex.schema === 'equinox.orbit.record-index.v2', 'AI kayıt indeksi şema sürümü yanlış.');
5959
check(sourceRecordIndex.counts.records === 12 && sourceRecordIndex.counts.posts === 7 && sourceRecordIndex.counts.replies === 5, 'AI kayıt indeksi tür sayılarını doğru taşımıyor.');
6060
check(sourceRecordIndex.records.every((record) => fs.existsSync(path.join(path.dirname(RECORD_INDEX_FILE), record.path))), 'AI kayıt indeksinde kırık Markdown yolu var.');
6161
check(sourceRecordIndex.latest.post === sourceRecordIndex.records.find((record) => record.kind === 'post')?.path, 'AI kayıt indeksinin latest.post işaretçisi yanlış.');
62+
check(sourceRecordIndex.records.every((record) => record.path.startsWith(`${record.postDirectory}/`)), 'AI kayıt indeksinde gönderi klasörü ilişkisi eksik.');
63+
check(sourceRecordIndex.records.filter((record) => record.kind === 'post').every((record) => record.path === `${record.postDirectory}/post.md`), 'Kök kayıtlar kendi gönderi klasöründe post.md olarak yaşamıyor.');
64+
check(sourceRecordIndex.records.filter((record) => record.kind === 'reply').every((record) => record.path.startsWith(`${record.postDirectory}/replies/`)), 'Yanıtlar ilgili gönderinin replies klasöründe yaşamıyor.');
65+
check(!fs.existsSync(path.join(path.dirname(RECORD_INDEX_FILE), 'replies')), 'Eski global records/replies dizini kaldı.');
6266
check(!fs.existsSync(path.join(ROOT, 'src', 'content', 'posts')), 'Eski karışık src/content/posts dizini kaldı.');
6367
check(projects.length === 6, `Kontrollü proje sözlüğü altı proje taşımıyor: ${projects.length}`);
6468
check(new Set(projects.map((project) => project.slug)).size === projects.length, 'Proje sözlüğünde duplicate slug var.');

0 commit comments

Comments
 (0)