newsflow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNewsflow
新闻流(Newsflow)
Execution Setup
执行设置
- Resolve as the absolute directory containing this loaded
<SKILL_ROOT>.SKILL.md - Use the current environment's command or shell execution capability to run bundled scripts with .
python3 - Use the current environment's file-reading and file-writing capabilities for configuration, run artifacts, and model-produced translation JSON.
- Substitute the resolved absolute in every command, quote every path argument, and pass all arguments in the same command invocation. Do not rely on environment variables or shell state surviving between calls.
<SKILL_ROOT> - Do not detect or guess the runtime, load runtime-specific adapters, or assume a global install path or canonical repository path. This skill uses the same workflow in every environment.
- 将解析为包含已加载的
<SKILL_ROOT>文件的绝对目录。SKILL.md - 使用当前环境的命令或shell执行能力,通过运行打包的脚本。
python3 - 使用当前环境的文件读写能力处理配置文件、运行产物及模型生成的翻译JSON文件。
- 在每个命令中替换已解析的绝对路径,为每个路径参数添加引号,并在同一命令调用中传递所有参数。不要依赖环境变量或shell状态在多次调用间保留。
<SKILL_ROOT> - 无需检测或猜测运行时环境,无需加载特定于运行时的适配器,也无需假设全局安装路径或标准仓库路径。该工具在所有环境中使用相同的工作流。
Workflow
工作流程
- Use current working directory as the output directory.
- Read command configuration:
- Default: .
<SKILL_ROOT>/references/commands.json - Optional override: user-provided config path via .
--config
- Default:
- Define run-scoped working paths. Do not reuse flat temp files like ; each run must use its own artifact directory under
.news_state/tmp_current.json:.news_state/runs/<run-dir>/
bash
WORKDIR=<absolute current working directory>
STATE_DIR=<WORKDIR>/.news_state
RUNS_DIR=<STATE_DIR>/runs
RUN_DIR=<RUNS_DIR>/<unique-run-dir>
CURRENT_JSON_PATH=<RUN_DIR>/current.json
INCREMENTAL_JSON_PATH=<RUN_DIR>/incremental.json
TRANSLATED_JSON_PATH=<RUN_DIR>/translated.json- Run pipeline script sequentially:
bash
python3 "<SKILL_ROOT>/scripts/run_news_pipeline.py" --config "<commands.json>" --out-json "<CURRENT_JSON_PATH>"- Wait for step 4 to exit successfully before continuing. Never run while the pipeline command is still in flight.
prepare - Prepare incremental payload:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" prepare --current-json "<CURRENT_JSON_PATH>" --state-dir "<STATE_DIR>" --out-json "<INCREMENTAL_JSON_PATH>"Prepare recovery policy:
- If succeeds, continue normally.
prepare - If fails, inspect stderr for a bracketed
prepareerror code.PREPARE_* - For recoverable prepare codes, abandon the current run artifact directory, create a fresh , rerun the pipeline from step 4, then rerun
<RUN_DIR>once.prepare - Do not reuse the failed run's ,
current.json, orincremental.json.translated.json - Do not retry more than once. If the second fails, stop and report both the original and retry failures.
prepare - Do not retry non-recoverable prepare codes; stop and report the error.
- When an automatic prepare retry happens, mention it in the final response with the first failure reason and the new run directory.
Recoverable prepare codes:
- : the current payload is older than the latest finalized run.
PREPARE_STALE_CURRENT_JSON - : the run id has already been finalized today.
PREPARE_RUN_ID_ALREADY_FINALIZED - : the current run artifact is missing or not valid JSON.
PREPARE_CURRENT_JSON_UNREADABLE
Non-recoverable prepare codes:
- : artifact paths are outside the run directory, mixed across run directories, identical, or inconsistent with stored metadata.
PREPARE_BAD_ARTIFACT_PATH - :
PREPARE_BAD_CURRENT_JSONexists but does not match the expected pipeline payload structure.current.json - : run identity, timezone, or timestamp metadata is missing or invalid.
PREPARE_BAD_RUN_METADATA - : the daily state file is invalid or cannot be parsed safely.
PREPARE_BAD_STATE - :
PREPARE_WRITE_FAILEDcould not be written.incremental.json
- Parse incremental JSON result:
- : this run's fresh stories after removing yesterday URLs and earlier same-day URLs.
run_fresh_items_raw - : stories whose titles still need model translation for display.
items_to_translate - : errors and recovered degradations from this run. When a primary command fails but a retry or fallback succeeds, the pipeline may still emit an
current_run_errorsentry here so downstream reports can surface source health issues.已恢复:... - : accumulated errors and recovered degradations for the current day.
daily_errors - /
run_id/started_at: immutable run identity fields. Downstream steps must preserve them exactly.finished_at - : deterministic
run_output_stemstem derived fromYYYY-MM-DD-HH-mm-ss-<sha256前12位>; use it for per-run output names.generated_at + run_id - : immutable per-run snapshot of source display/classification/translation metadata; downstream steps must use this snapshot rather than reread a mutable config.
section_metadata - : the latest finalized daily state seen during
state_snapshot.preparewill reject stale snapshots.finalize
- Translate display text into Chinese in-model:
- Create the initial deterministic plan. The script derives required fields and keeps the existing rule that any title containing a Unicode Han character (including mixed-language text) does not need title translation, while still planning an English quote or Bloomberg summary when required. This is a character check, not language detection: Japanese text containing Kanji also satisfies it.
auto
- Create the initial deterministic plan. The script derives required fields and keeps the existing
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" plan-translations --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>" --out-json "<RUN_DIR>/translation-plan.json" --phase initial- Translate each in
batches[*].items. The plan counts only required source text from titles, quotes, and summaries. Up totranslation-plan.json(currently 12,000 characters) stays in one batch; larger input is split by source-text capacity. No item is truncated or summarized to fit, and one oversized item remains intact in its own batch.batch_source_char_limit - Write each model result as , using exactly the batch's
<RUN_DIR>/translation-initial-batch-NNN.jsonas its top-level URL keys. Do not add, omit, or substitute a URL.expected_urls - Merge each batch only through the script, which checks the exact URL set and required fields before atomically updating the cumulative map:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" merge-translation-batch --plan-json "<RUN_DIR>/translation-plan.json" --batch-id batch-NNN --batch-json "<RUN_DIR>/translation-initial-batch-NNN.json" --translated-json "<TRANSLATED_JSON_PATH>"- For every planned item whose includes
required_fields, translatetitle.title - For Twitter quote items, translate quote text when present.
- For Twitter items, always means the main tweet
title, andtextalways meansquoted_text. Never swap them.quotedTweet.text - For long Twitter posts, translate the main tweet and quoted tweet in full, preserving paragraph boundaries / numbering when practical. Do not summarize, compress, or rewrite them into a shorter takeaway sentence.
- For Bloomberg items with , translate
summarytoo; final Markdown displays the translated summary under the Bloomberg item.summary - Translation must stay in the model, not inside any script.
- Write a JSON object into :
<TRANSLATED_JSON_PATH>- Legacy format (still supported): map URL to translated title string.
- Extended format (recommended): map URL to object with , optional quote fields, and optional
title/summary.summary_zh
- If is empty, still write
items_to_translateto{}.<TRANSLATED_JSON_PATH>
- Validate translated map before finalize:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" validate-translations --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>"Validation workflow:
- If validate returns , continue to finalize.
ok=true - If validate returns , generate the one permitted repair plan; it contains only required fields still missing or non-Chinese, split by the same deterministic rules:
ok=false
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" plan-translations --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>" --out-json "<RUN_DIR>/translation-repair-plan.json" --phase repair- Translate and merge each repair batch with , using
merge-translation-batchandtranslation-repair-batch-NNN.json. Do not directly edit the cumulative map or create another repair plan.translation-repair-plan.json - Run exactly one more time after repair.
validate-translations - Validation automatically records hidden run-scoped state in . Do not edit it. If initial validation has issues,
translation-validation.jsonrejects the run until one repair plan and a second validation have been completed.finalize - Translation diagnostics remain in the hidden validation state; they are not rendered in Markdown or news-reader sidecar .
errors - Do not loop indefinitely. Even if the second validate still reports title issues, continue to finalize so news collection is not blocked; report the result as a partial translation outcome, never as full translation success. Diagnostics remain only in hidden run state, not user output errors.
- checks structure and required-field coverage only; it does not score translation style/quality.
validate-translations
json
{
"https://example.com/story": "中文标题",
"https://x.com/ivanalog_com/status/123?s=20": {
"title": "中文正文标题",
"quoted_text_zh": "引用推文中文翻译"
},
"https://www.bloomberg.com/news/articles/example": {
"title": "中文标题",
"summary_zh": "中文摘要"
}
}- Finalize outputs:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" finalize --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>" --state-dir "<STATE_DIR>" --out-dir "<WORKDIR>"Optional export step (post-finalize):
bash
python3 "<SKILL_ROOT>/scripts/export_outputs.py" --daily "<daily_fresh_path>" --fresh "<run_fresh_path>" --target-root "<export_root>"Export rules:
- Export copies the daily Markdown, per-run fresh Markdown, and the daily sidecar. Per-run freshNews has no sidecar and export must not require or copy one.
.newsreader.json - Export root precedence is: CLI argument,
--target-root, then the legacy personal defaultNEWSFLOW_EXPORT_ROOT./Users/x/Library/Mobile Documents/iCloud~md~obsidian/Documents/DailyNews - When the legacy default is used, the command prints a compatibility warning to stderr; pass an explicit root for portable use.
- If the root directory does not exist, export fails with explicit error and root path.
- Month subdirectory is auto-created as , parsed from filenames.
YYYY年M月 - and either legacy
dailyFreshNews_YYYY-MM-DD.mdor currentYYYY-MM-DD-HH-mm_freshNews.mdmust resolve to the same year-month.YYYY-MM-DD-HH-mm-ss-<12hex>_freshNews.md - Export overwrites same-name files by default.
- Export failure never rolls back finalized local outputs.
Finalize recovery policy:
- If succeeds, continue normally.
finalize - If fails, inspect stderr for a bracketed
finalizeerror code.FINALIZE_* - For , rerun
FINALIZE_STATE_CHANGED_SINCE_PREPAREonce using the samepreparestored incurrent_json_path, the sameincremental.json.paths.current_json_path, and the sameSTATE_DIR.INCREMENTAL_JSON_PATH - After rerunning , reuse the existing
prepareas a base, translate only newly missingtranslated.jsonfields, then rerunitems_to_translateonce.finalize - Do not rerun the pipeline as part of finalize recovery. If the same is rejected during the new
current.json, stop and report that the current artifact is no longer usable against the latest state.prepare - Do not retry ,
FINALIZE_OUTPUT_EXISTS, bad artifact paths, bad JSON, bad metadata, bad state, or already-finalized runs.FINALIZE_WRITE_FAILED - Bloomberg summary translation issues are handled before by repairing
finalize; if still unresolved after one repair,translated.jsonuses the original summary and keeps diagnostics in hidden run state.finalize
Recoverable finalize codes:
- : daily state changed after
FINALIZE_STATE_CHANGED_SINCE_PREPARE; rerunpreparefrom the sameprepare.current.json
Non-recoverable finalize codes:
- : artifact paths are outside the run directory, mixed across run directories, or inconsistent with stored metadata.
FINALIZE_BAD_ARTIFACT_PATH - :
FINALIZE_BAD_INCREMENTAL_JSONis missing, invalid JSON, or not an object.incremental.json - :
FINALIZE_BAD_TRANSLATED_JSONis missing, invalid JSON, or not an object.translated.json - : run identity, timezone, or timestamp metadata is missing or invalid.
FINALIZE_BAD_RUN_METADATA - : date, run timestamp, paths, or state snapshot metadata is invalid.
FINALIZE_BAD_INCREMENTAL_METADATA - : the daily state file is invalid or cannot be parsed safely.
FINALIZE_BAD_STATE - : this run id or generated timestamp has already been finalized.
FINALIZE_RUN_ALREADY_FINALIZED - : the target per-run
FINALIZE_OUTPUT_EXISTSalready exists.freshNews.md - : an output Markdown or state file could not be written.
FINALIZE_WRITE_FAILED
- Finalize writes exactly two user-facing Markdown files:
- : one rolling summary file per day.
dailyFreshNews_YYYY-MM-DD.md - : one collision-resistant per-run fresh-news file.
YYYY-MM-DD-HH-mm-ss-<sha256前12位>_freshNews.md - Timezone: unless user explicitly requests another timezone.
Asia/Shanghai - Finalize also writes only ; it must not create a per-run
dailyFreshNews_YYYY-MM-DD.newsreader.json.*_freshNews.newsreader.json - Do not delete a legacy per-run sidecar if one already exists. Older state entries may contain ; accept and ignore that field, while new run records must not write it.
run_sidecar_path
- Hidden state is stored separately in the state directory, one JSON file per day.
- Safety rules:
- and
preparenow require run artifacts to live underfinalize.<STATE_DIR>/runs/<run-dir>/ - never overwrites an existing per-run fresh-news file; legacy incremental artifacts without
finalizeare finalized with the newly derived stem.run_output_stem - If sees a
prepareolder than the latest finalized run, it fails instead of returning a misleadingcurrent.json.0 条新增 - Recoverable failures may trigger one clean retry from a new run directory; non-recoverable failures must remain hard stops.
prepare - If state changes after , rerun
preparefrom the sameprepare; do not forcecurrent.jsonand do not automatically rerun pipeline.finalize
- 将当前工作目录作为输出目录。
- 读取命令配置:
- 默认路径:
<SKILL_ROOT>/references/commands.json - 可选覆盖:用户可通过参数提供自定义配置路径
--config
- 默认路径:
- 定义运行范围的工作路径。请勿复用这类扁平临时文件;每次运行必须在
.news_state/tmp_current.json下使用独立的产物目录:.news_state/runs/<run-dir>/
bash
WORKDIR=<absolute current working directory>
STATE_DIR=<WORKDIR>/.news_state
RUNS_DIR=<STATE_DIR>/runs
RUN_DIR=<RUNS_DIR>/<unique-run-dir>
CURRENT_JSON_PATH=<RUN_DIR>/current.json
INCREMENTAL_JSON_PATH=<RUN_DIR>/incremental.json
TRANSLATED_JSON_PATH=<RUN_DIR>/translated.json- 按顺序运行流水线脚本:
bash
python3 "<SKILL_ROOT>/scripts/run_news_pipeline.py" --config "<commands.json>" --out-json "<CURRENT_JSON_PATH>"- 等待步骤4执行成功后再继续。流水线命令运行期间切勿执行操作。
prepare - 准备增量负载:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" prepare --current-json "<CURRENT_JSON_PATH>" --state-dir "<STATE_DIR>" --out-json "<INCREMENTAL_JSON_PATH>"准备恢复策略:
- 若执行成功,正常继续后续流程。
prepare - 若执行失败,检查标准错误输出中带括号的
prepare错误码。PREPARE_* - 对于可恢复的错误码,丢弃当前运行的产物目录,创建新的,从步骤4重新运行流水线,然后重新执行一次
<RUN_DIR>。prepare - 请勿复用失败运行的、
current.json或incremental.json文件。translated.json - 重试次数不得超过一次。若第二次仍失败,停止流程并报告首次和重试的失败信息。
prepare - 对于不可恢复的错误码,无需重试;停止流程并报告错误。
- 当自动重试时,需在最终响应中提及首次失败原因和新的运行目录。
prepare
可恢复的prepare错误码:
- :当前负载早于最新的已完成运行版本
PREPARE_STALE_CURRENT_JSON - :该运行ID今日已完成
PREPARE_RUN_ID_ALREADY_FINALIZED - :当前运行产物缺失或不是有效的JSON文件
PREPARE_CURRENT_JSON_UNREADABLE
不可恢复的prepare错误码:
- :产物路径位于运行目录之外、跨运行目录混合、路径相同或与存储的元数据不一致
PREPARE_BAD_ARTIFACT_PATH - :
PREPARE_BAD_CURRENT_JSON存在但不符合预期的流水线负载结构current.json - :运行标识、时区或时间戳元数据缺失或无效
PREPARE_BAD_RUN_METADATA - :每日状态文件无效或无法安全解析
PREPARE_BAD_STATE - :无法写入
PREPARE_WRITE_FAILED文件incremental.json
- 解析增量JSON结果:
- :本次运行中移除昨日URL及当日更早URL后的新鲜新闻
run_fresh_items_raw - :标题仍需模型翻译以用于展示的新闻
items_to_translate - :本次运行中的错误及已恢复的异常。当主命令失败但重试或备用命令成功时,流水线可能会在此处输出
current_run_errors条目,以便下游报告能显示数据源健康问题已恢复:... - :当日累计的错误及已恢复的异常
daily_errors - /
run_id/started_at:不可变的运行标识字段。下游步骤必须完全保留这些字段finished_at - :由
run_output_stem生成的确定性标识,格式为generated_at + run_id;用于命名单次运行的输出文件YYYY-MM-DD-HH-mm-ss-<sha256前12位> - :数据源展示/分类/翻译元数据的运行范围快照;下游步骤必须使用该快照,而非重新读取可变配置
section_metadata - :
state_snapshot过程中读取的最新已完成每日状态。prepare会拒绝过时的快照finalize
- 通过模型将展示文本翻译成中文:
- 创建初始确定性翻译计划。脚本会推导所需字段,并保留现有规则:任何包含Unicode汉字(包括混合语言文本)的标题无需翻译,但在需要时仍会计划翻译英文引用或彭博社摘要。这是字符检查,而非语言检测:包含汉字的日文文本也符合该规则。
auto
- 创建初始确定性翻译计划。脚本会推导所需字段,并保留现有
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" plan-translations --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>" --out-json "<RUN_DIR>/translation-plan.json" --phase initial- 翻译中
translation-plan.json的内容。计划仅统计标题、引用和摘要中的必要源文本。不超过batches[*].items(当前为12000字符)的内容保留在一个批次中;超出容量的输入会按源文本拆分。不会为了适配而截断或摘要任何条目,单个超大条目会完整保留在独立批次中。batch_source_char_limit - 将每个模型结果写入,并以批次的
<RUN_DIR>/translation-initial-batch-NNN.json作为顶级URL键。请勿添加、省略或替换URL。expected_urls - 仅通过脚本合并每个批次,脚本会检查URL集和所需字段,然后原子性更新累积映射:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" merge-translation-batch --plan-json "<RUN_DIR>/translation-plan.json" --batch-id batch-NNN --batch-json "<RUN_DIR>/translation-initial-batch-NNN.json" --translated-json "<TRANSLATED_JSON_PATH>"- 对于每个包含
required_fields的计划条目,翻译title字段。title - 对于Twitter引用条目,若存在引用文本则翻译该文本。
- 对于Twitter条目,始终指主推文的
title字段,text始终指quoted_text字段。切勿混淆两者。quotedTweet.text - 对于长Twitter帖子,完整翻译主推文和引用推文,尽可能保留段落分隔/编号。请勿进行摘要、压缩或改写为简短要点。
- 对于包含的彭博社条目,同时翻译
summary字段;最终Markdown会在该条目下方展示翻译后的摘要。summary - 翻译必须由模型完成,而非在任何脚本内执行。
- 将JSON对象写入:
<TRANSLATED_JSON_PATH>- 兼容格式(仍支持):URL到翻译后标题字符串的映射
- 扩展格式(推荐):URL到包含、可选引用字段及可选
title/summary的对象的映射summary_zh
- 若为空,仍需向
items_to_translate写入<TRANSLATED_JSON_PATH>{}
- 在finalize前验证翻译映射:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" validate-translations --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>"验证工作流:
- 若验证返回,继续执行finalize。
ok=true - 若验证返回,生成唯一允许的修复计划;该计划仅包含仍缺失或非中文的必要字段,并按相同确定性规则拆分:
ok=false
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" plan-translations --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>" --out-json "<RUN_DIR>/translation-repair-plan.json" --phase repair- 使用翻译并合并每个修复批次,使用
merge-translation-batch和translation-repair-batch-NNN.json。请勿直接编辑累积映射或创建其他修复计划。translation-repair-plan.json - 修复后必须再执行一次。
validate-translations - 验证会自动将运行范围的隐藏状态记录在中。请勿编辑该文件。若初始验证存在问题,
translation-validation.json会拒绝该运行,直到完成一次修复计划和第二次验证。finalize - 翻译诊断信息仅保留在隐藏的运行状态中,不会在Markdown或新闻阅读器附属文件中显示。
errors - 请勿无限循环。即使第二次验证仍报告标题问题,仍需继续执行finalize以避免新闻收集中断;需将结果报告为部分翻译完成,而非完全翻译成功。诊断信息仅保留在隐藏运行状态中,不会在用户输出错误中显示。
- 仅检查结构和必要字段覆盖情况;不评估翻译风格/质量。
validate-translations
json
{
"https://example.com/story": "中文标题",
"https://x.com/ivanalog_com/status/123?s=20": {
"title": "中文正文标题",
"quoted_text_zh": "引用推文中文翻译"
},
"https://www.bloomberg.com/news/articles/example": {
"title": "中文标题",
"summary_zh": "中文摘要"
}
}- 完成输出:
bash
python3 "<SKILL_ROOT>/scripts/run_incremental_news.py" finalize --incremental-json "<INCREMENTAL_JSON_PATH>" --translated-json "<TRANSLATED_JSON_PATH>" --state-dir "<STATE_DIR>" --out-dir "<WORKDIR>"可选导出步骤(finalize之后):
bash
python3 "<SKILL_ROOT>/scripts/export_outputs.py" --daily "<daily_fresh_path>" --fresh "<run_fresh_path>" --target-root "<export_root>"导出规则:
- 导出会复制每日Markdown文件、单次运行新鲜新闻Markdown文件及每日附属文件。单次运行新鲜新闻无附属文件,导出无需复制或要求该文件。
.newsreader.json - 导出根目录优先级:命令行参数 >
--target-root环境变量 > 旧版个人默认路径NEWSFLOW_EXPORT_ROOT/Users/x/Library/Mobile Documents/iCloud~md~obsidian/Documents/DailyNews - 若使用旧版默认路径,命令会向标准错误输出打印兼容性警告;如需可移植性,请传递显式根目录。
- 若根目录不存在,导出会失败并输出明确错误及根路径。
- 月份子目录会自动创建为,由文件名解析而来。
YYYY年M月 - 以及旧版
dailyFreshNews_YYYY-MM-DD.md或新版YYYY-MM-DD-HH-mm_freshNews.md必须属于同一年月。YYYY-MM-DD-HH-mm-ss-<12hex>_freshNews.md - 默认情况下,导出会覆盖同名文件。
- 导出失败不会回滚已完成的本地输出。
Finalize恢复策略:
- 若执行成功,正常继续后续流程。
finalize - 若执行失败,检查标准错误输出中带括号的
finalize错误码。FINALIZE_* - 对于,使用
FINALIZE_STATE_CHANGED_SINCE_PREPARE中存储的同一incremental.json.paths.current_json_path、同一current_json_path和同一STATE_DIR重新执行一次INCREMENTAL_JSON_PATH。prepare - 重新执行后,复用现有
prepare作为基础,仅翻译新缺失的translated.json字段,然后重新执行一次items_to_translate。finalize - 请勿将重新运行流水线作为finalize恢复的一部分。若新的拒绝同一
prepare,停止流程并报告当前产物无法适配最新状态。current.json - 对于、
FINALIZE_OUTPUT_EXISTS、无效产物路径、无效JSON、无效元数据、无效状态或已完成的运行,无需重试。FINALIZE_WRITE_FAILED - 彭博社摘要翻译问题需在前通过修复
finalize处理;若一次修复后仍未解决,translated.json会使用原始摘要并将诊断信息保留在隐藏运行状态中。finalize
可恢复的finalize错误码:
- :
FINALIZE_STATE_CHANGED_SINCE_PREPARE后每日状态发生变化;使用同一prepare重新执行current.jsonprepare
不可恢复的finalize错误码:
- :产物路径位于运行目录之外、跨运行目录混合或与存储的元数据不一致
FINALIZE_BAD_ARTIFACT_PATH - :
FINALIZE_BAD_INCREMENTAL_JSON缺失、无效或不是对象incremental.json - :
FINALIZE_BAD_TRANSLATED_JSON缺失、无效或不是对象translated.json - :运行标识、时区或时间戳元数据缺失或无效
FINALIZE_BAD_RUN_METADATA - :日期、运行时间戳、路径或状态快照元数据无效
FINALIZE_BAD_INCREMENTAL_METADATA - :每日状态文件无效或无法安全解析
FINALIZE_BAD_STATE - :该运行ID或生成时间戳已完成
FINALIZE_RUN_ALREADY_FINALIZED - :目标单次运行
FINALIZE_OUTPUT_EXISTS已存在freshNews.md - :无法写入输出Markdown或状态文件
FINALIZE_WRITE_FAILED
- Finalize会生成两个面向用户的Markdown文件:
- :每日一份的滚动摘要文件
dailyFreshNews_YYYY-MM-DD.md - :具有防冲突特性的单次运行新鲜新闻文件
YYYY-MM-DD-HH-mm-ss-<sha256前12位>_freshNews.md - 时区:默认使用,除非用户明确要求其他时区
Asia/Shanghai - Finalize还会生成;不得生成单次运行的
dailyFreshNews_YYYY-MM-DD.newsreader.json文件*_freshNews.newsreader.json - 若已存在旧版单次运行附属文件,请勿删除。旧状态条目可能包含字段;需接受并忽略该字段,同时新运行记录不得写入该字段
run_sidecar_path
- 隐藏状态单独存储在状态目录中,每日一个JSON文件。
- 安全规则:
- 和
prepare现在要求运行产物必须存储在finalize下<STATE_DIR>/runs/<run-dir>/ - 绝不会覆盖已存在的单次运行新鲜新闻文件;无
finalize的旧版增量产物会使用新生成的标识完成处理run_output_stem - 若发现
prepare早于最新的已完成运行版本,会直接失败,而非返回误导性的current.json0 条新增 - 可恢复的失败可触发一次从新运行目录开始的重试;不可恢复的失败必须直接停止流程
prepare - 若后状态发生变化,使用同一
prepare重新执行current.json;不得强制执行prepare,也不得自动重新运行流水线finalize
State Schema Notes
状态架构说明
- Daily state file path: .
<STATE_DIR>/YYYY-MM-DD.json - Top-level keys are daily aggregates and metadata, for example:
- ,
date,timezonesection_order - ,
today_seen_urlstoday_first_seen_items daily_errors- (array of per-run summaries)
runs
- Run artifact directory: .
<STATE_DIR>/runs/<run-dir>/ - Pipeline payload now includes ,
run_id,started_at, and may includefinished_at.current_json_path - Per-run counters are stored under (latest run), not at top level.
runs[-1]- Read for this run's fresh count.
runs[-1].run_fresh_count - Read for current day cumulative fresh count.
runs[-1].daily_fresh_count - Read for this run error count.
runs[-1].error_count - Read /
runs[-1].run_fresh_pathfor output files.runs[-1].daily_fresh_path - New runs also record audit fields such as ,
run_id,current_json_path,incremental_json_path,translated_json_path, andprepared_at.finalized_at
- Read
- If is empty, treat run-level stats as unavailable rather than
runs.0
- 每日状态文件路径:
<STATE_DIR>/YYYY-MM-DD.json - 顶级键为每日聚合数据和元数据,例如:
- 、
date、timezonesection_order - 、
today_seen_urlstoday_first_seen_items daily_errors- (单次运行摘要数组)
runs
- 运行产物目录:
<STATE_DIR>/runs/<run-dir>/ - 流水线负载现在包含、
run_id、started_at,可能还包含finished_atcurrent_json_path - 单次运行统计数据存储在(最新运行)下,而非顶级目录
runs[-1]- 读取获取本次运行的新增数量
runs[-1].run_fresh_count - 读取获取当日累计新增数量
runs[-1].daily_fresh_count - 读取获取本次运行的错误数量
runs[-1].error_count - 读取/
runs[-1].run_fresh_path获取输出文件路径runs[-1].daily_fresh_path - 新运行还会记录审计字段,如、
run_id、current_json_path、incremental_json_path、translated_json_path和prepared_atfinalized_at
- 读取
- 若为空,需将运行级统计视为不可用,而非
runs0
commands.json Format
commands.json格式
The bundled v1.2.2 default config has 19 ordered source entries, including 11 Twitter accounts.
Use JSON array of objects:
json
[
{
"section": "middle-east",
"display_name": "Reuters · Middle East",
"source_type": "reuters",
"source_name": "Reuters",
"translation_policy": "always",
"command": ["opencli", "ReutersBrowser", "news", "https://www.reuters.com/world/middle-east/", "--limit", "10", "--format", "json"]
}
]Rules:
- Keep order as desired final processing order.
- Add/remove sources by adding/removing objects only.
- supports string array (recommended) or shell string.
command - ,
display_name, andsource_typedefine the source metadata snapshot consumed by prepare/finalize; Twitter also requiressource_name.source_handle - Optional reliability fields are supported per source:
- : retry the primary command once before recording failure.
retry_once - : secondary command when primary still fails.
fallback_command - : treat zero valid rows as failure for retry/fallback.
treat_empty_as_failure - : minimum valid rows required for success when empty-check is enabled.
min_valid_items - : translation requirement policy for this source (
translation_policy,always,auto).never
- Current policy in this skill:
- News portal sources use ; most also use
retry_onceandtreat_empty_as_failure: true.min_valid_items: 1 - Twitter sources use only; do not force empty-as-failure by default.
retry_once - Reuters/Bloomberg/TechCrunch/Ars should use .
translation_policy: "always" - Twitter should use .
translation_policy: "auto"
- News portal sources use
打包的v1.2.2默认配置包含19个有序数据源条目,其中包括11个Twitter账号。
使用JSON对象数组:
json
[
{
"section": "middle-east",
"display_name": "Reuters · Middle East",
"source_type": "reuters",
"source_name": "Reuters",
"translation_policy": "always",
"command": ["opencli", "ReutersBrowser", "news", "https://www.reuters.com/world/middle-east/", "--limit", "10", "--format", "json"]
}
]规则:
- 保持所需的最终处理顺序
- 仅通过添加/移除对象来添加/移除数据源
- 支持字符串数组(推荐)或shell字符串
command - 、
display_name和source_type定义source_name使用的数据源元数据快照;Twitter还需prepare/finalize字段source_handle - 每个数据源支持可选的可靠性字段:
- :记录失败前重试主命令一次
retry_once - :主命令仍失败时使用的备用命令
fallback_command - :将有效行数为零视为失败,触发重试/备用逻辑
treat_empty_as_failure - :启用空检查时,成功所需的最小有效行数
min_valid_items - :该数据源的翻译需求策略(
translation_policy、always、auto)never
- 本工具的当前策略:
- 新闻门户数据源使用;大多数还使用
retry_once和treat_empty_as_failure: truemin_valid_items: 1 - Twitter数据源仅使用;默认不强制将空结果视为失败
retry_once - Reuters/Bloomberg/TechCrunch/Ars应使用
translation_policy: "always" - Twitter应使用
translation_policy: "auto"
- 新闻门户数据源使用
Output Contract
输出约定
For each output Markdown file:
- Emit full sections only for non-empty groups after filtering.
## section(N条) - Use display-friendly section names when available, such as or
Reuters · World.TechCrunch - Add a one-line summary blockquote under each non-empty section header:
- Format:
> N条|最新 ...|最早 ...|时间倒序
- Format:
- Separate adjacent non-empty sections with .
--- - Do not emit standalone section headers for empty groups.
(0条) - Instead, append a summary section at the end:
## 本次无更新的分组(X个)- List each empty group as a bullet using its display name, or when there are none.
- 无
Example non-empty section:
markdown
undefined对于每个输出Markdown文件:
- 仅为过滤后非空的分组生成完整的章节
## section(N条) - 使用友好的展示名称(如或
Reuters · World)TechCrunch - 在每个非空章节标题下添加一行摘要块引用:
- 格式:
> N条|最新 ...|最早 ...|时间倒序
- 格式:
- 相邻非空章节之间用分隔
--- - 不为空分组生成单独的章节标题
(0条) - 取而代之,在末尾添加摘要章节:
## 本次无更新的分组(X个)- 使用展示名称将每个空分组列为项目符号;若无空分组则显示
- 无
非空章节示例:
markdown
undefinedReuters · World(3条)
Reuters · World(3条)
3条|最新 2026-04-09 10:00:00|最早 2026-04-09 08:00:00|时间倒序
3条|最新 2026-04-09 10:00:00|最早 2026-04-09 08:00:00|时间倒序
- 发布时间:YYYY-MM-DD HH:MM:SS
Example empty-group summary:
```markdown- 发布时间:YYYY-MM-DD HH:MM:SS
空分组摘要示例:
```markdown本次无更新的分组(2个)
本次无更新的分组(2个)
- Bloomberg
- Reuters · World
Constraints:
- Missing time must be `页面未显示`.
- Bloomberg summaries should be rendered in Chinese when translation is available. The translation map may use `summary` or `summary_zh`; `summary_zh` is preferred for clarity.
- If a non-Chinese Bloomberg summary is present but its translated summary is missing or still non-Chinese after one repair attempt, `finalize` writes the original source summary without adding a user-visible error.
- Preserve first-seen order: command order first, then source order.
- Global dedupe key is absolute URL exact match.
- Daily filtering removes yesterday's URLs.
- Per-run filtering removes yesterday's URLs and URLs seen earlier the same day.
- Twitter (`twitter user-posts --json`) is supported:
- Every Twitter command in `references/commands.json` must declare `source_type: "twitter"`, `source_handle`, and `source_name`; these fields identify the configured collection account and flow into every normalized item and sidecar entry.
- Keep collection identity separate from content authorship: `source_handle` / `source_name` identify whose timeline was collected, while `author_screen_name` / `author_name` and the item URL identify the actual content author for originals, reposts, replies, and quotes.
- Add or remove Twitter accounts only through `commands.json`; do not maintain a section-name allowlist in scripts.
- `text` -> output title.
- `author.name` can be used as `section` via commands config.
- URL auto-generated as `https://x.com/{screenName}/status/{id}?s=20`.
- `createdAtLocal` -> 发布时间.
- `quotedTweet.text` renders as blockquote.
- The translation map must keep the same split: main tweet translation in `title`, quoted tweet translation in canonical `quoted_text_zh`.
- `quoted_text` is accepted for legacy artifacts only when `quoted_text_zh` is absent. If both keys are present, the canonical key wins; distinct Chinese values are a validation conflict.
- Long Twitter posts should be translated in full; do not collapse them into a short summary sentence.
- If `quoted_text_zh` is provided, only Chinese quote text is rendered (no bilingual block).
- Recommended translation policy: only translate non-Chinese text.
- Add final block:
```markdown- Bloomberg
- Reuters · World
约束:
- 缺失的时间需显示为`页面未显示`
- 彭博社摘要在有翻译时需显示为中文。翻译映射可使用`summary`或`summary_zh`;优先使用`summary_zh`以保证清晰
- 若存在非中文彭博社摘要但翻译后的摘要缺失或经一次修复后仍为非中文,`finalize`会写入原始源摘要,且不添加用户可见的错误
- 保留首次出现顺序:先按命令顺序,再按数据源顺序
- 全局去重键为URL精确匹配
- 每日过滤会移除昨日的URL
- 单次运行过滤会移除昨日的URL及当日更早出现的URL
- 支持Twitter(`twitter user-posts --json`):
- `references/commands.json`中的每个Twitter命令必须声明`source_type: "twitter"`、`source_handle`和`source_name`;这些字段用于标识配置的采集账号,并会流入每个标准化条目和附属文件
- 将采集标识与内容作者分开:`source_handle` / `source_name`标识采集的时间线所属账号,而`author_screen_name` / `author_name`和条目URL标识原创、转发、回复和引用内容的实际作者
- 仅通过`commands.json`添加或移除Twitter账号;请勿在脚本中维护章节名称白名单
- `text`字段对应输出标题
- `author.name`可通过命令配置用作`section`字段
- URL自动生成为`https://x.com/{screenName}/status/{id}?s=20`
- `createdAtLocal`对应发布时间
- `quotedTweet.text`会渲染为块引用
- 翻译映射必须保持拆分:主推文翻译在`title`中,引用推文翻译在标准`quoted_text_zh`中
- 仅当`quoted_text_zh`缺失时,才接受旧版产物中的`quoted_text`。若两个键同时存在,标准键优先;不同的中文值会被视为验证冲突
- 长Twitter帖子需完整翻译;请勿压缩为简短摘要
- 若提供`quoted_text_zh`,仅渲染中文引用文本(无双语块)
- 推荐翻译策略:仅翻译非中文文本
- 添加最终错误块:
```markdownerrors
errors
1. section
1. section
- 命令:
... - 错误:...
undefined- 命令:
... - 错误:...
undefinedValidation Checklist
验证清单
- Pipeline runs all commands sequentially.
- Failed command does not stop later commands.
- Duplicate URLs are removed globally, keeping first occurrence.
- Translation is model-handled, not external translation API.
- Non-empty sections include display names and summary blockquotes; empty sections are grouped under .
本次无更新的分组 - Finalize writes and
dailyFreshNews_YYYY-MM-DD.md, notYYYY-MM-DD-HH-mm-ss-<sha256前12位>_freshNews.md.*_fullNews.md - Reusing stale pipeline JSON or attempting to overwrite an existing run file must fail loudly.
- 流水线按顺序运行所有命令
- 失败的命令不会终止后续命令
- 重复URL会被全局移除,保留首次出现的条目
- 翻译由模型处理,而非外部翻译API
- 非空章节包含展示名称和摘要块引用;空分组归类到下
本次无更新的分组 - Finalize生成和
dailyFreshNews_YYYY-MM-DD.md,而非YYYY-MM-DD-HH-mm-ss-<sha256前12位>_freshNews.md*_fullNews.md - 复用过时的流水线JSON或尝试覆盖已存在的运行文件必须直接失败