📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-07-13 02:44:46 +00:00
parent c1054eff0b
commit cb3f8713ee
16 changed files with 1935 additions and 10 deletions
+13 -1
View File
@@ -79,6 +79,18 @@ npx agentic-awesome-skills --agy
The npm installer uses a shallow, release-pinned clone by default so first-run installs stay lighter than a full repository history checkout while matching the published npm package version. Use `--tag main` only when you intentionally want the current repository tip. The npm installer uses a shallow, release-pinned clone by default so first-run installs stay lighter than a full repository history checkout while matching the published npm package version. Use `--tag main` only when you intentionally want the current repository tip.
### Focused single-skill install with GitHub CLI (preview)
GitHub CLI can preview and install one exact skill for Copilot and other supported hosts. Use an exact `SKILL.md` path in this large, mirrored repository so the selected source is unambiguous and discovery stays fast:
```bash
gh skill preview sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md
gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md \
--agent github-copilot --scope user --pin v14.2.0
```
`gh skill` support is currently a GitHub CLI preview and may change. Install a focused skill or plugin surface for the job; do not use `--all` unless you intentionally want every discovered canonical and mirrored skill.
### Verify the install ### Verify the install
```bash ```bash
@@ -142,7 +154,7 @@ Use the same repository, but install or invoke it in the way your host expects.
| Antigravity CLI (`agy`) | `npx agentic-awesome-skills --agy` | `/brainstorming help me plan a feature` | | Antigravity CLI (`agy`) | `npx agentic-awesome-skills --agy` | `/brainstorming help me plan a feature` |
| Kiro CLI | `npx agentic-awesome-skills --kiro` | `Use brainstorming to plan a feature` | | Kiro CLI | `npx agentic-awesome-skills --kiro` | `Use brainstorming to plan a feature` |
| Kiro IDE | `npx agentic-awesome-skills --path ~/.kiro/skills` | `Use @brainstorming to plan a feature` | | Kiro IDE | `npx agentic-awesome-skills --path ~/.kiro/skills` | `Use @brainstorming to plan a feature` |
| GitHub Copilot | _No installer — paste skills or rules manually_ | `Ask Copilot to use brainstorming to plan a feature` | | GitHub Copilot | `gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md --agent github-copilot --scope user --pin v14.2.0` (preview) | `Ask Copilot to use brainstorming to plan a feature` |
| OpenCode | `npx agentic-awesome-skills --path .agents/skills --category development,backend --risk safe,none` | `opencode run @brainstorming help me plan a feature` | | OpenCode | `npx agentic-awesome-skills --path .agents/skills --category development,backend --risk safe,none` | `opencode run @brainstorming help me plan a feature` |
| AdaL CLI | `npx agentic-awesome-skills --path .adal/skills` | `Use brainstorming to plan a feature` | | AdaL CLI | `npx agentic-awesome-skills --path .adal/skills` | `Use brainstorming to plan a feature` |
| Custom path | `npx agentic-awesome-skills --path ./my-skills` | Depends on your tool | | Custom path | `npx agentic-awesome-skills --path ./my-skills` | Depends on your tool |
+2 -2
View File
@@ -1,8 +1,8 @@
# Source # Source
- Repo: https://github.com/sickn33/antigravity-awesome-skills - Repo: https://github.com/sickn33/antigravity-awesome-skills
- Ref: 6b7501e33dd3fe4ff3715e5bd79dec2881e8b7d4 - Ref: 314c8c499af407db8160a3eeac9043b58dc9c41a
- Remove-Paths: - Remove-Paths:
- Snapshot: 2026-07-12 - Snapshot: 2026-07-13
- Sync-Mode: copy_skill_dirs - Sync-Mode: copy_skill_dirs
- Notes: vendored into playbook branch thirdparty/skill - Notes: vendored into playbook branch thirdparty/skill
@@ -17,6 +17,10 @@ function bundleDocUrl(): string {
return `${repoBaseUrl}/blob/main/docs/users/bundles.md`; return `${repoBaseUrl}/blob/main/docs/users/bundles.md`;
} }
function gettingStartedDocUrl(): string {
return `${repoBaseUrl}/blob/main/docs/users/getting-started.md`;
}
export function Plugins(): React.ReactElement { export function Plugins(): React.ReactElement {
usePageMeta(buildPluginsMeta(specializedPlugins.length)); usePageMeta(buildPluginsMeta(specializedPlugins.length));
@@ -36,7 +40,7 @@ export function Plugins(): React.ReactElement {
Choose the focused AAS plugin for your AI coding workflow Choose the focused AAS plugin for your AI coding workflow
</h1> </h1>
<p className="mt-4 max-w-4xl text-sm leading-relaxed text-slate-600 sm:text-base dark:text-slate-300"> <p className="mt-4 max-w-4xl text-sm leading-relaxed text-slate-600 sm:text-base dark:text-slate-300">
AAS specialized plugins are focused, domain-specific distributions of the 1,550+ skill library. AAS specialized plugins are focused, domain-specific distributions of the full skill library.
Start here when you know the job: web apps, security, data analytics, documents, DevOps, QA, Start here when you know the job: web apps, security, data analytics, documents, DevOps, QA,
OSS maintenance, mobile apps, automation, or agent and MCP systems. OSS maintenance, mobile apps, automation, or agent and MCP systems.
</p> </p>
@@ -55,6 +59,14 @@ export function Plugins(): React.ReactElement {
> >
Browse full skill catalog Browse full skill catalog
</Link> </Link>
<a
href={gettingStartedDocUrl()}
target="_blank"
rel="noreferrer"
className="inline-flex items-center justify-center rounded-lg border border-slate-400/80 bg-white/80 px-4 py-2.5 text-sm font-semibold text-slate-900 shadow-[inset_0_1px_0_rgba(255,255,255,0.9),0_10px_20px_-16px_rgba(15,23,42,0.7)] transition-colors hover:border-slate-500 hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-800/70 dark:text-slate-100 dark:hover:bg-slate-700"
>
Install one skill with GitHub CLI
</a>
</div> </div>
</section> </section>
@@ -11,6 +11,12 @@ describe('Plugins', () => {
expect(screen.getByText('AAS Web App Builder')).toBeInTheDocument(); expect(screen.getByText('AAS Web App Builder')).toBeInTheDocument();
expect(screen.getByText('AAS Security Engineer')).toBeInTheDocument(); expect(screen.getByText('AAS Security Engineer')).toBeInTheDocument();
expect(screen.getByText('AAS Marketing, SEO & Growth')).toBeInTheDocument(); expect(screen.getByText('AAS Marketing, SEO & Growth')).toBeInTheDocument();
expect(screen.getByText(/distributions of the full skill library/i)).toBeInTheDocument();
expect(screen.queryByText(/1,550\+/i)).not.toBeInTheDocument();
expect(screen.getByRole('link', { name: /Install one skill with GitHub CLI/i })).toHaveAttribute(
'href',
expect.stringContaining('docs/users/getting-started.md'),
);
expect(screen.getByText(/Plugins, bundles, and workflows serve different decisions/i)).toBeInTheDocument(); expect(screen.getByText(/Plugins, bundles, and workflows serve different decisions/i)).toBeInTheDocument();
expect(document.title).toContain('AAS Specialized Plugins'); expect(document.title).toContain('AAS Specialized Plugins');
expect(document.querySelector('meta[name="description"]')).toHaveAttribute( expect(document.querySelector('meta[name="description"]')).toHaveAttribute(
@@ -44,6 +44,16 @@ If you see a 404 error, use: `npx github:sickn33/agentic-awesome-skills`
git clone https://github.com/sickn33/agentic-awesome-skills.git .agent/skills git clone https://github.com/sickn33/agentic-awesome-skills.git .agent/skills
``` ```
**Option C — one exact skill with GitHub CLI (preview):**
```bash
gh skill preview sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md
gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md \
--agent github-copilot --scope user --pin v14.2.0
```
GitHub CLI skill support is currently in preview. In this large repository, use an exact `SKILL.md` path to avoid ambiguous canonical/plugin mirrors and unnecessary full-tree discovery. Avoid `--all` unless you intentionally want every discovered skill.
### 2. Pick Your Persona ### 2. Pick Your Persona
Find the bundle that matches your role (see [bundles.md](bundles.md)): Find the bundle that matches your role (see [bundles.md](bundles.md)):
@@ -110,7 +120,7 @@ Once installed, just talk to your AI naturally.
| **Cursor** | ✅ Native | `.cursor/skills/` | | **Cursor** | ✅ Native | `.cursor/skills/` |
| **OpenCode** | ✅ Full Support | `.agents/skills/` (prefer reduced installs with `--risk`, `--category`, or `--tags`) | | **OpenCode** | ✅ Full Support | `.agents/skills/` (prefer reduced installs with `--risk`, `--category`, or `--tags`) |
| **AdaL CLI** | ✅ Full Support | `.adal/skills/` | | **AdaL CLI** | ✅ Full Support | `.adal/skills/` |
| **Copilot** | ⚠️ Text Only | Manual copy-paste | | **Copilot** | ✅ Native (preview) | `gh skill install ... --agent github-copilot` at project or user scope |
--- ---
@@ -25,7 +25,14 @@ Nó giống như việc sở hữu một thư viện - tất cả sách đều
-**Cursor** (IDE tích hợp AI) -**Cursor** (IDE tích hợp AI)
-**Antigravity IDE** -**Antigravity IDE**
-**OpenCode** -**OpenCode**
- ⚠️ **GitHub Copilot** (Hỗ trợ một phần qua việc copy-paste) - 🧪 **GitHub Copilot** (hỗ trợ preview qua GitHub CLI `gh skill`)
Với Copilot, `gh skill` hiện vẫn ở trạng thái preview. Repository lớn này có cả bản canonical và bản mirror trong plugin, vì vậy hãy dùng đường dẫn chính xác; chỉ dùng `--all` khi bạn thực sự muốn cài mọi bản được phát hiện:
```bash
gh skill preview sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md
gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md --agent github-copilot --scope user --pin v14.2.0
```
### Những kỹ năng này có được sử dụng miễn phí không? ### Những kỹ năng này có được sử dụng miễn phí không?
@@ -78,7 +78,14 @@ Sau khi cài đặt, bạn chỉ cần trò chuyện với AI một cách tự n
| **Cursor** | ✅ Hỗ trợ gốc | `.cursor/skills/` | | **Cursor** | ✅ Hỗ trợ gốc | `.cursor/skills/` |
| **OpenCode** | ✅ Hỗ trợ đầy đủ | `.agents/skills/` | | **OpenCode** | ✅ Hỗ trợ đầy đủ | `.agents/skills/` |
| **AdaL CLI** | ✅ Hỗ trợ đầy đủ | `.adal/skills/` | | **AdaL CLI** | ✅ Hỗ trợ đầy đủ | `.adal/skills/` |
| **Copilot** | ⚠️ Chỉ văn bản | Copy-paste thủ công | | **Copilot** | 🧪 Hỗ trợ preview qua `gh skill` | Dùng GitHub CLI với đường dẫn skill chính xác |
> **GitHub Copilot (preview):** `gh skill` hiện vẫn ở trạng thái preview. Vì repository lớn này có cả bản canonical và bản mirror trong plugin, hãy dùng đường dẫn chính xác để tránh nhầm lẫn; chỉ dùng `--all` khi bạn thực sự muốn cài mọi bản được phát hiện:
>
> ```bash
> gh skill preview sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md
> gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md --agent github-copilot --scope user --pin v14.2.0
> ```
--- ---
@@ -166,7 +166,7 @@ AI 代理很聪明,但仍需要**特定于任务的操作指令**。技能是
| **Antigravity** | IDE | `(Agent Mode) Use skill...` | 全局:`~/.agents/skills/` · 工作区:`.agent/skills/` | | **Antigravity** | IDE | `(Agent Mode) Use skill...` | 全局:`~/.agents/skills/` · 工作区:`.agent/skills/` |
| **Antigravity CLI (`agy`)** | CLI | `/skill-name help me...` | `~/.gemini/antigravity-cli/skills/` | | **Antigravity CLI (`agy`)** | CLI | `/skill-name help me...` | `~/.gemini/antigravity-cli/skills/` |
| **Cursor** | IDE | `@skill-name (in Chat)` | `.cursor/skills/` | | **Cursor** | IDE | `@skill-name (in Chat)` | `.cursor/skills/` |
| **Copilot** | Ext | `(Paste content manually)` | N/A | | **Copilot** | Ext`gh skill` 预览) | `gh skill preview sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md` | GitHub CLI `gh skill` |
| **OpenCode** | CLI | `opencode run @skill-name` | `.agents/skills/` | | **OpenCode** | CLI | `opencode run @skill-name` | `.agents/skills/` |
| **AdaL CLI** | CLI | `(Auto) Skills load on-demand` | `.adal/skills/` | | **AdaL CLI** | CLI | `(Auto) Skills load on-demand` | `.adal/skills/` |
@@ -233,11 +233,13 @@ Codex 插件通过仓库本地插件入口指向相同的精选 `skills/` 树,
| Antigravity CLI (`agy`) | `npx agentic-awesome-skills --agy` | `/brainstorming help me plan a feature` | | Antigravity CLI (`agy`) | `npx agentic-awesome-skills --agy` | `/brainstorming help me plan a feature` |
| Kiro CLI | `npx agentic-awesome-skills --kiro` | `Use brainstorming to plan a feature` | | Kiro CLI | `npx agentic-awesome-skills --kiro` | `Use brainstorming to plan a feature` |
| Kiro IDE | `npx agentic-awesome-skills --path ~/.kiro/skills` | `Use @brainstorming to plan a feature` | | Kiro IDE | `npx agentic-awesome-skills --path ~/.kiro/skills` | `Use @brainstorming to plan a feature` |
| GitHub Copilot | _无安装器 — 手动粘贴技能或规则_ | `Ask Copilot to use brainstorming to plan a feature` | | GitHub Copilot | `gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md --agent github-copilot --scope user --pin v14.2.0`(预览) | `Ask Copilot to use brainstorming to plan a feature` |
| OpenCode | `npx agentic-awesome-skills --path .agents/skills --category development,backend --risk safe,none` | `opencode run @brainstorming help me plan a feature` | | OpenCode | `npx agentic-awesome-skills --path .agents/skills --category development,backend --risk safe,none` | `opencode run @brainstorming help me plan a feature` |
| AdaL CLI | `npx agentic-awesome-skills --path .adal/skills` | `Use brainstorming to plan a feature` | | AdaL CLI | `npx agentic-awesome-skills --path .adal/skills` | `Use brainstorming to plan a feature` |
| 自定义路径 | `npx agentic-awesome-skills --path ./my-skills` | 取决于你的工具 | | 自定义路径 | `npx agentic-awesome-skills --path ./my-skills` | 取决于你的工具 |
> **GitHub Copilot(预览)**GitHub CLI 的 `gh skill` 目前处于 preview 阶段。由于本仓库规模较大且同时包含 canonical 与 plugin 镜像,请使用精确路径,避免歧义;除非你确实要安装所有发现的副本,否则不要使用 `--all`。
## 按工具查看最佳技能 ## 按工具查看最佳技能
如果你想要比"浏览所有 1,936+ 技能"更快的答案,请从工具特定指南开始: 如果你想要比"浏览所有 1,936+ 技能"更快的答案,请从工具特定指南开始:
@@ -107,7 +107,14 @@ git clone https://github.com/sickn33/agentic-awesome-skills.git .agent/skills
| **Cursor** | ✅ 原生支持 | `.cursor/skills/` | | **Cursor** | ✅ 原生支持 | `.cursor/skills/` |
| **OpenCode** | ✅ 完全支持 | `.agents/skills/`(建议用 `--risk``--category``--tags` 做缩小安装) | | **OpenCode** | ✅ 完全支持 | `.agents/skills/`(建议用 `--risk``--category``--tags` 做缩小安装) |
| **AdaL CLI** | ✅ 完全支持 | `.adal/skills/` | | **AdaL CLI** | ✅ 完全支持 | `.adal/skills/` |
| **Copilot** | ⚠️ 仅文本 | 手动复制粘贴 | | **Copilot** | 🧪 `gh skill` 预览支持 | 使用 GitHub CLI 的 `gh skill`,并指定精确技能路径 |
> **GitHub Copilot(预览)**`gh skill` 目前处于 preview 阶段。由于本仓库规模较大且同时包含 canonical 与 plugin 镜像,请使用精确路径,避免歧义;除非你确实要安装所有发现的副本,否则不要使用 `--all`:
>
> ```bash
> gh skill preview sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md
> gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md --agent github-copilot --scope user --pin v14.2.0
> ```
--- ---
+3
View File
@@ -34,6 +34,9 @@
"build": "npm run chain && npm run catalog", "build": "npm run chain && npm run catalog",
"check:stale-claims": "node tools/scripts/run-python.js tools/scripts/check_stale_claims.py", "check:stale-claims": "node tools/scripts/run-python.js tools/scripts/check_stale_claims.py",
"check:live-seo": "node tools/scripts/check-live-seo-geo.js", "check:live-seo": "node tools/scripts/check-live-seo-geo.js",
"pages:redirect-bridge": "node tools/scripts/generate-pages-redirect-bridge.js",
"traffic:normalize": "node tools/scripts/normalize_traffic_snapshots.js",
"traffic:migration-readiness": "node tools/scripts/audit_search_migration_readiness.js",
"check:warning-budget": "node tools/scripts/run-python.js tools/scripts/check_validation_warning_budget.py", "check:warning-budget": "node tools/scripts/run-python.js tools/scripts/check_validation_warning_budget.py",
"check:readme-credits": "node tools/scripts/run-python.js tools/scripts/check_readme_credits.py", "check:readme-credits": "node tools/scripts/run-python.js tools/scripts/check_readme_credits.py",
"audit:consistency": "node tools/scripts/run-python.js tools/scripts/audit_consistency.py", "audit:consistency": "node tools/scripts/run-python.js tools/scripts/audit_consistency.py",
@@ -0,0 +1,399 @@
#!/usr/bin/env node
'use strict';
/*
* This audit deliberately uses only saved evidence. It is not a substitute for
* a live deploy check and must never turn a legacy-only dashboard capture into
* a green migration.
*/
const fs = require('fs');
const path = require('path');
const DEFAULT_LEGACY_PACKAGE = 'antigravity-awesome-skills';
const DEFAULT_CURRENT_PACKAGE = 'agentic-awesome-skills';
const DEFAULT_CURRENT_PAGES_URL = 'https://sickn33.github.io/agentic-awesome-skills/';
const DEFAULT_LEGACY_PAGES_URL = 'https://sickn33.github.io/antigravity-awesome-skills/';
const SNAPSHOT_DIRECTORY = /^\d{4}-\d{2}-\d{2}$/;
const DEFAULT_MAX_EVIDENCE_AGE_DAYS = 7;
const DASHBOARD_FILES = {
gsc: {
file: 'google-search-console.json',
hosts: new Set(['search.google.com']),
pathPrefix: '/search-console/',
primaryParam: 'resource_id',
},
bing: {
file: 'bing-webmaster-search-performance.json',
hosts: new Set(['bing.com', 'www.bing.com']),
pathPrefix: '/webmasters/',
primaryParam: 'siteUrl',
},
};
function normaliseUrl(value) {
if (typeof value !== 'string' || !value.trim()) return null;
try {
const url = new URL(value.trim());
if (url.protocol !== 'https:' && url.protocol !== 'http:') return null;
url.hash = '';
url.search = '';
return url.toString();
} catch (_) {
return null;
}
}
function withTrailingSlash(value) {
const url = normaliseUrl(value);
return url && url.endsWith('/') ? url : url && `${url}/`;
}
function readJson(filePath, errors, label) {
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
errors.push(`${label}: ${error.message}`);
return null;
}
}
function parseSitemap(filePath, errors) {
let source;
try {
source = fs.readFileSync(filePath, 'utf8');
} catch (error) {
errors.push(`current sitemap: ${error.message}`);
return [];
}
const locations = [...source.matchAll(/<loc>\s*([^<\s]+)\s*<\/loc>/gi)]
.map((match) => withTrailingSlash(match[1]))
.filter(Boolean);
if (!locations.length) errors.push('current sitemap: contains no valid <loc> URLs');
return [...new Set(locations)].sort();
}
function inferLegacyPagesUrl(currentPagesUrl) {
if (!currentPagesUrl) return null;
const inferred = withTrailingSlash(currentPagesUrl.replace('/agentic-awesome-skills/', '/antigravity-awesome-skills/'));
return inferred && inferred !== currentPagesUrl ? inferred : null;
}
function isValidIsoDate(value) {
if (typeof value !== 'string' || !SNAPSHOT_DIRECTORY.test(value)) return false;
const [year, month, day] = value.split('-').map(Number);
const parsed = new Date(Date.UTC(year, month - 1, day));
return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month - 1 && parsed.getUTCDate() === day;
}
function readSnapshots(snapshotRoot, filename, errors) {
let entries;
try {
entries = fs.readdirSync(snapshotRoot, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && isValidIsoDate(entry.name))
.map((entry) => entry.name)
.sort();
} catch (error) {
errors.push(`snapshot root: ${error.message}`);
return [];
}
return entries.flatMap((directory) => {
const filePath = path.join(snapshotRoot, directory, filename);
if (!fs.existsSync(filePath)) return [];
const data = readJson(filePath, errors, `${directory}/${filename}`);
return data && typeof data === 'object' ? [{ directory, filePath, data }] : [];
});
}
function propertyEvidence(data, dashboardConfig) {
const signals = [];
for (const [source, raw] of [
['source_property', data.source_property],
['property_url', data.property_url],
['propertyUrl', data.propertyUrl],
['site_url', data.site_url],
['siteUrl', data.siteUrl],
]) {
const value = withTrailingSlash(raw);
if (value) signals.push({ source, value });
}
let dashboardValid = false;
let dashboardValidationReason = 'missing dashboard_url';
if (typeof data.dashboard_url === 'string') {
try {
const dashboard = new URL(data.dashboard_url);
const hostValid = dashboard.protocol === 'https:' && dashboardConfig.hosts.has(dashboard.hostname.toLowerCase());
const pathValid = dashboard.pathname.startsWith(dashboardConfig.pathPrefix);
const primary = withTrailingSlash(dashboard.searchParams.get(dashboardConfig.primaryParam));
dashboardValid = Boolean(hostValid && pathValid && primary);
dashboardValidationReason = dashboardValid ? null : 'dashboard host, path, or primary property parameter is invalid';
if (dashboardValid) {
for (const param of ['resource_id', 'siteUrl']) {
const value = withTrailingSlash(dashboard.searchParams.get(param));
if (value) signals.push({ source: `dashboard_url:${param}`, value });
}
}
} catch (_) {
dashboardValidationReason = 'dashboard_url is malformed';
}
}
const distinct = [...new Set(signals.map((signal) => signal.value))];
const intendedProperty = withTrailingSlash(data.intended_property);
const property = distinct.length === 1 ? distinct[0] : null;
return {
property,
signals,
property_conflict: distinct.length > 1,
intended_property: intendedProperty,
intended_matches_observed: !intendedProperty || (property !== null && intendedProperty === property),
dashboard_valid: dashboardValid,
dashboard_validation_reason: dashboardValidationReason,
};
}
function classifyProperty(property, currentPagesUrl, legacyPagesUrl) {
const value = withTrailingSlash(property);
if (!value) return 'unknown';
if (currentPagesUrl && value === currentPagesUrl) return 'current';
if (legacyPagesUrl && value === legacyPagesUrl) return 'legacy';
return 'other';
}
function validMetric(value) {
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
}
function captureFreshness(capturedAt, snapshotDate, asOfDate, maxAgeDays) {
const captured = typeof capturedAt === 'string' ? new Date(capturedAt) : null;
if (!captured || Number.isNaN(captured.getTime())) {
return { valid: false, age_days: null, reason: 'missing or invalid captured_at_utc' };
}
const capturedDate = captured.toISOString().slice(0, 10);
const ageDays = (Date.parse(`${asOfDate}T00:00:00Z`) - Date.parse(`${capturedDate}T00:00:00Z`)) / 86400000;
if (capturedDate !== snapshotDate) return { valid: false, age_days: ageDays, reason: 'capture date does not match snapshot directory' };
if (capturedDate > asOfDate) return { valid: false, age_days: ageDays, reason: 'capture is in the future' };
if (ageDays > maxAgeDays) return { valid: false, age_days: ageDays, reason: 'capture is stale' };
return { valid: true, age_days: ageDays, reason: null };
}
function dashboardEvidence(snapshotRoot, dashboardConfig, currentPagesUrl, legacyPagesUrl, errors, asOfDate, maxAgeDays) {
const evidence = readSnapshots(snapshotRoot, dashboardConfig.file, errors).map(({ directory, filePath, data }) => {
const property = propertyEvidence(data, dashboardConfig);
const signalPropertyClasses = [...new Set(property.signals.map((signal) =>
classifyProperty(signal.value, currentPagesUrl, legacyPagesUrl)))];
return {
snapshot: directory,
file: filePath,
status: data.status || 'unknown',
...property,
signal_property_classes: signalPropertyClasses,
property_class: classifyProperty(property.property, currentPagesUrl, legacyPagesUrl),
captured_at_utc: data.captured_at_utc || null,
freshness: captureFreshness(data.captured_at_utc, directory, asOfDate, maxAgeDays),
metrics_complete: validMetric(data?.totals?.clicks) && validMetric(data?.totals?.impressions),
};
});
const current = evidence.filter((entry) => entry.status === 'success'
&& entry.property_class === 'current'
&& !entry.property_conflict
&& entry.intended_matches_observed
&& entry.dashboard_valid
&& entry.freshness.valid
&& entry.metrics_complete);
const legacy = evidence.filter((entry) => entry.status === 'success' && entry.property_class === 'legacy');
return {
status: current.length ? 'pass' : 'fail',
current_evidence: current,
rejected_current_evidence: evidence.filter((entry) =>
(entry.property_class === 'current' || entry.signal_property_classes.includes('current')) && !current.includes(entry)),
legacy_evidence: legacy,
other_evidence: evidence.filter((entry) => entry.property_class === 'other' || entry.property_class === 'unknown'),
message: current.length
? 'Fresh, schema-complete saved current-property evidence is present.'
: 'No fresh, schema-complete saved evidence for the exact current property; legacy-only or spoofed evidence is not a migration fix.',
};
}
function manifestPairs(payload) {
if (Array.isArray(payload)) return payload;
if (!payload || typeof payload !== 'object') return [];
if (Array.isArray(payload.redirects)) return payload.redirects;
if (Array.isArray(payload.routes)) return payload.routes;
if (payload.routes && typeof payload.routes === 'object') {
return Object.entries(payload.routes).map(([from, to]) => ({ from, to }));
}
if (payload.redirects && typeof payload.redirects === 'object') {
return Object.entries(payload.redirects).map(([from, to]) => ({ from, to }));
}
return [];
}
function redirectCoverage(manifestPath, currentUrls, currentPagesUrl, legacyPagesUrl, errors) {
if (!manifestPath) return { status: 'fail', expected: currentUrls.length, covered: 0, missing: currentUrls, message: 'No redirect manifest was supplied.' };
const payload = readJson(manifestPath, errors, 'redirect manifest');
if (!payload) return { status: 'fail', expected: currentUrls.length, covered: 0, missing: currentUrls, message: 'Redirect manifest is unreadable.' };
const redirects = new Map();
const duplicates = [];
const invalid = [];
for (const pair of manifestPairs(payload)) {
const from = withTrailingSlash(pair && (pair.from || pair.source || pair.legacy));
const to = withTrailingSlash(pair && (pair.to || pair.destination || pair.current));
if (!from || !to) {
invalid.push(pair);
} else if (redirects.has(from)) {
duplicates.push(from);
} else {
redirects.set(from, to);
}
}
const expectedPairs = new Map(currentUrls.map((currentUrl) => [
withTrailingSlash(currentUrl.replace(currentPagesUrl, legacyPagesUrl)),
currentUrl,
]));
const missing = currentUrls.filter((currentUrl) => {
const legacyUrl = withTrailingSlash(currentUrl.replace(currentPagesUrl, legacyPagesUrl));
return redirects.get(legacyUrl) !== currentUrl;
});
const unexpected = [...redirects.entries()]
.filter(([from, to]) => expectedPairs.get(from) !== to)
.map(([from, to]) => ({ from, to }));
const exact = currentUrls.length && !missing.length && !duplicates.length && !invalid.length && !unexpected.length
&& redirects.size === expectedPairs.size;
return {
status: exact ? 'pass' : 'fail',
expected: currentUrls.length,
covered: currentUrls.length - missing.length,
missing,
duplicates,
invalid,
unexpected,
message: exact ? 'Redirect manifest maps every expected legacy route exactly once and contains no extras.' : 'Redirect manifest does not exactly cover the expected legacy route set once each.',
};
}
function packageIdentity(currentPackagePath, legacyPackagePath, currentPackageName, errors) {
const current = readJson(currentPackagePath, errors, 'current package metadata');
const legacy = legacyPackagePath ? readJson(legacyPackagePath, errors, 'legacy package metadata') : null;
const currentName = currentPackageName || DEFAULT_CURRENT_PACKAGE;
const currentOk = Boolean(current && current.name === currentName && typeof current.version === 'string' && current.version);
const explicitReplacement = legacy && (legacy.replacementPackage || legacy.replacement_package);
const escapedName = currentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const deprecatedMentionsExactPackage = typeof legacy?.deprecated === 'string'
&& new RegExp(`(^|[^a-z0-9_-])${escapedName}([^a-z0-9_-]|$)`, 'i').test(legacy.deprecated);
const legacyOk = Boolean(legacy
&& legacy.name === DEFAULT_LEGACY_PACKAGE
&& typeof legacy.version === 'string'
&& (explicitReplacement === currentName || deprecatedMentionsExactPackage));
return {
status: currentOk && legacyOk ? 'pass' : 'fail',
current: current ? { name: current.name || null, version: current.version || null } : null,
legacy: legacy ? { name: legacy.name || null, version: legacy.version || null, deprecated: legacy.deprecated || null } : null,
message: currentOk && legacyOk
? 'Current package identity and explicit legacy migration path are present.'
: 'Both a current package identity and an explicit legacy-to-current package migration path are required.',
};
}
function auditMigrationReadiness(options = {}) {
const repoRoot = path.resolve(options.repoRoot || path.resolve(__dirname, '..', '..'));
const errors = [];
const sitemapPath = path.resolve(repoRoot, options.sitemapPath || 'apps/web-app/public/sitemap.xml');
const snapshotRoot = path.resolve(repoRoot, options.snapshotRoot || '.codex/traffic-snapshots');
const currentPackagePath = path.resolve(repoRoot, options.currentPackagePath || 'package.json');
const asOfDate = options.asOfDate || new Date().toISOString().slice(0, 10);
const maxEvidenceAgeDays = Number(options.maxEvidenceAgeDays ?? DEFAULT_MAX_EVIDENCE_AGE_DAYS);
if (!isValidIsoDate(asOfDate)) throw new Error(`invalid as-of date: ${asOfDate}`);
if (!Number.isFinite(maxEvidenceAgeDays) || maxEvidenceAgeDays < 0) throw new Error('max evidence age must be a non-negative number');
const currentUrls = parseSitemap(sitemapPath, errors);
const currentPagesUrl = withTrailingSlash(options.currentPagesUrl || DEFAULT_CURRENT_PAGES_URL);
const legacyPagesUrl = withTrailingSlash(options.legacyPagesUrl || DEFAULT_LEGACY_PAGES_URL || inferLegacyPagesUrl(currentPagesUrl));
const identitiesDistinct = Boolean(currentPagesUrl && legacyPagesUrl && currentPagesUrl !== legacyPagesUrl);
const currentSitemap = {
status: identitiesDistinct && currentUrls.includes(currentPagesUrl) && currentUrls.every((url) => url.startsWith(currentPagesUrl)) ? 'pass' : 'fail',
current_pages_url: currentPagesUrl,
urls: currentUrls,
message: identitiesDistinct && currentUrls.includes(currentPagesUrl) && currentUrls.every((url) => url.startsWith(currentPagesUrl))
? 'Current sitemap includes its root and contains only the configured current Pages identity.'
: 'Current sitemap is missing its root, malformed, uses identical legacy/current identities, or includes URLs outside the configured current Pages identity.',
};
const checks = {
identity_anchors: {
status: identitiesDistinct ? 'pass' : 'fail',
current_package_name: options.currentPackageName || DEFAULT_CURRENT_PACKAGE,
current_pages_url: currentPagesUrl,
legacy_pages_url: legacyPagesUrl,
message: identitiesDistinct ? 'Configured current and legacy identities are present and distinct.' : 'Current and legacy Pages identities must be explicit and distinct.',
},
current_sitemap_identity: currentSitemap,
google_search_console: dashboardEvidence(snapshotRoot, DASHBOARD_FILES.gsc, currentPagesUrl, legacyPagesUrl, errors, asOfDate, maxEvidenceAgeDays),
bing_webmaster: dashboardEvidence(snapshotRoot, DASHBOARD_FILES.bing, currentPagesUrl, legacyPagesUrl, errors, asOfDate, maxEvidenceAgeDays),
redirect_manifest_coverage: redirectCoverage(
options.redirectManifestPath && path.resolve(repoRoot, options.redirectManifestPath), currentUrls, currentPagesUrl, legacyPagesUrl, errors,
),
npm_identities: packageIdentity(
currentPackagePath,
options.legacyPackagePath && path.resolve(repoRoot, options.legacyPackagePath),
options.currentPackageName,
errors,
),
};
const failedChecks = Object.entries(checks).filter(([, check]) => check.status !== 'pass').map(([name]) => name);
return {
schema_version: 1,
status: failedChecks.length || errors.length ? 'not_ready' : 'ready',
evidence_policy: { as_of_date: asOfDate, max_age_days: maxEvidenceAgeDays },
identities: { current_pages_url: currentPagesUrl, legacy_pages_url: legacyPagesUrl },
checks,
third_party_state: {
status: 'not_assessed',
message: 'Third-party directories are outside this local audit. A request or partial third-party update is not reported as fixed.',
},
errors,
failed_checks: failedChecks,
};
}
function writeJsonAtomically(filePath, value) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
try {
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
fs.renameSync(temporary, filePath);
} finally {
if (fs.existsSync(temporary)) fs.unlinkSync(temporary);
}
}
function parseArgs(argv) {
const options = {};
const aliases = {
'--repo-root': 'repoRoot', '--snapshot-root': 'snapshotRoot', '--sitemap': 'sitemapPath',
'--redirect-manifest': 'redirectManifestPath', '--current-package': 'currentPackagePath',
'--legacy-package': 'legacyPackagePath', '--current-pages-url': 'currentPagesUrl',
'--legacy-pages-url': 'legacyPagesUrl', '--current-package-name': 'currentPackageName', '--output': 'outputPath',
'--as-of': 'asOfDate', '--max-evidence-age-days': 'maxEvidenceAgeDays',
};
for (let index = 0; index < argv.length; index += 1) {
const key = aliases[argv[index]];
if (!key || !argv[index + 1] || argv[index + 1].startsWith('--')) throw new Error(`Unknown option or missing value: ${argv[index]}`);
options[key] = argv[index + 1];
index += 1;
}
return options;
}
if (require.main === module) {
try {
const options = parseArgs(process.argv.slice(2));
const report = auditMigrationReadiness(options);
const outputPath = path.resolve(options.repoRoot || path.resolve(__dirname, '..', '..'), options.outputPath || '.codex/traffic-snapshots/migration-readiness.json');
writeJsonAtomically(outputPath, report);
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
process.exitCode = report.status === 'ready' ? 0 : 1;
} catch (error) {
process.stderr.write(`migration readiness audit failed: ${error.message}\n`);
process.exitCode = 2;
}
}
module.exports = { auditMigrationReadiness, parseArgs, redirectCoverage, writeJsonAtomically };
@@ -0,0 +1,293 @@
#!/usr/bin/env node
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const REPO_ROOT = path.resolve(__dirname, '..', '..');
const DEFAULT_SITEMAP = path.join(REPO_ROOT, 'apps', 'web-app', 'public', 'sitemap.xml');
const DEFAULT_CURRENT_BASE = 'https://sickn33.github.io/agentic-awesome-skills/';
const DEFAULT_LEGACY_BASE = 'https://sickn33.github.io/antigravity-awesome-skills/';
const DEFAULT_EXPECTED_ROUTES = 46;
const SAFE_SEGMENT = /^[A-Za-z0-9._~-]+$/;
function sha256(value) {
return crypto.createHash('sha256').update(value).digest('hex');
}
function htmlEscape(value) {
return String(value)
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;');
}
function xmlEscape(value) {
return htmlEscape(value);
}
function xmlUnescape(value) {
const entities = {
amp: '&',
lt: '<',
gt: '>',
quot: '"',
apos: "'",
};
return value.replace(/&(amp|lt|gt|quot|apos);/g, (_, entity) => entities[entity]);
}
function canonicalBase(value, label) {
let url;
try {
url = new URL(value);
} catch (_) {
throw new Error(`${label} must be an absolute URL`);
}
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {
throw new Error(`${label} must be a credential-free HTTPS URL without query or fragment`);
}
if (!url.pathname.endsWith('/')) throw new Error(`${label} must end with a slash`);
return url;
}
function parseSitemap(source) {
const rawLocations = [...source.matchAll(/<loc>\s*([^<]+?)\s*<\/loc>/gi)].map((match) => xmlUnescape(match[1].trim()));
if (!rawLocations.length) throw new Error('sitemap contains no <loc> URLs');
if (new Set(rawLocations).size !== rawLocations.length) throw new Error('sitemap contains duplicate <loc> URLs');
return rawLocations;
}
function safeRelativeRoute(currentUrl, currentBase) {
if (currentUrl.protocol !== 'https:' || currentUrl.origin !== currentBase.origin || currentUrl.search || currentUrl.hash) {
throw new Error(`sitemap URL is outside the current HTTPS identity: ${currentUrl.toString()}`);
}
if (!currentUrl.pathname.startsWith(currentBase.pathname) || !currentUrl.pathname.endsWith('/')) {
throw new Error(`sitemap URL is outside the current base path or lacks a trailing slash: ${currentUrl.toString()}`);
}
const relative = currentUrl.pathname.slice(currentBase.pathname.length);
const segments = relative.split('/').filter(Boolean);
for (const segment of segments) {
if (segment === '.' || segment === '..' || !SAFE_SEGMENT.test(segment)) {
throw new Error(`sitemap URL contains an unsafe path segment: ${currentUrl.toString()}`);
}
}
return segments.join('/');
}
function outputRelativePath(legacyBase, relativeRoute) {
const baseSegments = legacyBase.pathname.split('/').filter(Boolean);
for (const segment of baseSegments) {
if (segment === '.' || segment === '..' || !SAFE_SEGMENT.test(segment)) {
throw new Error('legacy base contains an unsafe path segment');
}
}
return path.posix.join(...baseSegments, relativeRoute, 'index.html');
}
function redirectHtml(destination) {
const escaped = htmlEscape(destination);
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="0; url=${escaped}">
<link rel="canonical" href="${escaped}">
<title>Agentic Awesome Skills has moved</title>
</head>
<body>
<main>
<h1>Agentic Awesome Skills has moved</h1>
<p>Continue to <a href="${escaped}">${escaped}</a>.</p>
</main>
</body>
</html>
`;
}
function legacySitemap(redirects) {
const entries = redirects.map(({ from }) => ` <url><loc>${xmlEscape(from)}</loc></url>`).join('\n');
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${entries}
</urlset>
`;
}
function isInside(parent, candidate) {
const relative = path.relative(parent, candidate);
return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));
}
function physicalCandidate(candidate) {
const suffix = [];
let cursor = path.resolve(candidate);
while (!fs.existsSync(cursor)) {
const parent = path.dirname(cursor);
if (parent === cursor) throw new Error(`cannot resolve an existing ancestor for: ${candidate}`);
suffix.unshift(path.basename(cursor));
cursor = parent;
}
return path.join(fs.realpathSync(cursor), ...suffix);
}
function containsExistingSymlink(parent, candidate) {
const relative = path.relative(parent, candidate);
if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative)) return false;
let cursor = parent;
for (const segment of relative.split(path.sep)) {
cursor = path.join(cursor, segment);
if (!fs.existsSync(cursor)) return false;
if (fs.lstatSync(cursor).isSymbolicLink()) return true;
}
return false;
}
function assertSafeOutput(outputDirectory, repoRoot) {
if (fs.existsSync(outputDirectory)) throw new Error(`output path already exists: ${outputDirectory}`);
const physicalRepoRoot = fs.realpathSync(repoRoot);
const codexDirectory = path.join(repoRoot, '.codex');
const codexIsSymlink = fs.existsSync(codexDirectory) && fs.lstatSync(codexDirectory).isSymbolicLink();
const physicalCodexDirectory = physicalCandidate(codexDirectory);
const physicalOutput = physicalCandidate(outputDirectory);
if (isInside(repoRoot, outputDirectory) && !isInside(codexDirectory, outputDirectory)) {
throw new Error('output inside the repository is allowed only under ignored .codex/');
}
if (isInside(repoRoot, outputDirectory) && containsExistingSymlink(repoRoot, path.dirname(outputDirectory))) {
throw new Error('in-repository output paths may not traverse symlinks');
}
if (isInside(physicalRepoRoot, physicalOutput) && (codexIsSymlink || !isInside(physicalCodexDirectory, physicalOutput))) {
throw new Error('physical output resolves inside the repository but outside ignored .codex/');
}
}
function writeBridge(stagingDirectory, redirects, manifest, legacyBase) {
for (const redirect of redirects) {
const filePath = path.join(stagingDirectory, ...redirect.output_file.split('/'));
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, redirectHtml(redirect.to), 'utf8');
}
const legacyDirectory = path.join(stagingDirectory, ...legacyBase.pathname.split('/').filter(Boolean));
fs.mkdirSync(legacyDirectory, { recursive: true });
fs.writeFileSync(path.join(legacyDirectory, 'sitemap.xml'), legacySitemap(redirects), 'utf8');
fs.writeFileSync(path.join(stagingDirectory, 'redirect-manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
fs.writeFileSync(path.join(stagingDirectory, '.nojekyll'), '', 'utf8');
}
function generateBridge(options) {
const repoRoot = path.resolve(options.repoRoot || REPO_ROOT);
const sitemapPath = path.resolve(options.sitemapPath || DEFAULT_SITEMAP);
if (!options.outputDirectory) throw new Error('--output is required');
const outputDirectory = path.resolve(options.outputDirectory);
const currentBase = canonicalBase(options.currentBase || DEFAULT_CURRENT_BASE, 'current base');
const legacyBase = canonicalBase(options.legacyBase || DEFAULT_LEGACY_BASE, 'legacy base');
if (currentBase.toString() === legacyBase.toString()) throw new Error('current and legacy bases must be distinct');
const expectedRoutes = Number(options.expectedRoutes ?? DEFAULT_EXPECTED_ROUTES);
if (!Number.isSafeInteger(expectedRoutes) || expectedRoutes <= 0) throw new Error('expected route count must be a positive integer');
assertSafeOutput(outputDirectory, repoRoot);
const sitemapSource = fs.readFileSync(sitemapPath, 'utf8');
const locations = parseSitemap(sitemapSource);
if (locations.length !== expectedRoutes) {
throw new Error(`sitemap route count ${locations.length} does not match locked expectation ${expectedRoutes}`);
}
const redirects = locations.map((location) => {
const currentUrl = new URL(location);
const relativeRoute = safeRelativeRoute(currentUrl, currentBase);
const from = new URL(relativeRoute ? `${relativeRoute}/` : '', legacyBase).toString();
const to = currentUrl.toString();
return {
from,
to,
output_file: outputRelativePath(legacyBase, relativeRoute),
};
});
if (!redirects.some(({ to }) => to === currentBase.toString())) throw new Error('sitemap does not contain the current root route');
if (new Set(redirects.map(({ from }) => from)).size !== redirects.length) throw new Error('legacy mapping is not one-to-one');
if (new Set(redirects.map(({ output_file }) => output_file)).size !== redirects.length) throw new Error('multiple routes map to the same output file');
redirects.sort((left, right) => left.from.localeCompare(right.from));
const legacySitemapPath = path.posix.join(...legacyBase.pathname.split('/').filter(Boolean), 'sitemap.xml');
if (redirects.some(({ output_file }) => output_file.startsWith(`${legacySitemapPath}/`))) {
throw new Error(`generated route collides with reserved legacy sitemap path: ${legacySitemapPath}`);
}
const manifest = {
schema_version: 1,
deployment_scope: 'separate GitHub Pages user-site subdirectory',
not_for_current_project_pages: true,
source_sitemap_sha256: sha256(sitemapSource),
current_base: currentBase.toString(),
legacy_base: legacyBase.toString(),
route_count: redirects.length,
legacy_sitemap: legacySitemapPath,
redirects,
};
fs.mkdirSync(path.dirname(outputDirectory), { recursive: true });
let stagingDirectory = null;
try {
stagingDirectory = fs.mkdtempSync(path.join(path.dirname(outputDirectory), `.${path.basename(outputDirectory)}.${process.pid}.`));
writeBridge(stagingDirectory, redirects, manifest, legacyBase);
fs.renameSync(stagingDirectory, outputDirectory);
} finally {
if (stagingDirectory && fs.existsSync(stagingDirectory)) fs.rmSync(stagingDirectory, { recursive: true, force: true });
}
return manifest;
}
function parseArgs(argv) {
const options = {};
const aliases = {
'--output': 'outputDirectory',
'--sitemap': 'sitemapPath',
'--current-base': 'currentBase',
'--legacy-base': 'legacyBase',
'--expected-routes': 'expectedRoutes',
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === '--help' || arg === '-h') {
options.help = true;
continue;
}
const key = aliases[arg];
const value = argv[index + 1];
if (!key || !value || value.startsWith('--')) throw new Error(`unknown option or missing value: ${arg}`);
options[key] = value;
index += 1;
}
return options;
}
function main() {
const options = parseArgs(process.argv.slice(2));
if (options.help) {
process.stdout.write('Usage: node tools/scripts/generate-pages-redirect-bridge.js --output NEW_DIR [--sitemap FILE] [--expected-routes N]\n');
return;
}
const manifest = generateBridge(options);
process.stdout.write(`Generated ${manifest.route_count} redirect pages in ${path.resolve(options.outputDirectory)}\n`);
}
if (require.main === module) {
try {
main();
} catch (error) {
process.stderr.write(`redirect bridge generation failed: ${error.message}\n`);
process.exitCode = 1;
}
}
module.exports = {
generateBridge,
htmlEscape,
parseArgs,
parseSitemap,
redirectHtml,
};
@@ -0,0 +1,541 @@
#!/usr/bin/env node
/**
* Normalize the read-only captures in .codex/traffic-snapshots.
*
* The source dashboards have different grains. GitHub supplies rolling daily
* rows, while Search Console can be a snapshot total and Bing can supply both
* totals and daily rows. This script deliberately keeps dashboard properties
* separate: a legacy Pages property and a current Pages property are not a
* time series that may safely be summed together.
*/
const fs = require("fs");
const path = require("path");
const DEFAULT_INPUT = path.resolve(__dirname, "..", "..", ".codex", "traffic-snapshots");
const DEFAULT_OUTPUT = path.join(DEFAULT_INPUT, "daily-normalized.json");
const SNAPSHOT_DIRECTORY = /^\d{4}-\d{2}-\d{2}$/;
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
const MONTH_NAMES = ["january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"];
const CURRENT_REPOSITORY = "sickn33/agentic-awesome-skills";
const REPOSITORY_ALIASES = new Map([
["sickn33/antigravity-awesome-skills", CURRENT_REPOSITORY],
[CURRENT_REPOSITORY, CURRENT_REPOSITORY],
]);
const CURRENT_PAGES_PROPERTY = "https://sickn33.github.io/agentic-awesome-skills/";
const LEGACY_PAGES_PROPERTY = "https://sickn33.github.io/antigravity-awesome-skills/";
const DASHBOARDS = [
{
file: "google-search-console.json",
source: "google_search_console",
dashboardHosts: new Set(["search.google.com"]),
dashboardPathPrefix: "/search-console/",
primaryPropertyParam: "resource_id",
requiredDailyMetrics: ["clicks", "impressions"],
requiredTotalMetrics: ["clicks", "impressions"],
dailyMetrics: (value) => ({
clicks: nonNegativeIntegerOrNull(value.clicks),
impressions: nonNegativeIntegerOrNull(value.impressions),
}),
totals: (value) => ({
clicks: nonNegativeIntegerOrNull(value?.totals?.clicks ?? value.clicks),
impressions: nonNegativeIntegerOrNull(value?.totals?.impressions ?? value?.totals?.impressions_visible ?? value.impressions),
ctr: value?.totals?.ctr ?? value.ctr ?? null,
average_position: nonNegativeNumberOrNull(value?.totals?.average_position ?? value?.totals?.position ?? value.average_position),
}),
},
{
file: "bing-webmaster-search-performance.json",
source: "bing_search",
dashboardHosts: new Set(["bing.com", "www.bing.com"]),
dashboardPathPrefix: "/webmasters/",
primaryPropertyParam: "siteUrl",
requiredDailyMetrics: ["clicks", "impressions"],
requiredTotalMetrics: ["clicks", "impressions"],
dailyMetrics: (value) => ({
clicks: nonNegativeIntegerOrNull(value.clicks),
impressions: nonNegativeIntegerOrNull(value.impressions),
}),
totals: (value) => ({
clicks: nonNegativeIntegerOrNull(value?.totals?.clicks ?? value.clicks),
impressions: nonNegativeIntegerOrNull(value?.totals?.impressions ?? value?.totals?.impressions_label ?? value.impressions),
ctr: value?.totals?.ctr ?? value.ctr ?? null,
}),
},
{
file: "bing-webmaster-ai-performance.json",
source: "bing_ai",
dashboardHosts: new Set(["bing.com", "www.bing.com"]),
dashboardPathPrefix: "/webmasters/",
primaryPropertyParam: "siteUrl",
requiredDailyMetrics: ["citations"],
requiredTotalMetrics: ["citations"],
dailyMetrics: (value) => ({
citations: nonNegativeIntegerOrNull(value.citations ?? value.total_citations),
avg_cited_pages: nonNegativeNumberOrNull(value.avg_cited_pages),
}),
totals: (value) => ({
citations: nonNegativeIntegerOrNull(value.total_citations ?? value?.totals?.citations),
avg_cited_pages: nonNegativeNumberOrNull(value.avg_cited_pages ?? value?.totals?.avg_cited_pages),
}),
},
];
function numberOrNull(value) {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value !== "string") return null;
const raw = value.trim().replace(/\s/g, "");
if (!raw) return null;
const hasK = /k$/i.test(raw);
let numeric = raw.replace(/k$/i, "");
// Dashboard captures use both 5.1K and Italian-style 39,4K labels.
if (hasK) {
numeric = numeric.replace(",", ".");
} else if (/^\d{1,3}(?:[.,]\d{3})+$/.test(numeric)) {
numeric = numeric.replace(/[.,]/g, "");
} else {
numeric = numeric.replace(",", ".");
}
const parsed = Number(numeric);
return Number.isFinite(parsed) ? (hasK ? parsed * 1000 : parsed) : null;
}
function nonNegativeNumberOrNull(value) {
const parsed = numberOrNull(value);
return parsed !== null && parsed >= 0 ? parsed : null;
}
function nonNegativeIntegerOrNull(value) {
const parsed = nonNegativeNumberOrNull(value);
return parsed !== null && Number.isInteger(parsed) ? parsed : null;
}
function isCompleteMetrics(metrics) {
return Object.values(metrics).some((value) => value !== null);
}
function normalizedDate(value) {
if (typeof value !== "string") return null;
const trimmed = value.trim();
if (ISO_DATE.test(trimmed)) return isValidIsoDate(trimmed) ? trimmed : null;
if (/^\d{4}-\d{2}-\d{2}T/.test(trimmed)) {
const date = trimmed.slice(0, 10);
return isValidIsoDate(date) ? date : null;
}
const match = trimmed.match(
/^(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2}),?\s+(\d{4})$/i,
);
if (!match) return null;
const month = MONTH_NAMES.indexOf(match[1].toLowerCase()) + 1;
const date = `${match[3]}-${String(month).padStart(2, "0")}-${String(match[2]).padStart(2, "0")}`;
return isValidIsoDate(date) ? date : null;
}
function isValidIsoDate(value) {
if (!ISO_DATE.test(value)) return false;
const [year, month, day] = value.split("-").map(Number);
const parsed = new Date(Date.UTC(year, month - 1, day));
return parsed.getUTCFullYear() === year
&& parsed.getUTCMonth() === month - 1
&& parsed.getUTCDate() === day;
}
function canonicalProperty(candidate) {
if (typeof candidate !== "string" || !candidate.trim()) return null;
try {
const url = new URL(candidate.trim());
if (url.protocol !== "https:" && url.protocol !== "http:") return null;
url.hash = "";
url.search = "";
const pathname = url.pathname.endsWith("/") ? url.pathname : `${url.pathname}/`;
return `${url.protocol}//${url.host}${pathname}`;
} catch {
return null;
}
}
function propertyFromDashboardUrl(dashboardUrl, config) {
if (typeof dashboardUrl !== "string") return { valid: false, signals: [], reason: "missing dashboard_url" };
try {
const url = new URL(dashboardUrl);
const hostValid = url.protocol === "https:" && config.dashboardHosts.has(url.hostname.toLowerCase());
const pathValid = url.pathname.startsWith(config.dashboardPathPrefix);
const primary = canonicalProperty(url.searchParams.get(config.primaryPropertyParam));
if (!hostValid || !pathValid || !primary) {
return { valid: false, signals: [], reason: "dashboard host, path, or primary property parameter is invalid" };
}
const signals = [];
for (const param of ["resource_id", "siteUrl"]) {
const value = canonicalProperty(url.searchParams.get(param));
if (value) signals.push({ source: `dashboard_url:${param}`, value });
}
return { valid: true, signals, reason: null };
} catch {
return { valid: false, signals: [], reason: "dashboard_url is malformed" };
}
}
function sourceProperty(snapshot, config) {
const observed = [];
for (const [source, raw] of [
["source_property", snapshot.source_property],
["property_url", snapshot.property_url],
["propertyUrl", snapshot.propertyUrl],
["site_url", snapshot.site_url],
["siteUrl", snapshot.siteUrl],
]) {
const value = canonicalProperty(raw);
if (value) observed.push({ source, value });
}
const dashboard = propertyFromDashboardUrl(snapshot.dashboard_url, config);
if (snapshot.dashboard_url && !dashboard.valid) {
return { property: null, provenance: "invalid", conflict: true, reason: dashboard.reason };
}
observed.push(...dashboard.signals);
const distinct = [...new Set(observed.map((signal) => signal.value))];
if (distinct.length > 1) {
return { property: null, provenance: "conflict", conflict: true, reason: "observed property signals conflict" };
}
const intended = canonicalProperty(snapshot.intended_property);
if (distinct.length === 1) {
return {
property: distinct[0],
provenance: "observed",
conflict: false,
reason: intended && intended !== distinct[0] ? "intended property disagrees with observed property" : null,
};
}
if (intended) {
return { property: intended, provenance: "intended_only", conflict: false, reason: "property identity comes from intended_property only" };
}
return { property: null, provenance: "unknown", conflict: false, reason: "no property identity signal" };
}
function propertyIdentity(property) {
const value = canonicalProperty(property);
if (value === LEGACY_PAGES_PROPERTY) return "legacy";
if (value === CURRENT_PAGES_PROPERTY) return "current";
return "unknown";
}
function coverageEnd(snapshot) {
const dailyValues = Array.isArray(snapshot.daily_values) ? snapshot.daily_values : [];
const dailyDates = dailyValues.map((row) => normalizedDate(row?.date)).filter(Boolean).sort();
if (dailyDates.length) return dailyDates.at(-1);
const text = [
snapshot.date_range_visible,
snapshot.date_range,
snapshot.chart_visible?.description,
].filter((value) => typeof value === "string").join(" ");
const isoDates = (text.match(/\d{4}-\d{2}-\d{2}/g) || []).map(normalizedDate).filter(Boolean);
if (isoDates?.length) return isoDates.sort().at(-1);
const monthDates = [...text.matchAll(
/\b(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2}),\s+(\d{4})\b/gi,
)].map((match) => {
const month = MONTH_NAMES.indexOf(match[1].toLowerCase()) + 1;
return normalizedDate(`${match[3]}-${String(month).padStart(2, "0")}-${String(match[2]).padStart(2, "0")}`);
}).filter(Boolean);
return monthDates.length ? monthDates.sort().at(-1) : null;
}
function readJson(filePath, warnings, label) {
try {
return JSON.parse(fs.readFileSync(filePath, "utf8"));
} catch (error) {
warnings.push(`${label}: skipped malformed JSON (${error.message.split("\n")[0]})`);
return null;
}
}
function setLatest(map, key, observation) {
const previous = map.get(key);
if (!previous || observation.observed_from_snapshot > previous.observed_from_snapshot) {
map.set(key, observation);
}
}
function setBestDashboardEvidence(map, key, observation) {
const previous = map.get(key);
const strength = { observed: 2, intended_only: 1, unknown: 0 };
const candidateStrength = strength[observation.property_provenance] ?? 0;
const previousStrength = strength[previous?.property_provenance] ?? -1;
if (!previous
|| candidateStrength > previousStrength
|| (candidateStrength === previousStrength && observation.observed_from_snapshot > previous.observed_from_snapshot)) {
map.set(key, observation);
}
}
function snapshotDirectories(inputDirectory) {
if (!fs.existsSync(inputDirectory)) throw new Error(`input directory does not exist: ${inputDirectory}`);
if (!fs.statSync(inputDirectory).isDirectory()) throw new Error(`input path is not a directory: ${inputDirectory}`);
const snapshots = fs.readdirSync(inputDirectory, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && SNAPSHOT_DIRECTORY.test(entry.name) && isValidIsoDate(entry.name))
.map((entry) => entry.name)
.sort();
if (!snapshots.length) throw new Error(`input directory has no valid snapshot directories: ${inputDirectory}`);
return snapshots;
}
function canonicalRepository(value) {
if (typeof value !== "string" || !value.trim()) return null;
const normalized = value.trim().toLowerCase();
return REPOSITORY_ALIASES.get(normalized) || normalized;
}
function normalizeSnapshots(inputDirectory) {
const warnings = [];
const github = { views: new Map(), clones: new Map() };
const dashboardDaily = new Map();
const dashboardTotals = new Map();
const discoveredSnapshots = snapshotDirectories(inputDirectory);
const snapshots = [];
const sourceRepositories = new Set();
let repo = null;
let timezone = null;
for (const snapshotDate of discoveredSnapshots) {
const directory = path.join(inputDirectory, snapshotDate);
const manifestPath = path.join(directory, "manifest.json");
if (!fs.existsSync(manifestPath)) {
warnings.push(`${snapshotDate}/manifest.json: skipped snapshot without identity manifest`);
continue;
}
const manifest = readJson(manifestPath, warnings, `${snapshotDate}/manifest.json`);
const rawRepository = typeof manifest?.repo === "string" ? manifest.repo.trim() : null;
const snapshotRepository = canonicalRepository(rawRepository);
const snapshotTimezone = typeof manifest?.timezone === "string" ? manifest.timezone.trim() : null;
if (!snapshotRepository || !snapshotTimezone) {
warnings.push(`${snapshotDate}/manifest.json: skipped snapshot with missing repository or timezone identity`);
continue;
}
if ((repo && snapshotRepository !== repo) || (timezone && snapshotTimezone !== timezone)) {
warnings.push(`${snapshotDate}/manifest.json: skipped snapshot with repository or timezone mismatch`);
continue;
}
repo ||= snapshotRepository;
timezone ||= snapshotTimezone;
sourceRepositories.add(rawRepository);
snapshots.push(snapshotDate);
for (const [kind, file] of [["views", "views.json"], ["clones", "clones.json"]]) {
const filePath = path.join(directory, file);
if (!fs.existsSync(filePath)) continue;
const payload = readJson(filePath, warnings, `${snapshotDate}/${file}`);
const values = Array.isArray(payload?.[kind]) ? payload[kind] : null;
if (!values) {
warnings.push(`${snapshotDate}/${file}: skipped missing ${kind} array`);
continue;
}
for (const value of values) {
const date = normalizedDate(value?.timestamp);
const count = nonNegativeIntegerOrNull(value?.count);
const uniques = nonNegativeIntegerOrNull(value?.uniques);
if (!date || !ISO_DATE.test(date) || count === null || uniques === null) {
warnings.push(`${snapshotDate}/${file}: skipped malformed daily row`);
continue;
}
setLatest(github[kind], date, { count, uniques, observed_from_snapshot: snapshotDate });
}
}
for (const config of DASHBOARDS) {
const filePath = path.join(directory, config.file);
if (!fs.existsSync(filePath)) continue;
const payload = readJson(filePath, warnings, `${snapshotDate}/${config.file}`);
if (!payload) continue;
if (payload.status !== "success") {
warnings.push(`${snapshotDate}/${config.file}: skipped non-success capture`);
continue;
}
const propertyEvidence = sourceProperty(payload, config);
if (propertyEvidence.conflict) {
warnings.push(`${snapshotDate}/${config.file}: skipped capture because ${propertyEvidence.reason}`);
continue;
}
const property = propertyEvidence.property;
if (propertyEvidence.reason) warnings.push(`${snapshotDate}/${config.file}: ${propertyEvidence.reason}`);
const identity = propertyIdentity(property);
if (identity === "unknown") {
warnings.push(`${snapshotDate}/${config.file}: property identity is unknown and remains isolated`);
}
const end = coverageEnd(payload);
const base = {
source_property: property,
property_provenance: propertyEvidence.provenance,
property_identity: identity,
coverage_end: end,
observed_from_snapshot: snapshotDate,
};
const propertyKey = property || `unknown:${snapshotDate}`;
const dailyValues = Array.isArray(payload.daily_values) ? payload.daily_values : [];
for (const value of dailyValues) {
const date = normalizedDate(value?.date);
if (!date) {
warnings.push(`${snapshotDate}/${config.file}: skipped malformed dashboard daily row`);
continue;
}
const metrics = config.dailyMetrics(value);
if (!isCompleteMetrics(metrics)) {
warnings.push(`${snapshotDate}/${config.file}: skipped dashboard daily row without metrics`);
continue;
}
// Never stitch a partial payload together with an older snapshot. A
// dashboard row is useful only when its source-specific core metrics
// arrived in the same source observation.
if (config.requiredDailyMetrics.some((metric) => metrics[metric] === null)) {
warnings.push(`${snapshotDate}/${config.file}: skipped partial dashboard daily row`);
continue;
}
const key = `${config.source}\u0000${propertyKey}\u0000${date}`;
setBestDashboardEvidence(dashboardDaily, key, { source: config.source, date, ...base, ...metrics });
}
const totals = config.totals(payload);
if (config.requiredTotalMetrics.every((metric) => totals[metric] !== null)) {
const key = `${config.source}\u0000${propertyKey}\u0000${end || "unknown"}`;
setBestDashboardEvidence(dashboardTotals, key, { source: config.source, ...base, ...totals });
} else if (isCompleteMetrics(totals)) {
warnings.push(`${snapshotDate}/${config.file}: skipped partial dashboard totals`);
}
}
}
if (!snapshots.length) {
throw new Error(`no snapshots with a consistent repository and timezone identity were accepted from: ${inputDirectory}`);
}
const rowsByDate = new Map();
const ensureRow = (date) => {
if (!rowsByDate.has(date)) rowsByDate.set(date, { date });
return rowsByDate.get(date);
};
for (const [kind, values] of Object.entries(github)) {
for (const [date, observation] of values) {
const row = ensureRow(date);
row.github ||= {};
row.github[kind] = observation;
}
}
for (const observation of dashboardDaily.values()) {
const row = ensureRow(observation.date);
row[observation.source] ||= [];
const { source, date, ...record } = observation;
row[source].push(record);
}
const rows = [...rowsByDate.values()].sort((left, right) => left.date.localeCompare(right.date));
for (const row of rows) {
for (const source of DASHBOARDS.map((config) => config.source)) {
if (Array.isArray(row[source])) {
row[source].sort((left, right) =>
`${left.property_identity}\u0000${left.source_property || ""}\u0000${left.observed_from_snapshot}`.localeCompare(
`${right.property_identity}\u0000${right.source_property || ""}\u0000${right.observed_from_snapshot}`,
),
);
}
}
}
const snapshot_totals = {};
for (const config of DASHBOARDS) {
snapshot_totals[config.source] = [...dashboardTotals.values()]
.filter((record) => record.source === config.source)
.map(({ source, ...record }) => record)
.sort((left, right) =>
`${left.property_identity}\u0000${left.source_property || ""}\u0000${left.coverage_end || ""}\u0000${left.observed_from_snapshot}`.localeCompare(
`${right.property_identity}\u0000${right.source_property || ""}\u0000${right.coverage_end || ""}\u0000${right.observed_from_snapshot}`,
),
);
}
return {
schema_version: "2.0.0",
repo,
source_repositories: [...sourceRepositories].sort(),
timezone,
grain: "date",
caveats: [
"GitHub rolling-window rows are deduplicated by date and retain the latest snapshot observation.",
"Dashboard data is separated by source_property and property_identity; legacy and current properties are never summed or overwritten.",
"coverage_end describes the latest date visible in a dashboard capture, not a promise of complete data.",
"Malformed, partial, or non-success captures are skipped with warnings rather than merged into another observation.",
],
discovered_snapshots: discoveredSnapshots,
snapshots,
rows,
snapshot_totals,
warnings: warnings.sort(),
};
}
function atomicWrite(outputPath, value) {
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
const temporary = path.join(
path.dirname(outputPath),
`.${path.basename(outputPath)}.${process.pid}.${Date.now()}.tmp`,
);
try {
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, "utf8");
fs.renameSync(temporary, outputPath);
} finally {
if (fs.existsSync(temporary)) fs.unlinkSync(temporary);
}
}
function parseArgs(args) {
const options = { input: DEFAULT_INPUT, output: DEFAULT_OUTPUT };
for (let index = 0; index < args.length; index += 1) {
const option = args[index];
if (option === "--input" || option === "--output") {
const value = args[index + 1];
if (!value || value.startsWith("--")) throw new Error(`${option} requires a value`);
options[option.slice(2)] = path.resolve(value);
index += 1;
} else if (option === "--help" || option === "-h") {
options.help = true;
} else {
throw new Error(`Unknown option: ${option}`);
}
}
return options;
}
function main() {
const options = parseArgs(process.argv.slice(2));
if (options.help) {
console.log("Usage: node tools/scripts/normalize_traffic_snapshots.js [--input DIR] [--output FILE]");
return;
}
const normalized = normalizeSnapshots(options.input);
atomicWrite(options.output, normalized);
console.log(`Normalized ${normalized.snapshots.length} snapshot directories to ${options.output}`);
}
if (require.main === module) {
try {
main();
} catch (error) {
console.error(`traffic normalization failed: ${error.message}`);
process.exitCode = 1;
}
}
module.exports = {
DEFAULT_INPUT,
DEFAULT_OUTPUT,
atomicWrite,
normalizeSnapshots,
parseArgs,
propertyIdentity,
sourceProperty,
};
@@ -0,0 +1,236 @@
const assert = require('assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawnSync } = require('child_process');
const scriptPath = path.resolve(__dirname, '..', 'audit_search_migration_readiness.js');
const { auditMigrationReadiness } = require(scriptPath);
function writeJson(filePath, data) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
}
function fixture({ currentProperties = true, legacyOnly = false, malformed = false } = {}) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'migration-readiness-'));
const current = 'https://example.github.io/agentic-awesome-skills/';
const legacy = 'https://example.github.io/antigravity-awesome-skills/';
fs.mkdirSync(path.join(root, 'apps/web-app/public'), { recursive: true });
fs.writeFileSync(path.join(root, 'apps/web-app/public/sitemap.xml'), `<?xml version="1.0"?><urlset><url><loc>${current}</loc></url><url><loc>${current}plugins/</loc></url></urlset>`);
writeJson(path.join(root, 'package.json'), { name: 'agentic-awesome-skills', version: '14.2.0' });
writeJson(path.join(root, 'legacy-package.json'), { name: 'antigravity-awesome-skills', version: '13.13.0', deprecated: 'Moved to agentic-awesome-skills' });
writeJson(path.join(root, 'redirects.json'), { redirects: [
{ from: legacy, to: current }, { from: `${legacy}plugins/`, to: `${current}plugins/` },
] });
const property = legacyOnly ? legacy : current;
const snapshot = new Date().toISOString().slice(0, 10);
if (currentProperties || legacyOnly) {
for (const filename of ['google-search-console.json', 'bing-webmaster-search-performance.json']) {
const filePath = path.join(root, '.codex/traffic-snapshots', snapshot, filename);
if (malformed) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, '{broken');
} else {
writeJson(filePath, {
status: 'success',
captured_at_utc: `${new Date().toISOString().slice(0, 10)}T12:00:00Z`,
dashboard_url: filename.startsWith('google')
? `https://search.google.com/search-console/performance?resource_id=${encodeURIComponent(property)}`
: `https://www.bing.com/webmasters/searchperf?siteUrl=${encodeURIComponent(property)}`,
totals: { clicks: 1, impressions: 10 },
});
}
}
}
return { root, current, legacy };
}
function options(root) {
return {
repoRoot: root,
redirectManifestPath: 'redirects.json',
legacyPackagePath: 'legacy-package.json',
currentPagesUrl: 'https://example.github.io/agentic-awesome-skills/',
legacyPagesUrl: 'https://example.github.io/antigravity-awesome-skills/',
currentPackageName: 'agentic-awesome-skills',
asOfDate: new Date().toISOString().slice(0, 10),
};
}
{
const { root } = fixture();
const snapshot = new Date().toISOString().slice(0, 10);
for (const filename of ['google-search-console.json', 'bing-webmaster-search-performance.json']) {
writeJson(path.join(root, '.codex/traffic-snapshots', snapshot, filename), {
status: 'success',
captured_at_utc: `${snapshot}T12:00:00Z`,
dashboard_url: `https://attacker.example/?next=${encodeURIComponent('https://example.github.io/agentic-awesome-skills/')}`,
totals: { clicks: 1, impressions: 10 },
});
}
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready', 'an unrelated URL containing the current property cannot pass');
assert(report.failed_checks.includes('google_search_console'));
}
{
const { root, current, legacy } = fixture();
const snapshot = new Date().toISOString().slice(0, 10);
for (const filename of ['google-search-console.json', 'bing-webmaster-search-performance.json']) {
writeJson(path.join(root, '.codex/traffic-snapshots', snapshot, filename), {
status: 'success',
captured_at_utc: `${snapshot}T12:00:00Z`,
source_property: current,
dashboard_url: filename.startsWith('google')
? `https://search.google.com/search-console/performance?resource_id=${encodeURIComponent(legacy)}`
: `https://www.bing.com/webmasters/searchperf?siteUrl=${encodeURIComponent(legacy)}`,
totals: { clicks: 1, impressions: 10 },
});
}
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready', 'conflicting observed property signals cannot pass');
assert.strictEqual(report.checks.google_search_console.rejected_current_evidence[0].property_conflict, true);
}
{
const { root, current } = fixture();
const tomorrow = new Date(Date.now() + 86400000).toISOString().slice(0, 10);
const today = new Date().toISOString().slice(0, 10);
for (const filename of ['google-search-console.json', 'bing-webmaster-search-performance.json']) {
writeJson(path.join(root, '.codex/traffic-snapshots', tomorrow, filename), {
status: 'success',
captured_at_utc: `${tomorrow}T12:00:00Z`,
dashboard_url: filename.startsWith('google')
? `https://search.google.com/search-console/performance?resource_id=${encodeURIComponent(current)}`
: `https://www.bing.com/webmasters/searchperf?siteUrl=${encodeURIComponent(current)}`,
totals: { clicks: 1, impressions: 10 },
});
}
fs.rmSync(path.join(root, '.codex/traffic-snapshots', today), { recursive: true, force: true });
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready', 'evidence after the as-of date cannot pass');
assert.strictEqual(report.checks.google_search_console.rejected_current_evidence[0].freshness.reason, 'capture is in the future');
}
{
const { root, current } = fixture();
const snapshot = new Date().toISOString().slice(0, 10);
for (const filename of ['google-search-console.json', 'bing-webmaster-search-performance.json']) {
writeJson(path.join(root, '.codex/traffic-snapshots', snapshot, filename), {
status: 'success',
captured_at_utc: `${snapshot}T12:00:00Z`,
dashboard_url: `https://attacker.example/${filename}?${filename.startsWith('google') ? 'resource_id' : 'siteUrl'}=${encodeURIComponent(current)}`,
totals: { clicks: 1, impressions: 10 },
});
}
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready', 'recognized property params on an attacker host cannot pass');
assert.strictEqual(report.checks.google_search_console.rejected_current_evidence.length, 0);
}
{
const { root, current, legacy } = fixture();
const snapshot = new Date().toISOString().slice(0, 10);
for (const filename of ['google-search-console.json', 'bing-webmaster-search-performance.json']) {
const base = filename.startsWith('google')
? 'https://search.google.com/search-console/performance'
: 'https://www.bing.com/webmasters/searchperf';
writeJson(path.join(root, '.codex/traffic-snapshots', snapshot, filename), {
status: 'success',
captured_at_utc: `${snapshot}T12:00:00Z`,
dashboard_url: `${base}?resource_id=${encodeURIComponent(current)}&siteUrl=${encodeURIComponent(legacy)}`,
totals: { clicks: 1, impressions: 10 },
});
}
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready', 'conflicting recognized dashboard property params cannot pass');
}
{
const { root } = fixture();
const snapshot = new Date().toISOString().slice(0, 10);
for (const filename of ['google-search-console.json', 'bing-webmaster-search-performance.json']) {
const filePath = path.join(root, '.codex/traffic-snapshots', snapshot, filename);
const payload = JSON.parse(fs.readFileSync(filePath, 'utf8'));
payload.totals = { clicks: 0.5, impressions: 1.25 };
writeJson(filePath, payload);
}
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready', 'fractional count metrics cannot satisfy evidence completeness');
}
{
const { root } = fixture();
const redirects = JSON.parse(fs.readFileSync(path.join(root, 'redirects.json'), 'utf8'));
redirects.redirects.push(redirects.redirects[0]);
writeJson(path.join(root, 'redirects.json'), redirects);
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready', 'duplicate redirects violate exact-once coverage');
assert.strictEqual(report.checks.redirect_manifest_coverage.duplicates.length, 1);
}
{
const { root } = fixture();
fs.writeFileSync(path.join(root, 'apps/web-app/public/sitemap.xml'), '<?xml version="1.0"?><urlset><url><loc>https://evil.example/not-the-project/</loc></url></urlset>');
writeJson(path.join(root, 'package.json'), { name: 'unrelated-package', version: '1.0.0' });
const report = auditMigrationReadiness({ ...options(root), currentPagesUrl: undefined, legacyPagesUrl: undefined, currentPackageName: undefined });
assert.strictEqual(report.status, 'not_ready', 'defaults stay anchored to the real AAS identities');
assert(report.failed_checks.includes('current_sitemap_identity'));
assert(report.failed_checks.includes('npm_identities'));
}
{
const { root } = fixture();
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'ready');
assert.deepStrictEqual(report.failed_checks, []);
assert.strictEqual(report.checks.redirect_manifest_coverage.covered, 2);
}
{
const { root } = fixture({ currentProperties: false });
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready');
assert(report.failed_checks.includes('google_search_console'));
assert(report.failed_checks.includes('bing_webmaster'));
}
{
const { root } = fixture({ currentProperties: false, legacyOnly: true });
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready');
assert.strictEqual(report.checks.google_search_console.legacy_evidence.length, 1);
assert.strictEqual(report.checks.google_search_console.current_evidence.length, 0);
}
{
const { root } = fixture({ malformed: true });
const report = auditMigrationReadiness(options(root));
assert.strictEqual(report.status, 'not_ready');
assert(report.errors.some((error) => error.includes('google-search-console.json')));
}
{
const { root } = fixture();
const output = path.join(root, 'out.json');
const command = [
scriptPath,
'--repo-root', root,
'--redirect-manifest', 'redirects.json',
'--legacy-package', 'legacy-package.json',
'--current-pages-url', 'https://example.github.io/agentic-awesome-skills/',
'--legacy-pages-url', 'https://example.github.io/antigravity-awesome-skills/',
'--current-package-name', 'agentic-awesome-skills',
'--as-of', new Date().toISOString().slice(0, 10),
'--output', output,
];
const first = spawnSync(process.execPath, command, { encoding: 'utf8' });
const firstFile = fs.readFileSync(output, 'utf8');
const second = spawnSync(process.execPath, command, { encoding: 'utf8' });
assert.strictEqual(first.status, 0, first.stderr);
assert.strictEqual(second.status, 0, second.stderr);
assert.strictEqual(fs.readFileSync(output, 'utf8'), firstFile);
}
console.log('audit_search_migration_readiness tests passed');
@@ -0,0 +1,191 @@
const assert = require('assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawnSync } = require('child_process');
const scriptPath = path.resolve(__dirname, '..', 'generate-pages-redirect-bridge.js');
const { generateBridge } = require(scriptPath);
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pages-redirect-bridge-'));
const sitemapPath = path.join(fixtureRoot, 'sitemap.xml');
const current = 'https://example.github.io/agentic-awesome-skills/';
const legacy = 'https://example.github.io/antigravity-awesome-skills/';
function sitemap(locations) {
return `<?xml version="1.0"?><urlset>${locations.map((location) => `<url><loc>${location}</loc></url>`).join('')}</urlset>`;
}
function readTree(root) {
const result = {};
function visit(directory) {
for (const entry of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
const filePath = path.join(directory, entry.name);
if (entry.isDirectory()) visit(filePath);
else result[path.relative(root, filePath)] = fs.readFileSync(filePath, 'utf8');
}
}
visit(root);
return result;
}
try {
const locations = [current, `${current}plugins/`, `${current}topics/github-ai-skills-repository/`, `${current}skill/brainstorming/`];
fs.writeFileSync(sitemapPath, sitemap(locations), 'utf8');
const outputOne = path.join(fixtureRoot, '.codex', 'bridge-one');
const manifest = generateBridge({
repoRoot: fixtureRoot,
sitemapPath,
outputDirectory: outputOne,
currentBase: current,
legacyBase: legacy,
expectedRoutes: 4,
});
assert.strictEqual(manifest.route_count, 4);
assert.strictEqual(manifest.redirects.length, 4);
assert.strictEqual(new Set(manifest.redirects.map((redirect) => redirect.from)).size, 4);
assert.strictEqual(new Set(manifest.redirects.map((redirect) => redirect.output_file)).size, 4);
for (const relative of [
'antigravity-awesome-skills/index.html',
'antigravity-awesome-skills/plugins/index.html',
'antigravity-awesome-skills/topics/github-ai-skills-repository/index.html',
'antigravity-awesome-skills/skill/brainstorming/index.html',
]) {
assert(fs.existsSync(path.join(outputOne, relative)), `missing generated route: ${relative}`);
}
const pluginHtml = fs.readFileSync(path.join(outputOne, 'antigravity-awesome-skills/plugins/index.html'), 'utf8');
assert.match(pluginHtml, /http-equiv="refresh" content="0; url=https:\/\/example\.github\.io\/agentic-awesome-skills\/plugins\/"/);
assert.match(pluginHtml, /rel="canonical" href="https:\/\/example\.github\.io\/agentic-awesome-skills\/plugins\/"/);
assert.match(pluginHtml, /<a href="https:\/\/example\.github\.io\/agentic-awesome-skills\/plugins\/">/);
assert.strictEqual((fs.readFileSync(path.join(outputOne, 'antigravity-awesome-skills/sitemap.xml'), 'utf8').match(/<loc>/g) || []).length, 4);
const outputTwo = path.join(fixtureRoot, '.codex', 'bridge-two');
generateBridge({
repoRoot: fixtureRoot,
sitemapPath,
outputDirectory: outputTwo,
currentBase: current,
legacyBase: legacy,
expectedRoutes: 4,
});
assert.deepStrictEqual(readTree(outputTwo), readTree(outputOne), 'identical input produces byte-identical output');
assert.throws(() => generateBridge({
repoRoot: fixtureRoot,
sitemapPath,
outputDirectory: outputOne,
currentBase: current,
legacyBase: legacy,
expectedRoutes: 4,
}), /output path already exists/);
const foreignSitemap = path.join(fixtureRoot, 'foreign.xml');
fs.writeFileSync(foreignSitemap, sitemap([current, 'https://attacker.example/skill/escape/']), 'utf8');
assert.throws(() => generateBridge({
repoRoot: fixtureRoot,
sitemapPath: foreignSitemap,
outputDirectory: path.join(fixtureRoot, '.codex', 'foreign'),
currentBase: current,
legacyBase: legacy,
expectedRoutes: 2,
}), /outside the current HTTPS identity/);
const duplicateSitemap = path.join(fixtureRoot, 'duplicate.xml');
fs.writeFileSync(duplicateSitemap, sitemap([current, current]), 'utf8');
assert.throws(() => generateBridge({
repoRoot: fixtureRoot,
sitemapPath: duplicateSitemap,
outputDirectory: path.join(fixtureRoot, '.codex', 'duplicate'),
currentBase: current,
legacyBase: legacy,
expectedRoutes: 2,
}), /duplicate/);
const doubleEncodedSitemap = path.join(fixtureRoot, 'double-encoded.xml');
fs.writeFileSync(doubleEncodedSitemap, sitemap([current, `${current}skill/&amp;lt;escape/`]), 'utf8');
assert.throws(() => generateBridge({
repoRoot: fixtureRoot,
sitemapPath: doubleEncodedSitemap,
outputDirectory: path.join(fixtureRoot, '.codex', 'double-encoded'),
currentBase: current,
legacyBase: legacy,
expectedRoutes: 2,
}), /unsafe path segment/, 'XML entities must be decoded exactly once');
const trackedOutput = path.join(fixtureRoot, 'apps', 'web-app', 'public', 'bridge');
assert.throws(() => generateBridge({
repoRoot: fixtureRoot,
sitemapPath,
outputDirectory: trackedOutput,
currentBase: current,
legacyBase: legacy,
expectedRoutes: 4,
}), /only under ignored \.codex/);
const symlinkRepo = path.join(fixtureRoot, 'symlink-repo');
const trackedPublic = path.join(symlinkRepo, 'apps', 'web-app', 'public');
fs.mkdirSync(trackedPublic, { recursive: true });
const symlinkSitemap = path.join(symlinkRepo, 'sitemap.xml');
fs.writeFileSync(symlinkSitemap, sitemap(locations), 'utf8');
fs.symlinkSync(trackedPublic, path.join(symlinkRepo, '.codex'));
assert.throws(() => generateBridge({
repoRoot: symlinkRepo,
sitemapPath: symlinkSitemap,
outputDirectory: path.join(symlinkRepo, '.codex', 'bridge'),
currentBase: current,
legacyBase: legacy,
expectedRoutes: 4,
}), /symlink|physical output/);
assert(!fs.existsSync(path.join(trackedPublic, 'bridge')), 'a .codex symlink cannot redirect writes into tracked public files');
const outsideRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pages-redirect-outside-'));
try {
fs.symlinkSync(trackedPublic, path.join(outsideRoot, 'linked-public'));
assert.throws(() => generateBridge({
repoRoot: symlinkRepo,
sitemapPath: symlinkSitemap,
outputDirectory: path.join(outsideRoot, 'linked-public', 'bridge'),
currentBase: current,
legacyBase: legacy,
expectedRoutes: 4,
}), /physical output resolves inside the repository/);
assert(!fs.existsSync(path.join(trackedPublic, 'bridge')));
} finally {
fs.rmSync(outsideRoot, { recursive: true, force: true });
}
const sentinelDirectory = path.join(path.dirname(outputOne), `.${path.basename(outputOne)}.${process.pid}.sentinel`);
fs.mkdirSync(sentinelDirectory);
fs.writeFileSync(path.join(sentinelDirectory, 'KEEP'), 'owned by another process', 'utf8');
const outputThree = path.join(fixtureRoot, '.codex', 'bridge-three');
generateBridge({
repoRoot: fixtureRoot,
sitemapPath,
outputDirectory: outputThree,
currentBase: current,
legacyBase: legacy,
expectedRoutes: 4,
});
assert.strictEqual(fs.readFileSync(path.join(sentinelDirectory, 'KEEP'), 'utf8'), 'owned by another process');
const cliOutput = path.join(fixtureRoot, '.codex', 'cli');
const cli = spawnSync(process.execPath, [
scriptPath,
'--sitemap', sitemapPath,
'--output', cliOutput,
'--current-base', current,
'--legacy-base', legacy,
'--expected-routes', '4',
], { encoding: 'utf8' });
assert.strictEqual(cli.status, 0, cli.stderr);
assert.match(cli.stdout, /Generated 4 redirect pages/);
const missingOutput = spawnSync(process.execPath, [scriptPath, '--sitemap', sitemapPath], { encoding: 'utf8' });
assert.strictEqual(missingOutput.status, 1);
assert.match(missingOutput.stderr, /--output is required/);
console.log('generate_pages_redirect_bridge tests passed');
} finally {
fs.rmSync(fixtureRoot, { recursive: true, force: true });
}
@@ -0,0 +1,199 @@
const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { spawnSync } = require("child_process");
const scriptPath = path.resolve(__dirname, "..", "normalize_traffic_snapshots.js");
const { normalizeSnapshots } = require(scriptPath);
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), "traffic-normalizer-"));
function writeJson(relativePath, value) {
const filePath = path.join(fixtureRoot, relativePath);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(value), "utf8");
}
function writeManifest(date, repo = "owner/repo", timezone = "Europe/Rome") {
writeJson(`${date}/manifest.json`, { repo, timezone });
}
function dashboard(property, dailyValues, extra = {}) {
return {
status: "success",
dashboard_url: `https://www.bing.com/webmasters/searchperf?siteUrl=${encodeURIComponent(property)}`,
date_range_visible: "3 M (April 12, 2026 to July 10, 2026)",
daily_values: dailyValues,
totals: { clicks: 10, impressions: 100 },
...extra,
};
}
try {
writeManifest("2026-07-01");
writeJson("2026-07-01/views.json", {
views: [{ timestamp: "2026-06-30T00:00:00Z", count: 1, uniques: 1 }],
});
writeJson("2026-07-01/clones.json", {
clones: [{ timestamp: "2026-06-30T00:00:00Z", count: 2, uniques: 1 }],
});
writeJson("2026-07-01/bing-webmaster-search-performance.json", dashboard(
"https://sickn33.github.io/antigravity-awesome-skills/",
[{ date: "2026-06-30", clicks: 3, impressions: 30 }],
));
writeJson("2026-07-01/google-search-console.json", {
status: "success",
intended_property: "https://sickn33.github.io/antigravity-awesome-skills/",
date_range_visible: "June 1, 2026 to July 9, 2026",
totals: { clicks: 4, impressions: "1.5K" },
});
writeManifest("2026-07-02");
writeJson("2026-07-02/views.json", {
views: [{ timestamp: "2026-06-30T00:00:00Z", count: 9, uniques: 8 }],
});
writeJson("2026-07-02/clones.json", {
clones: [{ timestamp: "2026-06-30T00:00:00Z", count: 7, uniques: 6 }],
});
writeJson("2026-07-02/bing-webmaster-search-performance.json", dashboard(
"https://sickn33.github.io/agentic-awesome-skills/",
[{ date: "2026-06-30", clicks: 11, impressions: 110 }],
));
writeJson("2026-07-02/google-search-console.json", {
status: "success",
dashboard_url: "https://search.google.com/search-console/performance?resource_id=https%3A%2F%2Fsickn33.github.io%2Fagentic-awesome-skills%2F",
date_range_visible: "June 1, 2026 to July 10, 2026",
totals: { clicks: 5, impressions_visible: "2,5K" },
});
writeJson("2026-07-02/bing-webmaster-ai-performance.json", {
status: "success",
dashboard_url: "https://www.bing.com/webmasters/aiperformance?siteUrl=https%3A%2F%2Fsickn33.github.io%2Fagentic-awesome-skills%2F",
daily_values: [{ date: "June 30, 2026", total_citations: 7, avg_cited_pages: 1 }],
total_citations: 7,
});
// A malformed file and partial rows must not leak values into valid rows.
writeManifest("2026-07-03");
fs.mkdirSync(path.join(fixtureRoot, "2026-07-03"), { recursive: true });
fs.writeFileSync(path.join(fixtureRoot, "2026-07-03", "views.json"), "{not json", "utf8");
writeJson("2026-07-03/clones.json", { clones: [{ timestamp: "2026-06-30T00:00:00Z", count: 99 }] });
writeJson("2026-07-03/bing-webmaster-search-performance.json", dashboard(
"https://sickn33.github.io/agentic-awesome-skills/",
[{ date: "2026-06-30", impressions: 999 }],
));
const normalized = normalizeSnapshots(fixtureRoot);
assert.strictEqual(normalized.schema_version, "2.0.0");
assert.strictEqual(normalized.repo, "owner/repo");
assert.deepStrictEqual(normalized.source_repositories, ["owner/repo"]);
assert.deepStrictEqual(normalized.snapshots, ["2026-07-01", "2026-07-02", "2026-07-03"]);
const row = normalized.rows.find((value) => value.date === "2026-06-30");
assert.strictEqual(row.github.views.count, 9, "latest GitHub view observation wins per date");
assert.strictEqual(row.github.views.observed_from_snapshot, "2026-07-02");
assert.strictEqual(row.github.clones.count, 7, "partial newer clone row cannot overwrite complete observation");
assert.strictEqual(row.bing_search.length, 2, "different dashboard properties remain separate");
assert.deepStrictEqual(row.bing_search.map((value) => value.property_identity), ["current", "legacy"]);
assert.deepStrictEqual(row.bing_search.map((value) => value.coverage_end), ["2026-06-30", "2026-06-30"]);
assert.deepStrictEqual(row.bing_search.map((value) => value.clicks), [11, 3]);
assert.strictEqual(row.bing_ai[0].citations, 7, "human-readable compact Bing dates and total_citations are supported");
const gscTotals = normalized.snapshot_totals.google_search_console;
assert.strictEqual(gscTotals.length, 2, "GSC legacy and current totals are never coalesced");
assert.deepStrictEqual(gscTotals.map((value) => value.property_identity), ["current", "legacy"]);
assert.deepStrictEqual(gscTotals.map((value) => value.coverage_end), ["2026-07-10", "2026-07-09"]);
assert.deepStrictEqual(gscTotals.map((value) => value.impressions), [2500, 1500]);
assert.ok(normalized.warnings.some((warning) => warning.includes("malformed JSON")));
assert.ok(normalized.warnings.some((warning) => warning.includes("partial dashboard")));
writeManifest("2026-07-04", "attacker/other-repo", "Mars/Olympus");
writeJson("2026-07-04/views.json", {
views: [{ timestamp: "2026-06-30T00:00:00Z", count: 999, uniques: 999 }],
});
writeManifest("2026-07-05");
writeJson("2026-07-05/views.json", {
views: [{ timestamp: "2026-99-99T00:00:00Z", count: -4, uniques: 1 }],
});
writeJson("2026-07-05/google-search-console.json", {
dashboard_url: "https://search.google.com/search-console/performance?resource_id=https%3A%2F%2Fsickn33.github.io%2Fagentic-awesome-skills%2F",
totals: { clicks: 1 },
});
writeJson("2026-07-05/bing-webmaster-ai-performance.json", {
status: "success",
dashboard_url: "https://www.bing.com/webmasters/aiperformance?siteUrl=https%3A%2F%2Fsickn33.github.io%2Fagentic-awesome-skills%2F",
date_range_visible: "2026-99-99 to 2026-99-99",
total_citations: 5,
});
writeManifest("2026-07-06");
writeJson("2026-07-06/google-search-console.json", {
status: "success",
intended_property: "https://sickn33.github.io/agentic-awesome-skills/",
dashboard_url: "https://search.google.com/search-console/performance?resource_id=https%3A%2F%2Fsickn33.github.io%2Fantigravity-awesome-skills%2F",
totals: { clicks: 8, impressions: 80 },
});
writeJson("2026-07-06/bing-webmaster-search-performance.json", {
status: "success",
dashboard_url: "https://attacker.example/webmasters/searchperf?siteUrl=https%3A%2F%2Fsickn33.github.io%2Fagentic-awesome-skills%2F",
totals: { clicks: 8, impressions: 80 },
});
writeManifest("2026-07-07");
writeJson("2026-07-07/google-search-console.json", {
status: "success",
dashboard_url: "https://search.google.com/search-console/performance?resource_id=https%3A%2F%2Fsickn33.github.io%2Fagentic-awesome-skills%2F",
date_range_visible: "June 1, 2026 to July 10, 2026",
daily_values: [{ date: "2026-06-29", clicks: 5, impressions: 50 }],
totals: { clicks: 5, impressions: 50 },
});
writeManifest("2026-07-08");
writeJson("2026-07-08/google-search-console.json", {
status: "success",
intended_property: "https://sickn33.github.io/agentic-awesome-skills/",
date_range_visible: "June 1, 2026 to July 10, 2026",
daily_values: [{ date: "2026-06-29", clicks: 999, impressions: 999 }],
totals: { clicks: 999, impressions: 999 },
});
const adversarial = normalizeSnapshots(fixtureRoot);
const stableRow = adversarial.rows.find((value) => value.date === "2026-06-30");
assert.strictEqual(stableRow.github.views.count, 9, "mismatched repository snapshot cannot overwrite the series");
assert(!adversarial.rows.some((value) => value.date === "2026-99-99"), "invalid calendar dates are rejected");
assert(adversarial.warnings.some((warning) => warning.includes("repository or timezone mismatch")));
assert(adversarial.warnings.some((warning) => warning.includes("non-success capture")));
const invalidCoverageTotal = adversarial.snapshot_totals.bing_ai.find((value) => value.observed_from_snapshot === "2026-07-05");
assert.strictEqual(invalidCoverageTotal.coverage_end, null, "impossible free-text coverage dates are rejected");
const conflictedGsc = adversarial.snapshot_totals.google_search_console.find((value) => value.observed_from_snapshot === "2026-07-06");
assert.strictEqual(conflictedGsc.property_identity, "legacy", "observed dashboard property wins over a conflicting intention");
assert.strictEqual(conflictedGsc.property_provenance, "observed");
assert(adversarial.warnings.some((warning) => warning.includes("intended property disagrees")));
assert(!adversarial.snapshot_totals.bing_search.some((value) => value.observed_from_snapshot === "2026-07-06"), "attacker-host dashboard evidence is skipped");
const preferredDaily = adversarial.rows.find((value) => value.date === "2026-06-29").google_search_console
.find((value) => value.property_identity === "current");
assert.strictEqual(preferredDaily.clicks, 5, "observed daily evidence cannot be overwritten by newer intended-only data");
assert.strictEqual(preferredDaily.property_provenance, "observed");
const preferredTotal = adversarial.snapshot_totals.google_search_console
.find((value) => value.property_identity === "current" && value.coverage_end === "2026-07-10");
assert.strictEqual(preferredTotal.clicks, 5, "observed totals cannot be overwritten by newer intended-only data");
assert.strictEqual(preferredTotal.property_provenance, "observed");
const outputPath = path.join(fixtureRoot, "out", "daily-normalized.json");
const first = spawnSync(process.execPath, [scriptPath, "--input", fixtureRoot, "--output", outputPath], { encoding: "utf8" });
assert.strictEqual(first.status, 0, first.stderr);
const firstOutput = fs.readFileSync(outputPath, "utf8");
const second = spawnSync(process.execPath, [scriptPath, "--input", fixtureRoot, "--output", outputPath], { encoding: "utf8" });
assert.strictEqual(second.status, 0, second.stderr);
assert.strictEqual(fs.readFileSync(outputPath, "utf8"), firstOutput, "stable input produces stable output");
const missing = spawnSync(process.execPath, [scriptPath, "--input", path.join(fixtureRoot, "missing"), "--output", outputPath], { encoding: "utf8" });
assert.strictEqual(missing.status, 1, "missing input is an operational failure");
assert.match(missing.stderr, /input directory does not exist/);
const rejectedRoot = fs.mkdtempSync(path.join(os.tmpdir(), "traffic-normalizer-rejected-"));
writeJson(path.relative(fixtureRoot, path.join(rejectedRoot, "2026-07-01", "manifest.json")), { repo: "owner/repo" });
const allRejected = spawnSync(process.execPath, [scriptPath, "--input", rejectedRoot, "--output", path.join(rejectedRoot, "out.json")], { encoding: "utf8" });
assert.strictEqual(allRejected.status, 1, "all-rejected snapshots are an operational failure");
assert.match(allRejected.stderr, /no snapshots .* were accepted/);
fs.rmSync(rejectedRoot, { recursive: true, force: true });
console.log("traffic snapshot normalizer tests passed.");
} finally {
fs.rmSync(fixtureRoot, { recursive: true, force: true });
}