Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,15 @@ jobs:
run: npm test
- name: Export static site
run: npm run export
- name: Validate public content across every main tab
run: npm run --silent public:validate -- --output="$RUNNER_TEMP/public-integrity.json"
- name: Upload public integrity evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: public-integrity-${{ github.run_id }}
path: ${{ runner.temp }}/public-integrity.json
if-no-files-found: ignore
retention-days: 7
- name: Build
run: npm run build
18 changes: 14 additions & 4 deletions .github/workflows/data-refresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ jobs:
- name: Remove unattached aggregator provenance debt
run: npm run repair:provenance -- --confirm

- name: Audit research impact before publication
env:
OPENALEX_API_KEY: ${{ secrets.OPENALEX_API_KEY }}
run: npm run --silent research:impact -- --skip-seed

- name: Enrich evidence-ready review Events with DeepSeek
id: enrichment
env:
Expand Down Expand Up @@ -246,6 +251,7 @@ jobs:
run: |
set -euo pipefail
npm run export -- --skip-seed
npm run --silent public:validate -- --output="$RUNNER_TEMP/public-integrity.json"
npm run --silent public:fingerprint > "$RUNNER_TEMP/public-after.sha256"
if cmp -s "$RUNNER_TEMP/public-before.sha256" "$RUNNER_TEMP/public-after.sha256"; then
echo "No material public content changes."
Expand All @@ -264,13 +270,15 @@ jobs:
set -euo pipefail
test -s data/snapshot/v1.json
test -s data/narratives/stage-promotions.json
test -s data/reports/research-impact.json
node -e 'JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8"))' data/narratives/stage-promotions.json
git diff --check -- data/snapshot/v1.json data/narratives/stage-promotions.json
if grep -E '"(token|secret|password|cookie|authorization|api[_-]?key|reasoning|prompt|completion)"[[:space:]]*:' data/snapshot/v1.json data/narratives/stage-promotions.json; then
node -e 'JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8"))' data/reports/research-impact.json
git diff --check -- data/snapshot/v1.json data/narratives/stage-promotions.json data/reports/research-impact.json
if grep -E '"(token|secret|password|cookie|authorization|api[_-]?key|reasoning|prompt|completion)"[[:space:]]*:' data/snapshot/v1.json data/narratives/stage-promotions.json data/reports/research-impact.json; then
echo "Sensitive or raw model material detected in repository data" >&2
exit 1
fi
if grep -E -- '-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----|/Users/[^/]+/|/home/runner/' data/snapshot/v1.json data/narratives/stage-promotions.json; then
if grep -E -- '-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----|/Users/[^/]+/|/home/runner/' data/snapshot/v1.json data/narratives/stage-promotions.json data/reports/research-impact.json; then
echo "Private material or local path detected in repository data" >&2
exit 1
fi
Expand All @@ -280,7 +288,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
git add -- data/snapshot/v1.json data/narratives/stage-promotions.json
git add -- data/snapshot/v1.json data/narratives/stage-promotions.json data/reports/research-impact.json
if git diff --cached --quiet; then
echo "No material data changes; skipping commit."
echo "changed=false" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -407,5 +415,7 @@ jobs:
${{ runner.temp }}/stage-promotion.json
${{ runner.temp }}/stage-promotion-apply.json
${{ runner.temp }}/weekly-status.json
${{ runner.temp }}/public-integrity.json
data/reports/research-impact.json
if-no-files-found: ignore
retention-days: 14
65 changes: 60 additions & 5 deletions .github/workflows/monitor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ on:
# Monday 08:17 Asia/Shanghai, after the weekly refresh and Pages deployment.
- cron: "17 0 * * 1"
workflow_dispatch:
inputs:
allow_notification:
description: Allow this manual run to update the monitor incident after all gates pass
required: false
default: false
type: boolean

permissions:
actions: read
Expand Down Expand Up @@ -79,39 +85,88 @@ jobs:
echo "status=$status" >> "$GITHUB_OUTPUT"
jq . "$RUNNER_TEMP/monitor.json"

- name: Read the current monitor incident
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
gh issue list --state open --label 'monitor:critical' --limit 1 --json number,updatedAt,body,url > "$RUNNER_TEMP/monitor-incident.json"
jq -e 'type == "array"' "$RUNNER_TEMP/monitor-incident.json" >/dev/null

- name: Decide whether this run deserves a notification
id: decision
env:
ALLOW_NOTIFICATION: ${{ inputs.allow_notification || 'false' }}
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
DEEPSEEK_MODEL: deepseek-v4-flash
shell: bash
run: |
set -euo pipefail
args=(--report "$RUNNER_TEMP/monitor.json" --incident "$RUNNER_TEMP/monitor-incident.json")
if [[ -n "${DEEPSEEK_API_KEY:-}" ]]; then
args+=(--ai)
fi
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" && "$ALLOW_NOTIFICATION" != "true" ]]; then
args+=(--dry-run)
fi
npm run --silent monitor:decide -- "${args[@]}" > "$RUNNER_TEMP/monitor-decision.json"
jq -e '.decision and (.notify | type == "boolean") and .fingerprint and .reasonCode' "$RUNNER_TEMP/monitor-decision.json" >/dev/null
echo "notify=$(jq -r '.notify' "$RUNNER_TEMP/monitor-decision.json")" >> "$GITHUB_OUTPUT"
echo "fingerprint=$(jq -r '.fingerprint' "$RUNNER_TEMP/monitor-decision.json")" >> "$GITHUB_OUTPUT"
jq . "$RUNNER_TEMP/monitor-decision.json"
{
echo '### Monitor alert decision'
echo
echo "- Decision: $(jq -r '.decision' "$RUNNER_TEMP/monitor-decision.json")"
echo "- Notification: $(jq -r '.notify' "$RUNNER_TEMP/monitor-decision.json")"
echo "- Source: $(jq -r '.decisionSource' "$RUNNER_TEMP/monitor-decision.json")"
echo "- Reason: $(jq -r '.reasonCode' "$RUNNER_TEMP/monitor-decision.json")"
echo "- Fingerprint: $(jq -r '.fingerprint' "$RUNNER_TEMP/monitor-decision.json")"
echo "- Rationale: $(jq -r '.rationale | gsub("[\\r\\n]"; " ")' "$RUNNER_TEMP/monitor-decision.json")"
} >> "$GITHUB_STEP_SUMMARY"

- name: Upload monitor evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: monitor-${{ github.run_id }}
path: ${{ runner.temp }}/monitor.json
path: |
${{ runner.temp }}/monitor.json
${{ runner.temp }}/monitor-decision.json
retention-days: 7

- name: Update the single monitor incident
if: steps.monitor.outputs.status == 'critical'
if: steps.decision.outputs.notify == 'true'
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
report="$RUNNER_TEMP/monitor.json"
body="$RUNNER_TEMP/monitor-issue.md"
decision="$RUNNER_TEMP/monitor-decision.json"
{
echo '<!-- agent-pulse-monitor:v1 -->'
echo "<!-- agent-pulse-monitor:v2 fingerprint=$(jq -r '.fingerprint' "$decision") -->"
echo '# Agent Pulse 自动监控告警'
echo
echo "- 时间:$(jq -r '.timestamp' "$report")"
echo "- 系统分:$(jq -r '.systemScore' "$report") / 100"
echo "- 状态:$(jq -r '.status' "$report")"
echo "- 告警门禁:$(jq -r '.decisionSource' "$decision") · $(jq -r '.reasonCode' "$decision")"
echo
echo '## 问题'
jq -r '.issues[]? | "- " + .' "$report"
echo
echo '## 本次告警判断'
echo "$(jq -r '.rationale' "$decision")"
echo
echo '## 建议动作'
jq -r '.recommendations[]? | "- " + .' "$report"
echo "- $(jq -r '.suggestedAction' "$decision")"
echo
echo "[查看本次 Actions 运行](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})"
} > "$body"
gh label create "monitor:critical" --color d73a4a --description "Automated critical monitor incident" --force
gh label create "monitor:critical" --color d73a4a --description "AI-gated critical monitor incident" --force
issue="$(gh issue list --state open --label 'monitor:critical' --limit 1 --json number --jq '.[0].number // empty')"
if [[ -n "$issue" ]]; then
gh issue edit "$issue" --body-file "$body"
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ jobs:
echo "GITHUB_METADATA_FETCHED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
- name: Export static site from restored data
run: npm run export -- --skip-seed
- name: Validate public content across every main tab
run: npm run --silent public:validate -- --output="$RUNNER_TEMP/public-integrity.json"
- name: Upload public integrity evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: pages-public-integrity-${{ github.run_id }}
path: ${{ runner.temp }}/public-integrity.json
if-no-files-found: ignore
retention-days: 7
- uses: actions/configure-pages@v6
- uses: actions/upload-pages-artifact@v5
with:
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/source-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ jobs:
run: |
set -euo pipefail
npm run export -- --skip-seed
npm run --silent public:validate -- --output="$RUNNER_TEMP/public-integrity.json"
npm run --silent public:fingerprint -- --include-sources > "$RUNNER_TEMP/public-after.sha256"
if cmp -s "$RUNNER_TEMP/public-before.sha256" "$RUNNER_TEMP/public-after.sha256"; then
echo "No material public source changes."
Expand All @@ -83,6 +84,15 @@ jobs:
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

- name: Upload public integrity evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: source-audit-public-integrity-${{ github.run_id }}
path: ${{ runner.temp }}/public-integrity.json
if-no-files-found: ignore
retention-days: 14

- name: Write privacy-safe snapshot with accumulated checks
run: npm run db:snapshot -- write

Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@

### 开发中

- 暂无。

## [0.11.0] - 2026-07-14

### 系统完整性、自动化与公开质量

- 新增跨 Tab 公开内容一致性门禁:中文与英文首页、趋势、事件脉络、Scout、角色、来源目录、来源动态和 Changelog 必须同时拥有内容、正确 canonical / hreflang、可解析结构化数据和完整 Sitemap,失败会阻断 CI、Data Refresh、Source Audit 与 Pages 发布。
- 静态导出改为批量读取 Event 的 Evidence、Track 与 Actor 关系,消除按事件重复查询;Timeline 卡片改为真实可抓取链接,保留抽屉交互与无 JavaScript 详情页回退。
- SEO / GEO 补齐 WebSite、Organization、Article、BreadcrumbList、CollectionPage、ItemList 与来源目录 Dataset 结构化数据;各主页面使用独立摘要,Sitemap 对非 ASCII 路径进行标准 URL 编码。
- JSON-LD 改为有效 JSON 并安全转义脚本边界字符;管理台动态内容统一文本转义,TypeScript 启用未使用代码检查,生产静态文件依赖升级到已修复路径穿越问题的版本。

- 事件脉络取消“每月必须补足 4 篇论文”的连续密度目标;论文必须通过标题身份核验和年龄分层的研究影响门禁才能展示,月份允许没有论文。Data Refresh 在自动发布前使用 OpenAlex 批量生成可审计影响报告,失败时不继续发布。
- 事件脉络左侧年份与月份改为可选择的时间导航;选择后会按筛选结果定位并平滑滚动到目标月份,同时按需加载较早内容并保留可分享的 `date` 查询参数。
- 健康监控告警增加稳定指纹、七天冷却、人工触发默认 dry-run 与 AI 结构化复核;站点不可达、监控崩溃和超过 72 小时的数据陈旧仍由确定性硬门禁立即告警,AI 不可压制。
- 新增 DeepSeek V4 Flash 事件收敛步骤:只处理已具备一手证据且通过事实门禁的 review Event,AI 结构化输出必须经过 Schema、证据 URL、主线和 readiness 校验后才能进入自动发布。
- GitHub AI 周报改为“核心判断、判断变化、角色决策卡、仍需验证、下周观察”结构;AI 只读取公开 DTO,建议必须包含 48 小时动作和可证伪停止条件,最终 Markdown 由程序确定性渲染。
- Data Refresh 通过 Actions Secret 注入 DeepSeek 凭据,运行摘要只保留数量、失败码、输入哈希和 token 用量,不把密钥、原始响应或私有数据写入仓库与日志。
Expand All @@ -20,6 +34,20 @@
- 趋势阶段新增改为极低频的重大里程碑晋级:确定性门禁先检查近 14 天、里程碑角色、阶段间隔、高影响高置信事件、独立信源和 Tier 1 证据,再由 DeepSeek V4 Pro 以高强度思考审慎判断,默认保持原阶段。
- 只有模型置信度、Event / Evidence / Source allowlist、阶段边界和本地一致性校验全部通过,才会先创建带 `stage:milestone` 标签的专属 GitHub Issue,再把新阶段及其证据、信源和 Issue 回链原子写入版本化数据。
- 重大阶段 Issue 详细说明事件、旧阶段为何不足、业务影响、中国位置、反向信号和下一验证;V4 Pro、Issue 或阶段落库失败只留下安全告警,不阻断快照增量提交与 Pages 更新。
- 事件脉络在公开 Event 超过 500 条时按月份懒挂载旧内容;单月表层条目超过 6 个时默认收起,并支持月级展开、筛选与搜索联动。
- 论文从“同日论文组”收敛为只在“全部变化 / 论文与研究”出现的当月论文组,最多展示 10 篇且不占单月 6 个普通事件名额;只有通过 OpenAlex 影响、同行认可或产业采用证据门禁的论文才进入 Timeline,数量不足时不凑数。
- 保留 2022 年 11 月以来的研究候选作为可审计归档,不再把月份连续或候选数量当作完成指标;候选均绑定论文一手链接,公开展示继续服从外部影响证据、可信度、价值和分析深度门禁。
- 全站可点击块不再保留点击后的 outline;单一一手证据统一显示为“官方资料”,不再使用“单一官方资料”措辞。
- 修复移动端事件抽屉关闭后再打开新事件仍停留在旧滚动位置的问题;每次打开事件都会从抽屉顶部开始。
- 事件抽屉仅在存在至少两条发展记录时展示“事情如何发展”,避免单条内容与原始证据重复。
- 重构中英文 README 的项目门面:用三个决策问题和可直达的产品地图说明价值,并按最新机器证据区分目录收录、有效观测、active 生产与公开发布状态。
- 来源动态瀑布流卡片升级为整卡可点击链接,增加 hover / 键盘焦点反馈,轻量高亮分类、地域与来源,并统一“查看原文”外链 icon 对齐及底部居中的加载入口。
- 全站文案完成一轮逐页审校:删减模板化对比句和抽象内部术语,改写导航、页面说明、事件分析、来源状态、方法说明与管理台提示,让读者更快理解内容、证据范围和可执行操作。
- 优化公开站细节交互:事件抽屉改为独立内容滚动并重做信息层级,移动端时间线与趋势导航采用紧凑悬浮样式和平滑居中;来源动态用状态色区分论文与 Tier 1 来源,页脚补充可追溯快照链接和低调联系方式。
- 统一移动端顶部 Hero 的标题间距、换行与字号;默认趋势详情紧贴顶部导航,小屏不再用装饰动效挤压标题和说明。
- 长页滚动超过首屏后显示“回到顶部”按钮;桌面端固定在右下角,移动端避让底部导航与安全区,并兼容减少动效偏好。
- 资本与公司演化补充 5 个可持续采集的投资机构官方 Feed 与 10 个公司官方公告支撑的融资、战略投资和整合事件;新增来源保持 disabled + shadow。
- 移动端将来源动态、Scout、关键角色、模型成本、覆盖与来源目录收敛为 4–12 条一批,支持按批继续查看;搜索或筛选后自动回到紧凑首批,桌面端与无 JavaScript 输出仍保留完整列表。

## [0.10.0] - 2026-07-14

Expand Down
Loading