Skip to content

Commit 395f82e

Browse files
committed
Add Selene newsroom author support
Keep Asteria as the default newsroom author, add explicit Selene author selection, surface Selene in site metadata and author pages, and attribute the first Selene newsroom article correctly. Verified with npm run quality: 212 newsroom tests passed and Astro built 654 pages.
1 parent fdea526 commit 395f82e

15 files changed

Lines changed: 61 additions & 14 deletions

‎newsroom/docs/RUNBOOK.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ görünmez; 2'ye çıkılırsa geçerli davranış budur.
9999
newsroom/.venv/bin/newsroom publish --response yanit.json
100100
```
101101

102+
Varsayılan yayın imzası `Asteria AI`dır. Aynı editoryal akışı yetkili başka bir
103+
operatör çalıştırıyorsa imza açıkça seçilebilir:
104+
105+
```bash
106+
newsroom/.venv/bin/newsroom publish --response yanit.json --author "Selene AI"
107+
```
108+
109+
Yazar adı model yanıtının parçası değildir; operasyonel metadata olarak CLI
110+
tarafından belirlenir. Böylece içerik sözleşmesi değişmeden Asteria varsayılan
111+
kalır ve farklı bir operatör yanlışlıkla Asteria imzasıyla yayımlanmaz.
112+
102113
Yanıtı sözleşmeye karşı doğrular, markdown'ı yazar, hero'yu yerleştirir,
103114
denetimleri ve Astro build'i çalıştırır, dar kapsamlı commit atar.
104115

‎newsroom/newsroom/cli.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import sys
1818

1919
from newsroom.cycle import BRIEF_PATH, CycleState, prepare, publish
20+
from newsroom.publish import DEFAULT_AUTHOR, SUPPORTED_AUTHORS
2021

2122

2223
def _emit(payload: dict, *, ok: bool) -> int:
@@ -57,7 +58,7 @@ def _publish(args: argparse.Namespace) -> int:
5758
if isinstance(payload, dict) and "__error__" in payload:
5859
return _emit({"ok": False, "problems": [payload["__error__"]]}, ok=False)
5960

60-
report = publish(payload, build=not args.no_build, do_commit=not args.no_commit)
61+
report = publish(payload, build=not args.no_build, do_commit=not args.no_commit, author=args.author)
6162

6263
return _emit(
6364
{
@@ -105,10 +106,11 @@ def build_parser() -> argparse.ArgumentParser:
105106
)
106107
prepare_cmd.set_defaults(func=_prepare)
107108

108-
publish_cmd = sub.add_parser("publish", help="Asteria yanıtını yayına taşır")
109+
publish_cmd = sub.add_parser("publish", help="Editoryal operatör yanıtını yayına taşır")
109110
group = publish_cmd.add_mutually_exclusive_group(required=True)
110111
group.add_argument("--response", help="Yanıt JSON dosyası")
111112
group.add_argument("--stdin", action="store_true", help="Yanıtı stdin'den oku")
113+
publish_cmd.add_argument("--author", choices=SUPPORTED_AUTHORS, default=DEFAULT_AUTHOR, help=f"Yayın imzası (varsayılan: {DEFAULT_AUTHOR})")
112114
publish_cmd.add_argument("--no-build", action="store_true", help="Astro build adımını atla")
113115
publish_cmd.add_argument("--no-commit", action="store_true", help="Commit adımını atla")
114116
publish_cmd.set_defaults(func=_publish)

‎newsroom/newsroom/cycle.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from newsroom.brief import attach_text, build_brief, select_board
3030
from newsroom.ingest import collect
3131
from newsroom.live import load_live
32-
from newsroom.publish import PUBLISH_TZ, render, slugify, write
32+
from newsroom.publish import DEFAULT_AUTHOR, PUBLISH_TZ, SUPPORTED_AUTHORS, render, slugify, write
3333
from newsroom.screen import eligible
3434
from newsroom.sources import due_sources, load_sources
3535
from newsroom.verify import commit, verify
@@ -207,9 +207,14 @@ def publish(
207207
hero_dir: Path | None = None,
208208
build: bool = True,
209209
do_commit: bool = True,
210+
author: str = DEFAULT_AUTHOR,
210211
) -> PublishReport:
211212
"""Asteria yanıtını doğrular ve yayına taşır."""
212213
report = PublishReport()
214+
if author not in SUPPORTED_AUTHORS:
215+
report.problems.append(f"desteklenmeyen yazar: {author}")
216+
return report
217+
213218
source_brief = brief
214219
if source_brief is None:
215220
target = brief_path or BRIEF_PATH
@@ -265,6 +270,7 @@ def publish(
265270
hero_describes_selection=hero_result.stock_description is None,
266271
now=moment,
267272
slug=slug,
273+
author=author,
268274
)
269275

270276
try:

‎newsroom/newsroom/publish.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
# Yayın saati Türkiye saatiyle yazılır; mevcut arşivin tamamı bu biçimde.
2424
PUBLISH_TZ = ZoneInfo("Europe/Istanbul")
2525

26-
AUTHOR = "Asteria AI"
26+
DEFAULT_AUTHOR = "Asteria AI"
27+
SUPPORTED_AUTHORS = ("Asteria AI", "Selene AI")
2728
HERO_DIR = "/images/generated/equinox-haber"
2829

2930
# Türkçe harfler ASCII karşılıklarına eşlenir. `str.lower()` "İ" için birleşik
@@ -75,6 +76,7 @@ def render(
7576
hero_describes_selection: bool = True,
7677
now: datetime | None = None,
7778
slug: str | None = None,
79+
author: str = DEFAULT_AUTHOR,
7880
) -> str:
7981
"""Yayına girecek markdown dosyasının tam içeriğini üretir."""
8082
moment = (now or datetime.now(PUBLISH_TZ)).astimezone(PUBLISH_TZ)
@@ -103,7 +105,7 @@ def render(
103105
lines += [
104106
"isDraft: false",
105107
f"tags: {json.dumps(selection['tags'], ensure_ascii=False)}",
106-
f"author: {_scalar(AUTHOR)}",
108+
f"author: {_scalar(author)}",
107109
f"category: {_scalar(selection['category'])}",
108110
"breaking: false",
109111
"sources:",

‎newsroom/tests/test_cli.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ def test_publish_iki_kaynak_birlikte_verilemez(self) -> None:
3333
with pytest.raises(SystemExit):
3434
build_parser().parse_args(["publish", "--stdin", "--response", "x.json"])
3535

36+
def test_varsayilan_yazar_asteria(self) -> None:
37+
args = build_parser().parse_args(["publish", "--response", "x.json"])
38+
assert args.author == "Asteria AI"
39+
40+
def test_selene_yazari_secilebilir(self) -> None:
41+
args = build_parser().parse_args(["publish", "--response", "x.json", "--author", "Selene AI"])
42+
assert args.author == "Selene AI"
43+
3644
def test_varsayilan_secim_sayisi_bir(self) -> None:
3745
assert build_parser().parse_args(["prepare"]).select_count == 1
3846

‎newsroom/tests/test_publish.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import yaml
1111

1212
from newsroom.publish import (
13-
AUTHOR,
13+
DEFAULT_AUTHOR,
1414
MAX_SLUG_LENGTH,
1515
PUBLISH_TZ,
1616
hero_path_for,
@@ -119,12 +119,15 @@ def test_frontmatter_semaya_uygun(self) -> None:
119119
front = _frontmatter(_rendered())
120120
assert front["title"] == SELECTION["title"]
121121
assert front["category"] == "Ekonomi"
122-
assert front["author"] == AUTHOR
122+
assert front["author"] == DEFAULT_AUTHOR
123123
assert front["isDraft"] is False
124124
assert front["breaking"] is False
125125
assert front["tags"] == SELECTION["tags"]
126126
assert front["sources"] == SOURCES
127127

128+
def test_selene_yazari_acikca_secilebilir(self) -> None:
129+
assert _frontmatter(_rendered(author="Selene AI"))["author"] == "Selene AI"
130+
128131
def test_yayin_saati_turkiye_saatiyle_yazilir(self) -> None:
129132
front = _frontmatter(_rendered())
130133
assert str(front["pubDate"]).startswith("2026-08-01T14:30:05")

‎src/content/equinoxHaber/abd-disisleri-katara-olasi-4-5-milyar-dolarlik-kc-46a-satisini-onayladi.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ updatedDate: '2026-08-20T20:35:35+03:00'
66
heroImage: "/images/generated/equinox-haber/abd-disisleri-katara-olasi-4-5-milyar-dolarlik-kc-46a-satisini-onayladi.webp"
77
isDraft: false
88
tags: ["ABD", "Katar", "KC-46A", "savunma", "askeri satış"]
9-
author: "Asteria AI"
9+
author: "Selene AI"
1010
category: "Siyaset"
1111
breaking: false
1212
sources:

‎src/data/authors.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ export const authorProfiles: AuthorProfile[] = [
5353
color: '#38bdf8',
5454
aliases: ['asteria ai', 'asteria'],
5555
},
56+
{
57+
id: 'selene',
58+
slug: 'selene-ai',
59+
name: 'Selene AI',
60+
role: 'Equinox Haber editörü',
61+
emoji: '🛰️',
62+
bio: 'Haber adaylarını editoryal politika doğrultusunda değerlendirir; kaynak metninden kısa Türkçe haber yazımı ve yayın öncesi kontrollerde destek veren AI editoryal operatördür.',
63+
color: '#a78bfa',
64+
aliases: ['selene ai', 'selene'],
65+
},
5666
];
5767

5868
const normalize = (value: string) =>

‎src/layouts/NewsLayout.astro‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const blogHomeHref = isNewsSubdomainEnabled() ? getMainSiteUrl() : '/';
124124
<div class="news-footer-inner">
125125
<div class="news-footer-brand">
126126
<span class="news-footer-kicker">Equinox Haber</span>
127-
<p>Kaynaklı, kısa ve okunaklı Türkçe haber akışı. Asteria AI tarafından desteklenir, Samet Başbuğ tarafından yönetilir.</p>
127+
<p>Kaynaklı, kısa ve okunaklı Türkçe haber akışı. Asteria AI ve Selene AI tarafından desteklenir, Samet Başbuğ tarafından yönetilir.</p>
128128
</div>
129129
<div class="news-footer-grid">
130130
<nav class="news-footer-group" aria-label="Yayın bilgileri">

‎src/pages/[...slug].astro‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const newsArticleSchema = {
6060
},
6161
isAccessibleForFree: true,
6262
author: {
63-
'@type': entry.data.author === 'Asteria AI' ? 'Organization' : 'Person',
63+
'@type': (entry.data.author ?? 'Asteria AI').endsWith(' AI') ? 'Organization' : 'Person',
6464
name: entry.data.author ?? 'Asteria AI',
6565
},
6666
publisher: {

0 commit comments

Comments
 (0)